From nsekhar at ti.com Fri Apr 1 11:19:09 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 1 Apr 2011 22:49:09 +0530 Subject: Status of DM6446 support in In-Reply-To: <4D94967B.8020101@ridgerun.com> References: <4D9323F2.6030807@ru.mvista.com> <4D932B59.1060205@acn-group.ch> <20110330145612.GA17958@xyzzy.org.uk> <4D936206.7090408@criticallink.com> <4D94967B.8020101@ridgerun.com> Message-ID: Hi Todd, On Thu, Mar 31, 2011 at 20:28:03, Todd Fischer wrote: > Sekhar, > > I have been seeing some DM6446 related activity on this list. We need I guess you refer to the V4L2 display patches being posted. > to update from 2.6.18 to something more recent for the DM6446. Do you > have a suggestion on what kernel source would be best to use as our > starting point (maybe a tag on linux-davinci repo)? This page summarizes the latest TI releases for each DaVinci (ARM9) platform: http://processors.wiki.ti.com/index.php/DaVinci_PSP_Releases > > Are you away of any issues we are likely to encounter using a newer > kernel with DM6446? Its mainly the video subsystem support that is missing. Latest status now available at: http://processors.wiki.ti.com/index.php/DaVinci_GIT_Linux_Kernel Thanks, Sekhar From manjunath.hadli at ti.com Sat Apr 2 03:40:38 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:10:38 +0530 Subject: [PATCH v16 00/13] davinci vpbe: dm6446 v4l2 driver Message-ID: <1301737238-3961-1-git-send-email-manjunath.hadli@ti.com> The more important among the patch history from previous comments 1. Removal of platform resource overlap. 2. Removal of unused macros. 3. Fixing the module params typo. 4. Minor changes in the GPL licensing header. 5. Removed the initializer for field inversion parameter. 6. Changing the Field inversion #ifdef to platform based implementation. 7. Interchanged platform and board specific patches due to dependencies. 8. Fixed sparse warnings. Manjunath Hadli (13): davinci vpbe: V4L2 display driver for DM644X SoC davinci vpbe: VPBE display driver davinci vpbe: OSD(On Screen Display) block davinci vpbe: VENC( Video Encoder) implementation davinci vpbe: Build infrastructure for VPBE driver davinci vpbe: Readme text for Dm6446 vpbe davinci: move DM64XX_VDD3P3V_PWDN to devices.c davinci: eliminate use of IO_ADDRESS() on sysmod davinci: dm644x: Replace register base value with a defined macro davinci: dm644x: change vpfe capture structure variables for consistency davinci: dm644x: move vpfe init from soc to board specific files davinci: dm644x: add support for v4l2 video display davinci: dm644x EVM: add support for VPBE display Documentation/video4linux/README.davinci-vpbe | 93 ++ arch/arm/mach-davinci/board-dm644x-evm.c | 131 ++- arch/arm/mach-davinci/devices.c | 25 +- arch/arm/mach-davinci/dm355.c | 1 + arch/arm/mach-davinci/dm365.c | 1 + arch/arm/mach-davinci/dm644x.c | 177 ++- arch/arm/mach-davinci/dm646x.c | 1 + arch/arm/mach-davinci/include/mach/dm644x.h | 6 +- arch/arm/mach-davinci/include/mach/hardware.h | 7 +- drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 827 ++++++++++ drivers/media/video/davinci/vpbe_display.c | 2085 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1216 ++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ drivers/media/video/davinci/vpbe_venc.c | 556 +++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ include/media/davinci/vpbe.h | 182 +++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_osd.h | 397 +++++ include/media/davinci/vpbe_types.h | 91 ++ include/media/davinci/vpbe_venc.h | 44 + 22 files changed, 6507 insertions(+), 44 deletions(-) create mode 100644 Documentation/video4linux/README.davinci-vpbe create mode 100644 drivers/media/video/davinci/vpbe.c create mode 100644 drivers/media/video/davinci/vpbe_display.c create mode 100644 drivers/media/video/davinci/vpbe_osd.c create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h create mode 100644 drivers/media/video/davinci/vpbe_venc.c create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h create mode 100644 include/media/davinci/vpbe.h create mode 100644 include/media/davinci/vpbe_display.h create mode 100644 include/media/davinci/vpbe_osd.h create mode 100644 include/media/davinci/vpbe_types.h create mode 100644 include/media/davinci/vpbe_venc.h From manjunath.hadli at ti.com Sat Apr 2 03:40:49 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:10:49 +0530 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1301737249-4012-1-git-send-email-manjunath.hadli@ti.com> This is the display driver for Texas Instruments's DM644X family SoC. This patch contains the main implementation of the driver with the V4L2 interface. The driver implements the streaming model with support for both kernel allocated buffers and user pointers. It also implements all of the necessary IOCTLs necessary and supported by the video display device. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_display.c | 2085 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 91 ++ 3 files changed, 2322 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_display.c create mode 100644 include/media/davinci/vpbe_display.h create mode 100644 include/media/davinci/vpbe_types.h diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c new file mode 100644 index 0000000..dde5f8a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2085 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vpbe_venc_regs.h" + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +static int venc_is_second_field() +{ + int ret = 0; + int val; + ret = v4l2_subdev_call(vpbe_dev->venc, + core, + ioctl, + VENC_GET_FLD, + &val); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in getting Field ID 0\n"); + } + return val; +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + layer = disp_dev->dev[i]; + /* If streaming is started in this layer */ + if (!layer->started) + continue; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + continue; + } + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + layer->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible( + &layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as active */ + layer->next_frm->state = VIDEOBUF_ACTIVE; + + addr = videobuf_to_dma_contig(layer->next_frm); + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, disp_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + layer->next_frm = list_entry( + layer->dma_queue.next, + struct videobuf_buffer, + queue); + + /* Remove that from the buffer queue */ + list_del(&layer->next_frm->queue); + + /* Mark state of the frame to active */ + layer->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(layer->next_frm); + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, + disp_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + event |= VENC_SECOND_FIELD; + else + event |= VENC_FIRST_FIELD; + + if (event == (last_event & ~VENC_END_OF_FRAME)) { + /* + * If the display is non-interlaced, then we need to flag the + * end-of-frame event at every interrupt regardless of the + * value of the FIDST bit. We can conclude that the display is + * non-interlaced if the value of the FIDST bit is unchanged + * from the previous interrupt. + */ + event |= VENC_END_OF_FRAME; + } else if (event == VENC_SECOND_FIELD) { + /* end-of-frame for interlaced display */ + event |= VENC_END_OF_FRAME; + } + last_event = event; + + vpbe_display_isr(event, arg); + return IRQ_HANDLED; +} + +/* + * vpbe_buffer_prepare() + * This is the callback function called from videobuf_qbuf() function + * the buffer is prepared and user space virtual address is converted into + * physical address + */ +static int vpbe_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = layer->pix_fmt.width; + vb->height = layer->pix_fmt.height; + vb->size = layer->pix_fmt.sizeimage; + vb->field = field; + + ret = videobuf_iolock(q, vb, NULL); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \ + user address\n"); + return -EINVAL; + } + + addr = videobuf_to_dma_contig(vb); + + if (q->streaming) { + if (!IS_ALIGNED(addr, 8)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "buffer_prepare:offset is \ + not aligned to 32 bytes\n"); + return -EINVAL; + } + } + vb->state = VIDEOBUF_PREPARED; + } + return 0; +} + +/* + * vpbe_buffer_setup() + * This function allocates memory for the buffers + */ +static int vpbe_buffer_setup(struct videobuf_queue *q, + unsigned int *count, + unsigned int *size) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + return 0; +} + +/* + * vpbe_buffer_queue() + * This function adds the buffer to DMA queue + */ +static void vpbe_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&disp->dma_queue_lock, flags); + list_add_tail(&vb->queue, &layer->dma_queue); + spin_unlock_irqrestore(&disp->dma_queue_lock, flags); + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +/* + * vpbe_buffer_release() + * This function is called from the videobuf layer to free memory allocated to + * the buffers + */ +static void vpbe_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_release\n"); + + if (V4L2_MEMORY_USERPTR != layer->memory) + videobuf_dma_contig_free(q, vb); + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpbe_buffer_setup, + .buf_prepare = vpbe_buffer_prepare, + .buf_queue = vpbe_buffer_queue, + .buf_release = vpbe_buffer_release, +}; + +static +struct vpbe_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + enum vpbe_display_device_id thiswin, otherwin; + thiswin = layer->device_id; + + otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ? + VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0; + return disp_dev->dev[otherwin]; +} + +static int vpbe_set_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + addr = videobuf_to_dma_contig(layer->cur_frm); + /* Set address in the display registers */ + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, + disp_dev->cbcr_ofst); + + ret = osd_device->ops.enable_layer(osd_device, + layer->layer_info.id, 0); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in enabling osd window layer 0\n"); + return -1; + } + + /* Enable the window */ + layer->layer_info.enable = 1; + if (cfg->pixfmt == PIXFMT_NV12) { + struct vpbe_display_obj *otherlayer = + _vpbe_display_get_other_win(disp_dev, layer); + + ret = osd_device->ops.enable_layer(osd_device, + otherlayer->layer_info.id, 1); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in enabling osd window layer 1\n"); + return -1; + } + otherlayer->layer_info.enable = 1; + } + return 0; +} + +static void +vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer, + int expected_xsize, int expected_ysize) +{ + struct display_layer_info *layer_info = &layer->layer_info; + struct v4l2_pix_format *pixfmt = &layer->pix_fmt; + struct osd_layer_config *cfg = &layer->layer_info.config; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id; + + /* + * Application initially set the image format. Current display + * size is obtained from the vpbe display controller. expected_xsize + * and expected_ysize are set through S_CROP ioctl. Based on this, + * driver will calculate the scale factors for vertical and + * horizontal direction so that the image is displayed scaled + * and expanded. Application uses expansion to display the image + * in a square pixel. Otherwise it is displayed using displays + * pixel aspect ratio.It is expected that application chooses + * the crop coordinates for cropped or scaled display. if crop + * size is less than the image size, it is displayed cropped or + * it is displayed scaled and/or expanded. + * + * to begin with, set the crop window same as expected. Later we + * will override with scaled window size + */ + + cfg->xsize = pixfmt->width; + cfg->ysize = pixfmt->height; + layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */ + layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */ + layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */ + layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */ + + if (pixfmt->width < expected_xsize) { + h_scale = vpbe_dev->current_timings.xres / pixfmt->width; + if (h_scale < 2) + h_scale = 1; + else if (h_scale >= 4) + h_scale = 4; + else + h_scale = 2; + cfg->xsize *= h_scale; + if (cfg->xsize < expected_xsize) { + if ((standard_id & V4L2_STD_525_60) || + (standard_id & V4L2_STD_625_50)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + if (h_scale == 2) + layer_info->h_zoom = ZOOM_X2; + else if (h_scale == 4) + layer_info->h_zoom = ZOOM_X4; + if (h_exp) + layer_info->h_exp = H_EXP_9_OVER_8; + } else { + /* no scaling, only cropping. Set display area to crop area */ + cfg->xsize = expected_xsize; + } + + if (pixfmt->height < expected_ysize) { + v_scale = expected_ysize / pixfmt->height; + if (v_scale < 2) + v_scale = 1; + else if (v_scale >= 4) + v_scale = 4; + else + v_scale = 2; + cfg->ysize *= v_scale; + if (cfg->ysize < expected_ysize) { + if ((standard_id & V4L2_STD_625_50)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + if (v_scale == 2) + layer_info->v_zoom = ZOOM_X2; + else if (v_scale == 4) + layer_info->v_zoom = ZOOM_X4; + if (v_exp) + layer_info->h_exp = V_EXP_6_OVER_5; + } else { + /* no scaling, only cropping. Set display area to crop area */ + cfg->ysize = expected_ysize; + } + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "crop display xsize = %d, ysize = %d\n", + cfg->xsize, cfg->ysize); +} + +static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) || + ((c->width + c->left) > vpbe_dev->current_timings.xres) || + (c->height == 0) || ((c->height + c->top) > + vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * vpbe_try_format() + * If user application provides width and height, and have bytesperline set + * to zero, driver calculates bytesperline and sizeimage based on hardware + * limits. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) && + (pixfmt->pixelformat != V4L2_PIX_FMT_NV12)) + /* choose default as V4L2_PIX_FMT_UYVY */ + pixfmt->pixelformat = V4L2_PIX_FMT_UYVY; + + /* Check the field format */ + if (pixfmt->field == V4L2_FIELD_ANY) { + if (vpbe_dev->current_timings.interlaced) + pixfmt->field = V4L2_FIELD_INTERLACED; + else + pixfmt->field = V4L2_FIELD_NONE; + } + + if (pixfmt->field == V4L2_FIELD_INTERLACED) + min_height = 2; + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + bpp = 1; + else + bpp = 2; + + max_width = vpbe_dev->current_timings.xres; + max_height = vpbe_dev->current_timings.yres; + + min_width /= bpp; + + if (!pixfmt->width && !pixfmt->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + return 0; +} + +static int vpbe_display_g_priority(struct file *file, void *priv, + enum v4l2_priority *p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + *p = v4l2_prio_max(&layer->prio); + + return 0; +} + +static int vpbe_display_s_priority(struct file *file, void *priv, + enum v4l2_priority p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + int ret; + + ret = v4l2_prio_change(&layer->prio, &fh->prio, p); + + return ret; +} + +static int vpbe_display_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + + vpbe_disp_calculate_scale_factor(disp_dev, layer, + rect->width, + rect->height); + vpbe_disp_adj_position(disp_dev, layer, rect->top, + rect->left); + ret = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, cfg); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in set layer config:\n"); + return -EINVAL; + } + + /* apply zooming and h or v expansion */ + osd_device->ops.set_zoom(osd_device, + layer->layer_info.id, + layer->layer_info.h_zoom, + layer->layer_info.v_zoom); + ret = osd_device->ops.set_vid_expansion(osd_device, + layer->layer_info.h_exp, + layer->layer_info.v_exp); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in set vid expansion:\n"); + return -EINVAL; + } + + if ((layer->layer_info.h_zoom != ZOOM_X1) || + (layer->layer_info.v_zoom != ZOOM_X1) || + (layer->layer_info.h_exp != H_EXP_OFF) || + (layer->layer_info.v_exp != V_EXP_OFF)) + /* Enable expansion filter */ + osd_device->ops.set_interpolation_filter(osd_device, 1); + else + osd_device->ops.set_interpolation_filter(osd_device, 0); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_G_CROP, layer id = %d\n", + layer->device_id); + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + rect->top = cfg->ypos; + rect->left = cfg->xpos; + rect->width = cfg->xsize; + rect->height = cfg->ysize; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n"); + + cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + cropcap->bounds.left = 0; + cropcap->bounds.top = 0; + cropcap->bounds.width = vpbe_dev->current_timings.xres; + cropcap->bounds.height = vpbe_dev->current_timings.yres; + cropcap->pixelaspect = vpbe_dev->current_timings.aspect; + cropcap->defrect = cropcap->bounds; + return 0; +} + +static int vpbe_display_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_G_FMT, layer id = %d\n", + layer->device_id); + + /* If buffer type is video output */ + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + return 0; +} + +static int vpbe_display_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + unsigned int index = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_ENUM_FMT, layer id = %d\n", + layer->device_id); + if (fmt->index > 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + index = fmt->index; + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (index == 0) { + strcpy(fmt->description, "YUV 4:2:2 - UYVY"); + fmt->pixelformat = V4L2_PIX_FMT_UYVY; + } else if (index == 1) { + strcpy(fmt->description, "Y/CbCr 4:2:0"); + fmt->pixelformat = V4L2_PIX_FMT_NV12; + } + return 0; +} + +static int vpbe_display_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_S_FMT, layer id = %d\n", + layer->device_id); + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } else { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid pixel format */ + ret = vpbe_try_format(disp_dev, pixfmt, 1); + if (ret) + return ret; + + /* YUV420 is requested, check availability of the + other video window */ + + layer->pix_fmt = *pixfmt; + + /* Get osd layer config */ + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + /* Store the pixel format in the layer object */ + cfg->xsize = pixfmt->width; + cfg->ysize = pixfmt->height; + cfg->line_length = pixfmt->bytesperline; + cfg->ypos = 0; + cfg->xpos = 0; + cfg->interlaced = vpbe_dev->current_timings.interlaced; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(disp_dev, + layer); + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; + } + + /* Set the layer config in the osd window */ + ret = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, cfg); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in S_FMT params:\n"); + return -EINVAL; + } + + /* Readback and fill the local copy of current pix format */ + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || (cfg->interlaced && + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field != + V4L2_FIELD_NONE)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "mismatch:layer conf params:\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; +} + +/** + * vpbe_display_s_std - Set the given standard in the encoder + * + * Sets the standard if supported by the current encoder. Return the status. + * 0 - success & -EINVAL on error + */ +static int vpbe_display_s_std(struct file *file, void *priv, + v4l2_std_id *std_id) +{ + struct vpbe_fh *fh = priv; + struct vpbe_display_obj *layer = fh->layer; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (NULL != vpbe_dev->ops.s_std) { + ret = vpbe_dev->ops.s_std(vpbe_dev, std_id); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set standard for sub devices\n"); + return -EINVAL; + } + } + return 0; +} + +/** + * vpbe_display_g_std - Get the standard in the current encoder + * + * Get the standard in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_display_g_std(struct file *file, void *priv, + v4l2_std_id *std_id) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); + + /* Get the standard from the current encoder */ + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + *std_id = vpbe_dev->current_timings.timings.std_id; + return 0; + } + return -EINVAL; +} + +/** + * vpbe_display_enum_output - enumerate outputs + * + * Enumerates the outputs available at the vpbe display + * returns the status, -EINVAL if end of output list + */ +static int vpbe_display_enum_output(struct file *file, void *priv, + struct v4l2_output *output) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); + if (ret) { + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "Failed to enumerate outputs\n"); + return -EINVAL; + } + } + return 0; +} + +/** + * vpbe_display_s_output - Set output to + * the output specified by the index + */ +static int vpbe_display_s_output(struct file *file, void *priv, + unsigned int i) +{ + struct vpbe_fh *fh = priv; + struct vpbe_display_obj *layer = fh->layer; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n"); + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (NULL != vpbe_dev->ops.set_output) { + ret = vpbe_dev->ops.set_output(vpbe_dev, i); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set output for sub devices\n"); + return -EINVAL; + } + } + return ret; +} + +/** + * vpbe_display_g_output - Get output from subdevice + * for a given by the index + */ +static int vpbe_display_g_output(struct file *file, void *priv, + unsigned int *i) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n"); + /* Get the standard from the current encoder */ + *i = vpbe_dev->current_out_index; + + return 0; +} + +/** + * vpbe_display_enum_dv_presets - Enumerate the dv presets + * + * enum the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_enum_dv_presets(struct file *file, void *priv, + struct v4l2_dv_enum_preset *preset) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to enumerate dv presets info\n"); + return -EINVAL; + } + } + + return ret; +} + +/** + * vpbe_display_s_dv_preset - Set the dv presets + * + * Set the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_s_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpbe_fh *fh = priv; + struct vpbe_display_obj *layer = fh->layer; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n"); + + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Set the given standard in the encoder */ + if (NULL != vpbe_dev->ops.s_dv_preset) { + ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set the dv presets info\n"); + return -EINVAL; + } + } + /* set the current norm to zero to be consistent. If STD is used + * v4l2 layer will set the norm properly on successful s_std call + */ + layer->video_dev->current_norm = 0; + return ret; +} + +/** + * vpbe_display_g_dv_preset - Set the dv presets + * + * Get the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_g_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *dv_preset) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n"); + + /* Get the given standard in the encoder */ + + if (vpbe_dev->current_timings.timings_type & + VPBE_ENC_DV_PRESET) { + dv_preset->preset = + vpbe_dev->current_timings.timings.dv_preset; + } else { + return -EINVAL; + } + return 0; +} + +static int vpbe_display_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_STREAMOFF,layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer" + " id = %d\n", layer->device_id); + return -EINVAL; + } + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + layer->started = 0; + ret = videobuf_streamoff(&layer->buffer_queue); + + return ret; +} + +static int vpbe_display_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + /* If Streaming is already started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n"); + return -EBUSY; + } + + /* + * Call videobuf_streamon to start streaming + * in videobuf + */ + ret = videobuf_streamon(&layer->buffer_queue); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "error in videobuf_streamon\n"); + return ret; + } + /* If buffer queue is empty, return error */ + if (list_empty(&layer->dma_queue)) { + v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); + goto streamoff; + } + /* Get the next frame from the buffer queue */ + layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove buffer from the buffer queue */ + list_del(&layer->cur_frm->queue); + /* Mark state of the current frame to active */ + layer->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + layer->field_id = 0; + + /* Set parameters in OSD and VENC */ + ret = vpbe_set_video_display_params(disp_dev, layer); + if (ret < 0) + goto streamoff; + + /* + * if request format is yuv420 semiplanar, need to + * enable both video windows + */ + layer->started = 1; + + layer_first_int[layer->device_id] = 1; + + return ret; +streamoff: + ret = videobuf_streamoff(&layer->buffer_queue); + return ret; +} + +static int vpbe_display_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_DQBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + /* If this file handle is not allowed to do IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + if (file->f_flags & O_NONBLOCK) + /* Call videobuf_dqbuf for non blocking mode */ + ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1); + else + /* Call videobuf_dqbuf for blocking mode */ + ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0); + return ret; +} + +static int vpbe_display_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If this file handle is not allowed to do IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + + return videobuf_qbuf(&layer->buffer_queue, p); +} + +static int vpbe_display_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* Call videobuf_querybuf to get information */ + ret = videobuf_querybuf(&layer->buffer_queue, buf); + + return ret; +} + +static int vpbe_display_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If io users of the layer is not zero, return error */ + if (0 != layer->io_usrs) { + v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n"); + return -EBUSY; + } + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&layer->buffer_queue, + &video_qops, + vpbe_dev->pdev, + &layer->irqlock, + V4L2_BUF_TYPE_VIDEO_OUTPUT, + layer->pix_fmt.field, + sizeof(struct videobuf_buffer), + fh, NULL); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed = 1; + /* Increment io usrs member of layer object to 1 */ + layer->io_usrs = 1; + /* Store type of memory requested in layer object */ + layer->memory = req_buf->memory; + /* Initialize buffer queue */ + INIT_LIST_HEAD(&layer->dma_queue); + /* Allocate buffers */ + ret = videobuf_reqbufs(&layer->buffer_queue, req_buf); + + return ret; +} + +/* + * vpbe_display_mmap() + * It is used to map kernel space buffers into user spaces + */ +static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma) +{ + /* Get the layer object and file handle object */ + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n"); + return videobuf_mmap_mapper(&layer->buffer_queue, vma); +} + +/* vpbe_display_poll(): It is used for select/poll system call + */ +static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n"); + if (layer->started) + err = videobuf_poll_stream(filep, &layer->buffer_queue, wait); + return err; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* First claim the layer for this device */ + err = osd_device->ops.request_layer(osd_device, + layer->layer_info.id); + if (err < 0) { + /* Couldn't get layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to allocate layer\n"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * vpbe_display_open() + * It creates object of file handle structure and stores it in private_data + * member of filepointer + */ +static int vpbe_display_open(struct file *file) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL); + if (fh == NULL) { + v4l2_err(&vpbe_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe display open plane = %d\n", + layer->device_id); + + /* store pointer to fh in private_data member of filep */ + file->private_data = fh; + fh->layer = layer; + fh->disp_dev = disp_dev; + + if (!layer->usrs) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + return -EINVAL; + } + } + /* Increment layer usrs counter */ + layer->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&layer->prio, &fh->prio); + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe display device opened successfully\n"); + return 0; +} + +/* + * vpbe_display_release() + * This function deletes buffer queue, frees the buffers and the davinci + * display file * handle + */ +static int vpbe_display_release(struct file *file) +{ + /* Get the layer object and file handle object */ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* if this instance is doing IO */ + if (fh->io_allowed) { + /* Reset io_usrs member of layer object */ + layer->io_usrs = 0; + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + layer->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&layer->buffer_queue); + videobuf_mmap_free(&layer->buffer_queue); + } + + /* Decrement layer usrs counter */ + layer->usrs--; + /* If this file handle has initialize encoder device, reset it */ + if (!layer->usrs) { + if (cfg->pixfmt == PIXFMT_NV12) { + struct vpbe_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(disp_dev, layer); + osd_device->ops.disable_layer(osd_device, + otherlayer->layer_info.id); + osd_device->ops.release_layer(osd_device, + otherlayer->layer_info.id); + } + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + osd_device->ops.release_layer(osd_device, + layer->layer_info.id); + } + /* Close the priority */ + v4l2_prio_close(&layer->prio, fh->prio); + file->private_data = NULL; + + /* Free memory allocated to file handle object */ + kfree(fh); + + disp_dev->cbcr_ofst = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vpbe_display_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct v4l2_dbg_match *match = ®->match; + + if (match->type >= 2) { + v4l2_subdev_call(vpbe_dev->venc, + core, + g_register, + reg); + } + + return 0; +} + +static int vpbe_display_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + return 0; +} +#endif + +/* vpbe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { + .vidioc_querycap = vpbe_display_querycap, + .vidioc_g_fmt_vid_out = vpbe_display_g_fmt, + .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt, + .vidioc_s_fmt_vid_out = vpbe_display_s_fmt, + .vidioc_try_fmt_vid_out = vpbe_display_try_fmt, + .vidioc_reqbufs = vpbe_display_reqbufs, + .vidioc_querybuf = vpbe_display_querybuf, + .vidioc_qbuf = vpbe_display_qbuf, + .vidioc_dqbuf = vpbe_display_dqbuf, + .vidioc_streamon = vpbe_display_streamon, + .vidioc_streamoff = vpbe_display_streamoff, + .vidioc_cropcap = vpbe_display_cropcap, + .vidioc_g_crop = vpbe_display_g_crop, + .vidioc_s_crop = vpbe_display_s_crop, + .vidioc_g_priority = vpbe_display_g_priority, + .vidioc_s_priority = vpbe_display_s_priority, + .vidioc_s_std = vpbe_display_s_std, + .vidioc_g_std = vpbe_display_g_std, + .vidioc_enum_output = vpbe_display_enum_output, + .vidioc_s_output = vpbe_display_s_output, + .vidioc_g_output = vpbe_display_g_output, + .vidioc_s_dv_preset = vpbe_display_s_dv_preset, + .vidioc_g_dv_preset = vpbe_display_g_dv_preset, + .vidioc_enum_dv_presets = vpbe_display_enum_dv_presets, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vpbe_display_g_register, + .vidioc_s_register = vpbe_display_s_register, +#endif +}; + +static struct v4l2_file_operations vpbe_fops = { + .owner = THIS_MODULE, + .open = vpbe_display_open, + .release = vpbe_display_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpbe_display_mmap, + .poll = vpbe_display_poll +}; + +static int vpbe_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + return 0; +} + + +/* + * vpbe_display_probe() + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each layer objects + */ +static __devinit int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + int irq; + + printk(KERN_DEBUG "vpbe_display_probe\n"); + + /* Allocate memory for vpbe_display */ + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); + if (!disp_dev) { + printk(KERN_ERR "ran out of memory\n"); + return -ENOMEM; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * Scan all the platform devices to find the vpbe + * controller device and get the vpbe_dev object + */ + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + vpbe_dev->current_timings.timings.std_id; + } else + vbd->current_norm = 0; + + snprintf(vbd->name, sizeof(vbd->name), + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, + (VPBE_DISPLAY_VERSION_CODE) & 0xff); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&vpbe_dev->v4l2_dev, + "layer=%x,layer->video_dev=%x\n", + (int)vpbe_display_layer, + (int)&vpbe_display_layer->video_dev); + + err = video_register_device(vpbe_display_layer-> + video_dev, + VFL_TYPE_GRABBER, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC interrupt resource\n"); + err = -ENODEV; + goto probe_out; + } + irq = res->start; + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n"); + + /* unregister irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + free_irq(res->start, disp_dev); + + /* deinitialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.deinitialize) + vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev); + /* un-register device */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + + vpbe_display_layer->video_dev = NULL; + } + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + kfree(disp_dev->dev[i]); + disp_dev->dev[i] = NULL; + } + + return 0; +} + +static struct platform_driver vpbe_display_driver = { + .driver = { + .name = VPBE_DISPLAY_DRIVER, + .owner = THIS_MODULE, + .bus = &platform_bus_type, + }, + .probe = vpbe_display_probe, + .remove = __devexit_p(vpbe_display_remove), +}; + +/* + * vpbe_display_init() + * This function registers device and driver to the kernel, requests irq + * handler and allocates memory for layer objects + */ +static __devinit int vpbe_display_init(void) +{ + int err = 0; + + printk(KERN_DEBUG "vpbe_display_init\n"); + + /* Register driver to the kernel */ + err = platform_driver_register(&vpbe_display_driver); + if (0 != err) + return err; + + printk(KERN_DEBUG "vpbe_display_init:" + "VPBE V4L2 Display Driver V1.0 loaded\n"); + return 0; +} + +/* + * vpbe_display_cleanup() + * This function un-registers device and driver to the kernel, frees requested + * irq handler and de-allocates memory allocated for layer objects. + */ +static void vpbe_display_cleanup(void) +{ + printk(KERN_DEBUG "vpbe_display_cleanup\n"); + + /* platform driver unregister */ + platform_driver_unregister(&vpbe_display_driver); +} + +/* Function for module initialization and cleanup */ +module_init(vpbe_display_init); +module_exit(vpbe_display_cleanup); + +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h new file mode 100644 index 0000000..d5cce40 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +#ifdef __KERNEL__ + +/* Header files */ +#include +#include +#include +#include +#include +#include + +#define VPBE_DISPLAY_MAX_DEVICES 2 + +enum vpbe_display_device_id { + VPBE_DISPLAY_DEVICE_0, + VPBE_DISPLAY_DEVICE_1 +}; + +#define VPBE_DISPLAY_DRV_NAME "vpbe-display" + +#define VPBE_DISPLAY_MAJOR_RELEASE 1 +#define VPBE_DISPLAY_MINOR_RELEASE 0 +#define VPBE_DISPLAY_BUILD 1 +#define VPBE_DISPLAY_VERSION_CODE ((VPBE_DISPLAY_MAJOR_RELEASE << 16) | \ + (VPBE_DISPLAY_MINOR_RELEASE << 8) | \ + VPBE_DISPLAY_BUILD) + +#define VPBE_DISPLAY_VALID_FIELD(field) ((V4L2_FIELD_NONE == field) || \ + (V4L2_FIELD_ANY == field) || (V4L2_FIELD_INTERLACED == field)) + +/* Exp ratio numerator and denominator constants */ +#define VPBE_DISPLAY_H_EXP_RATIO_N (9) +#define VPBE_DISPLAY_H_EXP_RATIO_D (8) +#define VPBE_DISPLAY_V_EXP_RATIO_N (6) +#define VPBE_DISPLAY_V_EXP_RATIO_D (5) + +/* Zoom multiplication factor */ +#define VPBE_DISPLAY_ZOOM_4X (4) +#define VPBE_DISPLAY_ZOOM_2X (2) + +/* Structures */ +struct display_layer_info { + int enable; + /* Layer ID used by Display Manager */ + enum osd_layer id; + struct osd_layer_config config; + enum osd_zoom_factor h_zoom; + enum osd_zoom_factor v_zoom; + enum osd_h_exp_ratio h_exp; + enum osd_v_exp_ratio v_exp; +}; + +/* vpbe display object structure */ +struct vpbe_display_obj { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* videobuf specific parameters + * Buffer queue used in video-buf + */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* V4l2 specific parameters */ + /* Identifies video device for this layer */ + struct video_device *video_dev; + /* This field keeps track of type of buffer exchange mechanism user + * has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* Used to store pixel format */ + struct v4l2_pix_format pix_fmt; + enum v4l2_field buf_field; + /* Video layer configuration params */ + struct display_layer_info layer_info; + /* vpbe specific parameters + * enable window for display + */ + unsigned char window_enable; + /* number of open instances of the layer */ + unsigned int usrs; + /* number of users performing IO */ + unsigned int io_usrs; + /* Indicates id of the field which is being displayed */ + unsigned int field_id; + /* Indicates whether streaming started */ + unsigned char started; + /* Identifies device object */ + enum vpbe_display_device_id device_id; + /* facilitation of ioctl ops lock by v4l2*/ + struct mutex opslock; +}; + +/* vpbe device structure */ +struct vpbe_display { + /* layer specific parameters */ + /* lock for isr updates to buf layers*/ + spinlock_t dma_queue_lock; + /* C-Plane offset from start of y-plane */ + unsigned int cbcr_ofst; + struct vpbe_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_display_obj *layer; + /* Indicates whether this file handle is doing IO */ + unsigned char io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +struct buf_config_params { + unsigned char min_numbuffers; + unsigned char numbuffers[VPBE_DISPLAY_MAX_DEVICES]; + unsigned int min_bufsize[VPBE_DISPLAY_MAX_DEVICES]; + unsigned int layer_bufsize[VPBE_DISPLAY_MAX_DEVICES]; +}; + +static int venc_is_second_field(void); +#endif /* end of __KERNEL__ */ +#endif /* VPBE_DISPLAY_H */ diff --git a/include/media/davinci/vpbe_types.h b/include/media/davinci/vpbe_types.h new file mode 100644 index 0000000..24a358b --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_TYPES_H +#define _VPBE_TYPES_H + +enum vpbe_types { + VPBE_VERSION_1 = 1, + VPBE_VERSION_2, + VPBE_VERSION_3, +}; + +/* vpbe_timing_type - Timing types used in vpbe device */ +enum vpbe_enc_timings_type { + VPBE_ENC_STD = 0x1, + VPBE_ENC_DV_PRESET = 0x2, + VPBE_ENC_CUSTOM_TIMINGS = 0x4, + /* Used when set timings through FB device interface */ + VPBE_ENC_TIMINGS_INVALID = 0x8, +}; + +union vpbe_timings { + v4l2_std_id std_id; + unsigned int dv_preset; +}; + +/* + * struct vpbe_enc_mode_info + * @name: ptr to name string of the standard, "NTSC", "PAL" etc + * @std: standard or non-standard mode. 1 - standard, 0 - nonstandard + * @interlaced: 1 - interlaced, 0 - non interlaced/progressive + * @xres: x or horizontal resolution of the display + * @yres: y or vertical resolution of the display + * @fps: frame per second + * @left_margin: left margin of the display + * @right_margin: right margin of the display + * @upper_margin: upper margin of the display + * @lower_margin: lower margin of the display + * @hsync_len: h-sync length + * @vsync_len: v-sync length + * @flags: bit field: bit usage is documented below + * + * Description: + * Structure holding timing and resolution information of a standard. + * Used by vpbe_device to set required non-standard timing in the + * venc when lcd controller output is connected to a external encoder. + * A table of timings is maintained in vpbe device to set this in + * venc when external encoder is connected to lcd controller output. + * Encoder may provide a g_dv_timings() API to override these values + * as needed. + * + * Notes + * ------ + * if_type should be used only by encoder manager and encoder. + * flags usage + * b0 (LSB) - hsync polarity, 0 - negative, 1 - positive + * b1 - vsync polarity, 0 - negative, 1 - positive + * b2 - field id polarity, 0 - negative, 1 - positive + */ +struct vpbe_enc_mode_info { + unsigned char *name; + enum vpbe_enc_timings_type timings_type; + union vpbe_timings timings; + unsigned int interlaced; + unsigned int xres; + unsigned int yres; + struct v4l2_fract aspect; + struct v4l2_fract fps; + unsigned int left_margin; + unsigned int right_margin; + unsigned int upper_margin; + unsigned int lower_margin; + unsigned int hsync_len; + unsigned int vsync_len; + unsigned int flags; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:41:11 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:11:11 +0530 Subject: [PATCH v16 02/13] davinci vpbe: VPBE display driver Message-ID: <1301737271-4054-1-git-send-email-manjunath.hadli@ti.com> This patch implements the core functionality of the display driver, mainly controlling the VENC and other encoders, and acting as the one point interface for the main V4L2 driver. This implements the core of each of the V4L2 IOCTLs. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe.c | 827 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 182 ++++++++ 2 files changed, 1009 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe.c create mode 100644 include/media/davinci/vpbe.h diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/video/davinci/vpbe.c new file mode 100644 index 0000000..2cdba70 --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,827 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define VPBE_DEFAULT_OUTPUT "Composite" +#define VPBE_DEFAULT_MODE "ntsc" + +static char *def_output = VPBE_DEFAULT_OUTPUT; +static char *def_mode = VPBE_DEFAULT_MODE; +static struct osd_state *osd_device; +static struct venc_platform_data *venc_device; +static int debug; + +module_param(def_output, charp, S_IRUGO); +module_param(def_mode, charp, S_IRUGO); +module_param(debug, int, 0644); + +MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)"); +MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/** + * vpbe_current_encoder_info - Get config info for current encoder + * @vpbe_dev - vpbe device ptr + * + * Return ptr to current encoder config info + */ +static struct encoder_config_info* +vpbe_current_encoder_info(struct vpbe_device *vpbe_dev) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int index = vpbe_dev->current_sd_index; + + return ((index == 0) ? &cfg->venc : + &cfg->ext_encoders[index-1]); +} + +/** + * vpbe_find_encoder_sd_index - Given a name find encoder sd index + * + * @vpbe_config - ptr to vpbe cfg + * @output_index - index used by application + * + * Return sd index of the encoder + */ +static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg, + int index) +{ + char *encoder_name = cfg->outputs[index].subdev_name; + int i; + + /* Venc is always first */ + if (!strcmp(encoder_name, cfg->venc.module_name)) + return 0; + + for (i = 0; i < cfg->num_ext_encoders; i++) { + if (!strcmp(encoder_name, + cfg->ext_encoders[i].module_name)) + return i+1; + } + return -EINVAL; +} + +/** + * vpbe_g_cropcap - Get crop capabilities of the display + * @vpbe_dev - vpbe device ptr + * @cropcap - cropcap is a ptr to struct v4l2_cropcap + * + * Update the crop capabilities in crop cap for current + * mode + */ +static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev, + struct v4l2_cropcap *cropcap) +{ + if (NULL == cropcap) + return -EINVAL; + cropcap->bounds.left = 0; + cropcap->bounds.top = 0; + cropcap->bounds.width = vpbe_dev->current_timings.xres; + cropcap->bounds.height = vpbe_dev->current_timings.yres; + cropcap->defrect = cropcap->bounds; + return 0; +} + +/** + * vpbe_enum_outputs - enumerate outputs + * @vpbe_dev - vpbe device ptr + * @output - ptr to v4l2_output structure + * + * Enumerates the outputs available at the vpbe display + * returns the status, -EINVAL if end of output list + */ +static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev, + struct v4l2_output *output) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int temp_index = output->index; + + if (temp_index >= cfg->num_outputs) + return -EINVAL; + + *output = cfg->outputs[temp_index].output; + output->index = temp_index; + return 0; +} + +static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, i; + + if (NULL == mode) + return -EINVAL; + + for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if (!strcmp(mode, var.name)) { + vpbe_dev->current_timings = var; + return 0; + } + } + return -EINVAL; +} + +static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info *mode_info) +{ + if (NULL == mode_info) + return -EINVAL; + + *mode_info = vpbe_dev->current_timings; + return 0; +} + +static int vpbe_get_dv_preset_info(struct vpbe_device *vpbe_dev, + unsigned int dv_preset) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if ((var.timings_type & VPBE_ENC_DV_PRESET) && + (var.timings.dv_preset == dv_preset)) { + vpbe_dev->current_timings = var; + return 0; + } + } + return -EINVAL; +} + +/* Get std by std id */ +static int vpbe_get_std_info(struct vpbe_device *vpbe_dev, + v4l2_std_id std_id) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if ((var.timings_type & VPBE_ENC_STD) && + (var.timings.std_id & std_id)) { + vpbe_dev->current_timings = var; + return 0; + } + } + return -EINVAL; +} + +static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev, + char *std_name) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if (!strcmp(var.name, std_name)) { + vpbe_dev->current_timings = var; + return 0; + } + } + return -EINVAL; +} + +/** + * vpbe_set_output - Set output + * @vpbe_dev - vpbe device ptr + * @index - index of output + * + * Set vpbe output to the output specified by the index + */ +static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + int ret = 0, enc_out_index = 0, sd_index; + + if (index >= cfg->num_outputs) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + sd_index = vpbe_dev->current_sd_index; + enc_out_index = cfg->outputs[index].output.index; + /* + * Currently we switch the encoder based on output selected + * by the application. If media controller is implemented later + * there is will be an API added to setup_link between venc + * and external encoder. So in that case below comparison always + * match and encoder will not be switched. But if application + * chose not to use media controller, then this provides current + * way of switching encoder at the venc output. + */ + if (strcmp(curr_enc_info->module_name, + cfg->outputs[index].subdev_name)) { + /* Need to switch the encoder at the output */ + sd_index = vpbe_find_encoder_sd_index(cfg, index); + if (sd_index < 0) { + ret = -EINVAL; + goto out; + } + + if (ret) + goto out; + } + + /* Set output at the encoder */ + ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, + s_routing, 0, enc_out_index, 0); + if (ret) + goto out; + + /* + * It is assumed that venc or extenal encoder will set a default + * mode in the sub device. For external encoder or LCD pannel output, + * we also need to set up the lcd port for the required mode. So setup + * the lcd port for the default mode that is configured in the board + * arch/arm/mach-davinci/board-dm355-evm.setup file for the external + * encoder. + */ + ret = vpbe_get_mode_info(vpbe_dev, + cfg->outputs[index].default_mode); + if (!ret) { + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + vpbe_dev->current_sd_index = sd_index; + vpbe_dev->current_out_index = index; + } +out: + mutex_unlock(&vpbe_dev->lock); + return ret; +} + +static int vpbe_set_default_output(struct vpbe_device *vpbe_dev) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int i, ret = 0; + + for (i = 0; i < cfg->num_outputs; i++) { + if (!strcmp(def_output, + cfg->outputs[i].output.name)) { + ret = vpbe_set_output(vpbe_dev, i); + if (!ret) + vpbe_dev->current_out_index = i; + return ret; + } + } + return ret; +} + +/** + * vpbe_get_output - Get output + * @vpbe_dev - vpbe device ptr + * + * return current vpbe output to the the index + */ +static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev) +{ + return vpbe_dev->current_out_index; +} + +/** + * vpbe_s_dv_preset - Set the given preset timings in the encoder + * + * Sets the preset if supported by the current encoder. Return the status. + * 0 - success & -EINVAL on error + */ +static int vpbe_s_dv_preset(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index; + int out_index = vpbe_dev->current_out_index, ret; + + + if (!(cfg->outputs[out_index].output.capabilities & + V4L2_OUT_CAP_PRESETS)) + return -EINVAL; + + ret = vpbe_get_dv_preset_info(vpbe_dev, dv_preset->preset); + + if (ret) + return ret; + + mutex_lock(&vpbe_dev->lock); + + + ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, + s_dv_preset, dv_preset); + /* set the lcd controller output for the given mode */ + if (!ret) { + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + } + mutex_unlock(&vpbe_dev->lock); + return ret; +} + +/** + * vpbe_g_dv_preset - Get the preset in the current encoder + * + * Get the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_g_dv_preset(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset) +{ + if (vpbe_dev->current_timings.timings_type & + VPBE_ENC_DV_PRESET) { + dv_preset->preset = vpbe_dev->current_timings.timings.dv_preset; + return 0; + } + return -EINVAL; +} + +/** + * vpbe_enum_dv_presets - Enumerate the dv presets in the current encoder + * + * Get the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_enum_dv_presets(struct vpbe_device *vpbe_dev, + struct v4l2_dv_enum_preset *preset_info) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + struct vpbe_output *output = &cfg->outputs[out_index]; + int i, j = 0; + + if (!(output->output.capabilities & V4L2_OUT_CAP_PRESETS)) + return -EINVAL; + + for (i = 0; i < output->num_modes; i++) { + if (output->modes[i].timings_type == VPBE_ENC_DV_PRESET) { + if (j == preset_info->index) + break; + j++; + } + } + + if (i == output->num_modes) + return -EINVAL; + + return v4l_fill_dv_preset_info(output->modes[i].timings.dv_preset, + preset_info); +} + +/** + * vpbe_s_std - Set the given standard in the encoder + * + * Sets the standard if supported by the current encoder. Return the status. + * 0 - success & -EINVAL on error + */ +static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index, out_index = + vpbe_dev->current_out_index, ret; + + if (!(cfg->outputs[out_index].output.capabilities & + V4L2_OUT_CAP_STD)) + return -EINVAL; + + ret = vpbe_get_std_info(vpbe_dev, *std_id); + if (ret) + return ret; + + mutex_lock(&vpbe_dev->lock); + + ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, + s_std_output, *std_id); + /* set the lcd controller output for the given mode */ + if (!ret) { + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + } + mutex_unlock(&vpbe_dev->lock); + return ret; +} + +/** + * vpbe_g_std - Get the standard in the current encoder + * + * Get the standard in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) +{ + struct vpbe_enc_mode_info cur_timings = vpbe_dev->current_timings; + + if (cur_timings.timings_type & VPBE_ENC_STD) { + *std_id = cur_timings.timings.std_id; + return 0; + } + return -EINVAL; +} + +/** + * vpbe_set_mode - Set mode in the current encoder using mode info + * + * Use the mode string to decide what timings to set in the encoder + * This is typically useful when fbset command is used to change the current + * timings by specifying a string to indicate the timings. + */ +static int vpbe_set_mode(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info *mode_info) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index, ret = 0, i; + struct vpbe_enc_mode_info *preset_mode = NULL; + struct v4l2_dv_preset dv_preset; + + if ((NULL == mode_info) || (NULL == mode_info->name)) + return -EINVAL; + + for (i = 0; i < cfg->outputs[out_index].num_modes; i++) { + if (!strcmp(mode_info->name, + cfg->outputs[out_index].modes[i].name)) { + preset_mode = &cfg->outputs[out_index].modes[i]; + /* + * it may be one of the 3 timings type. Check and + * invoke right API + */ + if (preset_mode->timings_type & VPBE_ENC_STD) + return vpbe_s_std(vpbe_dev, + &preset_mode->timings.std_id); + if (preset_mode->timings_type & VPBE_ENC_DV_PRESET) { + dv_preset.preset = + preset_mode->timings.dv_preset; + return vpbe_s_dv_preset(vpbe_dev, &dv_preset); + } + } + } + + /* Only custom timing should reach here */ + if (preset_mode == NULL) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + if (!ret) { + vpbe_dev->current_timings = *preset_mode; + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + } + mutex_unlock(&vpbe_dev->lock); + return ret; +} + +static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev) +{ + int ret; + + ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode); + if (ret) + return ret; + /* set the default mode in the encoder */ + return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings); +} + +static int platform_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + if (strcmp("vpbe-venc", pdev->name) == 0) + venc_device = dev_get_platdata(&pdev->dev); + + return 0; +} + +/** + * vpbe_initialize() - Initialize the vpbe display controller + * @vpbe_dev - vpbe device ptr + * + * Master frame buffer device drivers calls this to initialize vpbe + * display controller. This will then registers v4l2 device and the sub + * devices and sets a current encoder sub device for display. v4l2 display + * device driver is the master and frame buffer display device driver is + * the slave. Frame buffer display driver checks the initialized during + * probe and exit if not initialized. Returns status. + */ +static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) +{ + struct encoder_config_info *enc_info; + struct v4l2_subdev **enc_subdev; + int i, ret = 0, num_encoders; + struct i2c_adapter *i2c_adap; + int output_index; + int err; + + /* + * v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer + * from the platform device by iteration of platform drivers and + * matching with device name + */ + if (NULL == vpbe_dev || NULL == dev) { + printk(KERN_ERR "Null device pointers.\n"); + return -ENODEV; + } + + if (vpbe_dev->initialized) + return 0; + + mutex_lock(&vpbe_dev->lock); + + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) { + /* We have dac clock available for platform */ + vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac"); + if (IS_ERR(vpbe_dev->dac_clk)) { + ret = PTR_ERR(vpbe_dev->dac_clk); + goto vpbe_unlock; + } + if (clk_enable(vpbe_dev->dac_clk)) { + ret = -ENODEV; + goto vpbe_unlock; + } + } + + /* first enable vpss clocks */ + vpss_enable_clock(VPSS_VPBE_CLOCK, 1); + + /* First register a v4l2 device */ + ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev); + if (ret) { + v4l2_err(dev->driver, + "Unable to register v4l2 device.\n"); + goto vpbe_fail_clock; + } + v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n"); + + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, + platform_device_get); + if (err < 0) + return err; + + vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev, + vpbe_dev->cfg->venc.module_name); + /* register venc sub device */ + if (vpbe_dev->venc == NULL) { + v4l2_err(&vpbe_dev->v4l2_dev, + "vpbe unable to init venc sub device\n"); + ret = -ENODEV; + goto vpbe_fail_v4l2_device; + } + /* initialize osd device */ + if (NULL != osd_device->ops.initialize) { + err = osd_device->ops.initialize(osd_device); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, + "unable to initialize the OSD device"); + err = -ENOMEM; + goto vpbe_fail_v4l2_device; + } + } + + /* + * Register any external encoders that are configured. At index 0 we + * store venc sd index. + */ + num_encoders = vpbe_dev->cfg->num_ext_encoders + 1; + vpbe_dev->encoders = kmalloc( + sizeof(struct v4l2_subdev *)*num_encoders, + GFP_KERNEL); + if (NULL == vpbe_dev->encoders) { + v4l2_err(&vpbe_dev->v4l2_dev, + "unable to allocate memory for encoders sub devices"); + ret = -ENOMEM; + goto vpbe_fail_v4l2_device; + } + + i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id); + for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) { + if (i == 0) { + /* venc is at index 0 */ + enc_subdev = &vpbe_dev->encoders[i]; + *enc_subdev = vpbe_dev->venc; + continue; + } + enc_info = &vpbe_dev->cfg->ext_encoders[i]; + if (enc_info->is_i2c) { + enc_subdev = &vpbe_dev->encoders[i]; + *enc_subdev = v4l2_i2c_new_subdev_board( + &vpbe_dev->v4l2_dev, i2c_adap, + &enc_info->board_info, NULL); + if (*enc_subdev) + v4l2_info(&vpbe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + enc_info->module_name); + else { + v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s" + " failed to register", + enc_info->module_name); + ret = -ENODEV; + goto vpbe_fail_sd_register; + } + } else + v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders" + " currently not supported"); + } + + /* set the current encoder and output to that of venc by default */ + vpbe_dev->current_sd_index = 0; + vpbe_dev->current_out_index = 0; + output_index = 0; + + mutex_unlock(&vpbe_dev->lock); + + printk(KERN_NOTICE "Setting default output to %s\n", def_output); + ret = vpbe_set_default_output(vpbe_dev); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s", + def_output); + return ret; + } + + printk(KERN_NOTICE "Setting default mode to %s\n", def_mode); + ret = vpbe_set_default_mode(vpbe_dev); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s", + def_mode); + return ret; + } + vpbe_dev->initialized = 1; + /* TBD handling of bootargs for default output and mode */ + return 0; + +vpbe_fail_sd_register: + kfree(vpbe_dev->encoders); +vpbe_fail_v4l2_device: + v4l2_device_unregister(&vpbe_dev->v4l2_dev); +vpbe_fail_clock: + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) + clk_put(vpbe_dev->dac_clk); +vpbe_unlock: + mutex_unlock(&vpbe_dev->lock); + return ret; +} + +/** + * vpbe_deinitialize() - de-initialize the vpbe display controller + * @dev - Master and slave device ptr + * + * vpbe_master and slave frame buffer devices calls this to de-initialize + * the display controller. It is called when master and slave device + * driver modules are removed and no longer requires the display controller. + */ +static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev) +{ + v4l2_device_unregister(&vpbe_dev->v4l2_dev); + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) + clk_put(vpbe_dev->dac_clk); + + kfree(vpbe_dev->encoders); + vpbe_dev->initialized = 0; + /* disaable vpss clocks */ + vpss_enable_clock(VPSS_VPBE_CLOCK, 0); +} + +static struct vpbe_device_ops vpbe_dev_ops = { + .g_cropcap = vpbe_g_cropcap, + .enum_outputs = vpbe_enum_outputs, + .set_output = vpbe_set_output, + .get_output = vpbe_get_output, + .s_dv_preset = vpbe_s_dv_preset, + .g_dv_preset = vpbe_g_dv_preset, + .enum_dv_presets = vpbe_enum_dv_presets, + .s_std = vpbe_s_std, + .g_std = vpbe_g_std, + .initialize = vpbe_initialize, + .deinitialize = vpbe_deinitialize, + .get_mode_info = vpbe_get_current_mode_info, + .set_mode = vpbe_set_mode, +}; + +static __devinit int vpbe_probe(struct platform_device *pdev) +{ + struct vpbe_config *cfg; + struct vpbe_device *vpbe_dev; + + int ret = -EINVAL; + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "No platform data\n"); + return -ENODEV; + } + cfg = pdev->dev.platform_data; + + if (!cfg->module_name[0] || + !cfg->osd.module_name[0] || + !cfg->venc.module_name[0]) { + v4l2_err(pdev->dev.driver, "vpbe display module names not" + " defined\n"); + return ret; + } + + vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL); + if (vpbe_dev == NULL) { + v4l2_err(pdev->dev.driver, "Unable to allocate memory" + " for vpbe_device\n"); + return -ENOMEM; + } + vpbe_dev->cfg = cfg; + vpbe_dev->ops = vpbe_dev_ops; + vpbe_dev->pdev = &pdev->dev; + + if (cfg->outputs->num_modes > 0) + vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0]; + else + return -ENODEV; + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpbe_dev); + mutex_init(&vpbe_dev->lock); + return 0; +} + +static int vpbe_remove(struct platform_device *device) +{ + struct vpbe_device *vpbe_dev = platform_get_drvdata(device); + + kfree(vpbe_dev); + return 0; +} + +static struct platform_driver vpbe_driver = { + .driver = { + .name = "vpbe_controller", + .owner = THIS_MODULE, + }, + .probe = vpbe_probe, + .remove = vpbe_remove, +}; + +/** + * vpbe_init: initialize the vpbe driver + * + * This function registers device and driver to the kernel + */ +static __init int vpbe_init(void) +{ + return platform_driver_register(&vpbe_driver); +} + +/** + * vpbe_cleanup : cleanup function for vpbe driver + * + * This will un-registers the device and driver to the kernel + */ +static void vpbe_cleanup(void) +{ + platform_driver_unregister(&vpbe_driver); +} + +/* Function for module initialization and cleanup */ +module_init(vpbe_init); +module_exit(vpbe_cleanup); diff --git a/include/media/davinci/vpbe.h b/include/media/davinci/vpbe.h new file mode 100644 index 0000000..9847d6e --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_H +#define _VPBE_H + +#include +#include + +#include +#include +#include +#include +#include + +/* OSD configuration info */ +struct osd_config_info { + char module_name[32]; +}; + +struct vpbe_output { + struct v4l2_output output; + /* + * If output capabilities include dv_preset, list supported presets + * below + */ + char *subdev_name; + /* + * defualt_mode identifies the default timings set at the venc or + * external encoder. + */ + char *default_mode; + /* + * Fields below are used for supporting multiple modes. For example, + * LCD panel might support different modes and they are listed here. + * Similarly for supporting external encoders, lcd controller port + * requires a set of non-standard timing values to be listed here for + * each supported mode since venc is used in non-standard timing mode + * for interfacing with external encoder similar to configuring lcd + * panel timings + */ + unsigned int num_modes; + struct vpbe_enc_mode_info *modes; + /* + * Bus configuration goes here for external encoders. Some encoders + * may require multiple interface types for each of the output. For + * example, SD modes would use YCC8 where as HD mode would use YCC16. + * Not sure if this is needed on a per mode basis instead of per + * output basis. If per mode is needed, we may have to move this to + * mode_info structure + */ +}; + +/* encoder configuration info */ +struct encoder_config_info { + char module_name[32]; + /* Is this an i2c device ? */ + unsigned int is_i2c:1; + /* i2c subdevice board info */ + struct i2c_board_info board_info; +}; + +/* structure for defining vpbe display subsystem components */ +struct vpbe_config { + char module_name[32]; + /* i2c bus adapter no */ + int i2c_adapter_id; + struct osd_config_info osd; + struct encoder_config_info venc; + /* external encoder information goes here */ + int num_ext_encoders; + struct encoder_config_info *ext_encoders; + int num_outputs; + /* Order is venc outputs followed by LCD and then external encoders */ + struct vpbe_output *outputs; +}; + +struct vpbe_device; + +struct vpbe_device_ops { + /* crop cap for the display */ + int (*g_cropcap)(struct vpbe_device *vpbe_dev, + struct v4l2_cropcap *cropcap); + + /* Enumerate the outputs */ + int (*enum_outputs)(struct vpbe_device *vpbe_dev, + struct v4l2_output *output); + + /* Set output to the given index */ + int (*set_output)(struct vpbe_device *vpbe_dev, + int index); + + /* Get current output */ + unsigned int (*get_output)(struct vpbe_device *vpbe_dev); + + /* Set DV preset at current output */ + int (*s_dv_preset)(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset); + + /* Get DV presets supported at the output */ + int (*g_dv_preset)(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset); + + /* Enumerate the DV Presets supported at the output */ + int (*enum_dv_presets)(struct vpbe_device *vpbe_dev, + struct v4l2_dv_enum_preset *preset_info); + + /* Set std at the output */ + int (*s_std)(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id); + + /* Get the current std at the output */ + int (*g_std)(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id); + + /* initialize the device */ + int (*initialize)(struct device *dev, struct vpbe_device *vpbe_dev); + + /* De-initialize the device */ + void (*deinitialize)(struct device *dev, struct vpbe_device *vpbe_dev); + + /* Get the current mode info */ + int (*get_mode_info)(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info*); + + /* + * Set the current mode in the encoder. Alternate way of setting + * standard or DV preset or custom timings in the encoder + */ + int (*set_mode)(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info*); + /* Power management operations */ + int (*suspend)(struct vpbe_device *vpbe_dev); + int (*resume)(struct vpbe_device *vpbe_dev); +}; + +/* struct for vpbe device */ +struct vpbe_device { + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* vpbe dispay controller cfg */ + struct vpbe_config *cfg; + /* parent device */ + struct device *pdev; + /* external encoder v4l2 sub devices */ + struct v4l2_subdev **encoders; + /* current encoder index */ + int current_sd_index; + struct mutex lock; + /* device initialized */ + int initialized; + /* vpbe dac clock */ + struct clk *dac_clk; + + /* + * fields below are accessed by users of vpbe_device. Not the + * ones above + */ + + /* current output */ + int current_out_index; + /* lock used by caller to do atomic operation on vpbe device */ + /* current timings set in the controller */ + struct vpbe_enc_mode_info current_timings; + /* venc sub device */ + struct v4l2_subdev *venc; + /* device operations below */ + struct vpbe_device_ops ops; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:41:28 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:11:28 +0530 Subject: [PATCH v16 03/13] davinci vpbe: OSD(On Screen Display) block Message-ID: <1301737288-4092-1-git-send-email-manjunath.hadli@ti.com> This patch implements the functionality of the OSD block of the VPBE. The OSD in total supports 4 planes or Video sources - 2 mainly RGB and 2 Video. The patch implements general handling of all the planes, with specific emphasis on the Video plane capabilities as the Video planes are supported through the V4L2 driver. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_osd.c | 1216 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 ++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1977 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_osd.c create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h create mode 100644 include/media/davinci/vpbe_osd.h diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c new file mode 100644 index 0000000..bbc43ea --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1216 @@ +/* + * Copyright (C) 2007-2010 Texas Instruments Inc + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Andy Lowe (alowe at mvista.com), MontaVista Software + * - Initial version + * Murali Karicheri (mkaricheri at gmail.com), Texas Instruments Ltd. + * - ported to sub device interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "vpbe_osd_regs.h" + +#define MODULE_NAME VPBE_OSD_SUBDEV_NAME + +/* register access routines */ +static inline u32 osd_read(struct osd_state *sd, u32 offset) +{ + struct osd_state *osd = sd; + return readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + writel(val, osd->osd_base + offset); + return val; +} + +static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset) +{ + struct osd_state *osd = sd; + + u32 addr = osd->osd_base + offset; + u32 val = readl(addr) | mask; + + writel(val, addr); + return val; +} + +static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset) +{ + struct osd_state *osd = sd; + u32 addr = osd->osd_base + offset; + u32 val = readl(addr) & ~mask; + + writel(val, addr); + return val; +} + +static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val, + u32 offset) +{ + struct osd_state *osd = sd; + + u32 addr = osd->osd_base + offset; + u32 new_val = (readl(addr) & ~mask) | (val & mask); + writel(new_val, addr); + return new_val; +} + +/* define some macros for layer and pixfmt classification */ +#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1)) +#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1)) +#define is_rgb_pixfmt(pixfmt) \ + (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888)) +#define is_yc_pixfmt(pixfmt) \ + (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \ + ((pixfmt) == PIXFMT_NV12)) +#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X +#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5) + +/** + * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446 + * @sd - ptr to struct osd_state + * @field_inversion - inversion flag + * @fb_base_phys - frame buffer address + * @lconfig - ptr to layer config + * + * This routine implements a workaround for the field signal inversion silicon + * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and + * lconfig parameters apply to the vid0 window. This routine should be called + * whenever the vid0 layer configuration or start address is modified, or when + * the OSD field inversion setting is modified. + * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or + * 0 otherwise + */ +static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, + int field_inversion, + unsigned long fb_base_phys, + const struct osd_layer_config *lconfig) +{ + struct osd_platform_data *pdata; + pdata = (struct osd_platform_data *)sd->dev->platform_data; + if (pdata->field_inv_wa_enable) { + + if (!field_inversion || !lconfig->interlaced) { + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); + osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR); + osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0, + OSD_MISCCTL); + return 0; + } else { + unsigned miscctl = OSD_MISCCTL_PPRV; + + osd_write(sd, + (fb_base_phys & ~0x1F) - lconfig->line_length, + OSD_VIDWIN0ADR); + osd_write(sd, + (fb_base_phys & ~0x1F) + lconfig->line_length, + OSD_PPVWIN0ADR); + osd_modify(sd, + OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl, + OSD_MISCCTL); + + return 1; + } + } + return 0; +} + +static void _osd_set_field_inversion(struct osd_state *sd, int enable) +{ + unsigned fsinv = 0; + + if (enable) + fsinv = OSD_MODE_FSINV; + + osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE); +} + +static void _osd_set_blink_attribute(struct osd_state *sd, int enable, + enum osd_blink_interval blink) +{ + u32 osdatrmd = 0; + + if (enable) { + osdatrmd |= OSD_OSDATRMD_BLNK; + osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT; + } + /* caller must ensure that OSD1 is configured in attribute mode */ + osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd, + OSD_OSDATRMD); +} + +static void _osd_set_rom_clut(struct osd_state *sd, + enum osd_rom_clut rom_clut) +{ + if (rom_clut == ROM_CLUT0) + osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); + else + osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); +} + +static void _osd_set_palette_map(struct osd_state *sd, + enum osd_win_layer osdwin, + unsigned char pixel_value, + unsigned char clut_index, + enum osd_pix_format pixfmt) +{ + int bmp_reg, bmp_offset, bmp_mask, bmp_shift; + static const int map_1bpp[] = { 0, 15 }; + static const int map_2bpp[] = { 0, 5, 10, 15 }; + + switch (pixfmt) { + case PIXFMT_1BPP: + bmp_reg = map_1bpp[pixel_value & 0x1]; + break; + case PIXFMT_2BPP: + bmp_reg = map_2bpp[pixel_value & 0x3]; + break; + case PIXFMT_4BPP: + bmp_reg = pixel_value & 0xf; + break; + default: + return; + } + + switch (osdwin) { + case OSDWIN_OSD0: + bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32); + break; + case OSDWIN_OSD1: + bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32); + break; + default: + return; + } + + if (bmp_reg & 1) { + bmp_shift = 8; + bmp_mask = 0xff << 8; + } else { + bmp_shift = 0; + bmp_mask = 0xff; + } + + osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset); +} + +static void _osd_set_rec601_attenuation(struct osd_state *sd, + enum osd_win_layer osdwin, int enable) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_modify(sd, OSD_OSDWIN0MD_ATN0E, + enable ? OSD_OSDWIN0MD_ATN0E : 0, + OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_modify(sd, OSD_OSDWIN1MD_ATN1E, + enable ? OSD_OSDWIN1MD_ATN1E : 0, + OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_blending_factor(struct osd_state *sd, + enum osd_win_layer osdwin, + enum osd_blending_factor blend) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_modify(sd, OSD_OSDWIN0MD_BLND0, + blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_modify(sd, OSD_OSDWIN1MD_BLND1, + blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_enable_color_key(struct osd_state *sd, + enum osd_win_layer osdwin, + unsigned colorkey, + enum osd_pix_format pixfmt) +{ + switch (pixfmt) { + case PIXFMT_RGB565: + osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS, + OSD_TRANSPVAL); + break; + default: + break; + } + + switch (osdwin) { + case OSDWIN_OSD0: + osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_disable_color_key(struct osd_state *sd, + enum osd_win_layer osdwin) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_osd_clut(struct osd_state *sd, + enum osd_win_layer osdwin, + enum osd_clut clut) +{ + u32 winmd = 0; + + switch (osdwin) { + case OSDWIN_OSD0: + if (clut == RAM_CLUT) + winmd |= OSD_OSDWIN0MD_CLUTS0; + osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + if (clut == RAM_CLUT) + winmd |= OSD_OSDWIN1MD_CLUTS1; + osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer, + enum osd_zoom_factor h_zoom, + enum osd_zoom_factor v_zoom) +{ + u32 winmd = 0; + + switch (layer) { + case WIN_OSD0: + winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT); + winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT); + osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd, + OSD_OSDWIN0MD); + break; + case WIN_VID0: + winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT); + winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT); + osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd, + OSD_VIDWINMD); + break; + case WIN_OSD1: + winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT); + winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT); + osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd, + OSD_OSDWIN1MD); + break; + case WIN_VID1: + winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT); + winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT); + osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd, + OSD_VIDWINMD); + break; + } +} + +static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer) +{ + switch (layer) { + case WIN_OSD0: + osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); + break; + case WIN_VID0: + osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); + break; + case WIN_OSD1: + /* disable attribute mode as well as disabling the window */ + osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, + OSD_OSDWIN1MD); + break; + case WIN_VID1: + osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); + break; + } +} + +static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (!win->is_enabled) { + spin_unlock_irqrestore(&osd->lock, flags); + return; + } + win->is_enabled = 0; + + _osd_disable_layer(sd, layer); + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void _osd_enable_attribute_mode(struct osd_state *sd) +{ + /* enable attribute mode for OSD1 */ + osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD); +} + +static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer) +{ + switch (layer) { + case WIN_OSD0: + osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); + break; + case WIN_VID0: + osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); + break; + case WIN_OSD1: + /* enable OSD1 and disable attribute mode */ + osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, + OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD); + break; + case WIN_VID1: + osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); + break; + } +} + +static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer, + int otherwin) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + spin_lock_irqsave(&osd->lock, flags); + + /* + * use otherwin flag to know this is the other vid window + * in YUV420 mode, if is, skip this check + */ + if (!otherwin && (!win->is_allocated || + !win->fb_base_phys || + !cfg->line_length || + !cfg->xsize || + !cfg->ysize)) { + spin_unlock_irqrestore(&osd->lock, flags); + return -1; + } + + if (win->is_enabled) { + spin_unlock_irqrestore(&osd->lock, flags); + return 0; + } + win->is_enabled = 1; + + if (cfg->pixfmt != PIXFMT_OSD_ATTR) + _osd_enable_layer(sd, layer); + else { + _osd_enable_attribute_mode(sd); + _osd_set_blink_attribute(sd, osd->is_blinking, osd->blink); + } + + spin_unlock_irqrestore(&osd->lock, flags); + return 0; +} + +static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst) +{ + switch (layer) { + case WIN_OSD0: + osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR); + break; + case WIN_VID0: + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); + break; + case WIN_OSD1: + osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR); + break; + case WIN_VID1: + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR); + break; + } +} + +static void osd_start_layer(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + spin_lock_irqsave(&osd->lock, flags); + + win->fb_base_phys = fb_base_phys & ~0x1F; + _osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst); + + if (layer == WIN_VID0) { + osd->pingpong = + _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, + win->fb_base_phys, + cfg); + } + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + *lconfig = win->lconfig; + + spin_unlock_irqrestore(&osd->lock, flags); +} + +/** + * try_layer_config() - Try a specific configuration for the layer + * @sd - ptr to struct osd_state + * @layer - layer to configure + * @lconfig - layer configuration to try + * + * If the requested lconfig is completely rejected and the value of lconfig on + * exit is the current lconfig, then try_layer_config() returns 1. Otherwise, + * try_layer_config() returns 0. A return value of 0 does not necessarily mean + * that the value of lconfig on exit is identical to the value of lconfig on + * entry, but merely that it represents a change from the current lconfig. + */ +static int try_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + int bad_config = 0; + + /* verify that the pixel format is compatible with the layer */ + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + case PIXFMT_2BPP: + case PIXFMT_4BPP: + case PIXFMT_8BPP: + case PIXFMT_RGB565: + bad_config = !is_osd_win(layer); + break; + case PIXFMT_YCbCrI: + case PIXFMT_YCrCbI: + bad_config = !is_vid_win(layer); + break; + case PIXFMT_RGB888: + bad_config = !is_vid_win(layer); + break; + case PIXFMT_NV12: + bad_config = 1; + break; + case PIXFMT_OSD_ATTR: + bad_config = (layer != WIN_OSD1); + break; + default: + bad_config = 1; + break; + } + if (bad_config) { + /* + * The requested pixel format is incompatible with the layer, + * so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return bad_config; + } + + /* DM6446: */ + /* only one OSD window at a time can use RGB pixel formats */ + if (is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) { + enum osd_pix_format pixfmt; + if (layer == WIN_OSD0) + pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt; + else + pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt; + + if (is_rgb_pixfmt(pixfmt)) { + /* + * The other OSD window is already configured for an + * RGB, so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return 1; + } + } + + /* DM6446: only one video window at a time can use RGB888 */ + if (is_vid_win(layer) && lconfig->pixfmt == PIXFMT_RGB888) { + enum osd_pix_format pixfmt; + + if (layer == WIN_VID0) + pixfmt = osd->win[WIN_VID1].lconfig.pixfmt; + else + pixfmt = osd->win[WIN_VID0].lconfig.pixfmt; + + if (pixfmt == PIXFMT_RGB888) { + /* + * The other video window is already configured for + * RGB888, so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return 1; + } + } + + /* window dimensions must be non-zero */ + if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) { + *lconfig = win->lconfig; + return 1; + } + + /* round line_length up to a multiple of 32 */ + lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32; + lconfig->line_length = + min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH); + lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE); + lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE); + lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE); + lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE); + lconfig->interlaced = (lconfig->interlaced != 0); + if (lconfig->interlaced) { + /* ysize and ypos must be even for interlaced displays */ + lconfig->ysize &= ~1; + lconfig->ypos &= ~1; + } + + return 0; +} + +static void _osd_disable_vid_rgb888(struct osd_state *sd) +{ + /* + * The DM6446 supports RGB888 pixel format in a single video window. + * This routine disables RGB888 pixel format for both video windows. + * The caller must ensure that neither video window is currently + * configured for RGB888 pixel format. + */ + osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL); +} + +static void _osd_enable_vid_rgb888(struct osd_state *sd, + enum osd_layer layer) +{ + /* + * The DM6446 supports RGB888 pixel format in a single video window. + * This routine enables RGB888 pixel format for the specified video + * window. The caller must ensure that the other video window is not + * currently configured for RGB888 pixel format, as this routine will + * disable RGB888 pixel format for the other window. + */ + if (layer == WIN_VID0) { + osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL_RGBEN, OSD_MISCCTL); + } else if (layer == WIN_VID1) { + osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL); + } +} + +static void _osd_set_cbcr_order(struct osd_state *sd, + enum osd_pix_format pixfmt) +{ + /* + * The caller must ensure that all windows using YC pixfmt use the same + * Cb/Cr order. + */ + if (pixfmt == PIXFMT_YCbCrI) + osd_clear(sd, OSD_MODE_CS, OSD_MODE); + else if (pixfmt == PIXFMT_YCrCbI) + osd_set(sd, OSD_MODE_CS, OSD_MODE); +} + +static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, + const struct osd_layer_config *lconfig) +{ + u32 winmd = 0, winmd_mask = 0, bmw = 0; + + _osd_set_cbcr_order(sd, lconfig->pixfmt); + + switch (layer) { + case WIN_OSD0: + winmd_mask |= OSD_OSDWIN0MD_RGB0E; + if (lconfig->pixfmt == PIXFMT_RGB565) + winmd |= OSD_OSDWIN0MD_RGB0E; + + winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0; + + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + bmw = 0; + break; + case PIXFMT_2BPP: + bmw = 1; + break; + case PIXFMT_4BPP: + bmw = 2; + break; + case PIXFMT_8BPP: + bmw = 3; + break; + default: + break; + } + winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT); + + if (lconfig->interlaced) + winmd |= OSD_OSDWIN0MD_OFF0; + + osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD); + osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST); + osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP); + osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL); + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP); + osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL); + } else { + osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP); + osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL); + } + break; + case WIN_VID0: + winmd_mask |= OSD_VIDWINMD_VFF0; + if (lconfig->interlaced) + winmd |= OSD_VIDWINMD_VFF0; + + osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); + osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST); + osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP); + osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL); + /* + * For YUV420P format the register contents are + * duplicated in both VID registers + */ + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP); + osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL); + } else { + osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP); + osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL); + } + break; + case WIN_OSD1: + /* + * The caller must ensure that OSD1 is disabled prior to + * switching from a normal mode to attribute mode or from + * attribute mode to a normal mode. + */ + if (lconfig->pixfmt == PIXFMT_OSD_ATTR) { + winmd_mask |= + OSD_OSDWIN1MD_ATN1E | OSD_OSDWIN1MD_RGB1E | + OSD_OSDWIN1MD_CLUTS1 | + OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1; + } else { + winmd_mask |= OSD_OSDWIN1MD_RGB1E; + if (lconfig->pixfmt == PIXFMT_RGB565) + winmd |= OSD_OSDWIN1MD_RGB1E; + + winmd_mask |= OSD_OSDWIN1MD_BMW1; + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + bmw = 0; + break; + case PIXFMT_2BPP: + bmw = 1; + break; + case PIXFMT_4BPP: + bmw = 2; + break; + case PIXFMT_8BPP: + bmw = 3; + break; + default: + break; + } + winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT); + } + + winmd_mask |= OSD_OSDWIN1MD_OFF1; + if (lconfig->interlaced) + winmd |= OSD_OSDWIN1MD_OFF1; + + osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD); + osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST); + osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP); + osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL); + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP); + osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL); + } else { + osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP); + osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL); + } + break; + case WIN_VID1: + winmd_mask |= OSD_VIDWINMD_VFF1; + if (lconfig->interlaced) + winmd |= OSD_VIDWINMD_VFF1; + + osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); + osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST); + osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP); + osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL); + /* + * For YUV420P format the register contents are + * duplicated in both VID registers + */ + osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D, + OSD_MISCCTL); + + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP); + osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL); + } else { + osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP); + osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL); + } + break; + } +} + +static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + struct osd_layer_config *cfg = &win->lconfig; + int reject_config; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + reject_config = try_layer_config(sd, layer, lconfig); + if (reject_config) { + spin_unlock_irqrestore(&osd->lock, flags); + return reject_config; + } + + /* update the current Cb/Cr order */ + if (is_yc_pixfmt(lconfig->pixfmt)) + osd->yc_pixfmt = lconfig->pixfmt; + + /* + * If we are switching OSD1 from normal mode to attribute mode or from + * attribute mode to normal mode, then we must disable the window. + */ + if (layer == WIN_OSD1) { + if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) && + (cfg->pixfmt != PIXFMT_OSD_ATTR)) || + ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && + (cfg->pixfmt == PIXFMT_OSD_ATTR))) { + win->is_enabled = 0; + _osd_disable_layer(sd, layer); + } + } + + _osd_set_layer_config(sd, layer, lconfig); + + if (layer == WIN_OSD1) { + struct osd_osdwin_state *osdwin_state = + &osd->osdwin[OSDWIN_OSD1]; + + if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && + (cfg->pixfmt == PIXFMT_OSD_ATTR)) { + /* + * We just switched OSD1 from attribute mode to normal + * mode, so we must initialize the CLUT select, the + * blend factor, transparency colorkey enable, and + * attenuation enable (DM6446 only) bits in the + * OSDWIN1MD register. + */ + _osd_set_osd_clut(sd, OSDWIN_OSD1, + osdwin_state->clut); + _osd_set_blending_factor(sd, OSDWIN_OSD1, + osdwin_state->blend); + if (osdwin_state->colorkey_blending) { + _osd_enable_color_key(sd, OSDWIN_OSD1, + osdwin_state-> + colorkey, + lconfig->pixfmt); + } else + _osd_disable_color_key(sd, OSDWIN_OSD1); + _osd_set_rec601_attenuation(sd, OSDWIN_OSD1, + osdwin_state-> + rec601_attenuation); + } else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) && + (cfg->pixfmt != PIXFMT_OSD_ATTR)) { + /* + * We just switched OSD1 from normal mode to attribute + * mode, so we must initialize the blink enable and + * blink interval bits in the OSDATRMD register. + */ + _osd_set_blink_attribute(sd, osd->is_blinking, + osd->blink); + } + } + + /* + * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format + * then configure a default palette map. + */ + if ((lconfig->pixfmt != cfg->pixfmt) && + ((lconfig->pixfmt == PIXFMT_1BPP) || + (lconfig->pixfmt == PIXFMT_2BPP) || + (lconfig->pixfmt == PIXFMT_4BPP))) { + enum osd_win_layer osdwin = + ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1); + struct osd_osdwin_state *osdwin_state = + &osd->osdwin[osdwin]; + unsigned char clut_index; + unsigned char clut_entries = 0; + + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + clut_entries = 2; + break; + case PIXFMT_2BPP: + clut_entries = 4; + break; + case PIXFMT_4BPP: + clut_entries = 16; + break; + default: + break; + } + /* + * The default palette map maps the pixel value to the clut + * index, i.e. pixel value 0 maps to clut entry 0, pixel value + * 1 maps to clut entry 1, etc. + */ + for (clut_index = 0; clut_index < 16; clut_index++) { + osdwin_state->palette_map[clut_index] = clut_index; + if (clut_index < clut_entries) { + _osd_set_palette_map(sd, osdwin, clut_index, + clut_index, + lconfig->pixfmt); + } + } + } + + *cfg = *lconfig; + /* DM6446: configure the RGB888 enable and window selection */ + if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888) + _osd_enable_vid_rgb888(sd, WIN_VID0); + else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888) + _osd_enable_vid_rgb888(sd, WIN_VID1); + else + _osd_disable_vid_rgb888(sd); + + if (layer == WIN_VID0) { + osd->pingpong = + _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, + win->fb_base_phys, + cfg); + } + + spin_unlock_irqrestore(&osd->lock, flags); + + return 0; +} + +static void osd_init_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + enum osd_win_layer osdwin; + struct osd_osdwin_state *osdwin_state; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + win->is_enabled = 0; + _osd_disable_layer(sd, layer); + + win->h_zoom = ZOOM_X1; + win->v_zoom = ZOOM_X1; + _osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom); + + win->fb_base_phys = 0; + _osd_start_layer(sd, layer, win->fb_base_phys, 0); + + cfg->line_length = 0; + cfg->xsize = 0; + cfg->ysize = 0; + cfg->xpos = 0; + cfg->ypos = 0; + cfg->interlaced = 0; + switch (layer) { + case WIN_OSD0: + case WIN_OSD1: + osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1; + osdwin_state = &osd->osdwin[osdwin]; + /* + * Other code relies on the fact that OSD windows default to a + * bitmap pixel format when they are deallocated, so don't + * change this default pixel format. + */ + cfg->pixfmt = PIXFMT_8BPP; + _osd_set_layer_config(sd, layer, cfg); + osdwin_state->clut = RAM_CLUT; + _osd_set_osd_clut(sd, osdwin, osdwin_state->clut); + osdwin_state->colorkey_blending = 0; + _osd_disable_color_key(sd, osdwin); + osdwin_state->blend = OSD_8_VID_0; + _osd_set_blending_factor(sd, osdwin, osdwin_state->blend); + osdwin_state->rec601_attenuation = 0; + _osd_set_rec601_attenuation(sd, osdwin, + osdwin_state-> + rec601_attenuation); + if (osdwin == OSDWIN_OSD1) { + osd->is_blinking = 0; + osd->blink = BLINK_X1; + } + break; + case WIN_VID0: + case WIN_VID1: + cfg->pixfmt = osd->yc_pixfmt; + _osd_set_layer_config(sd, layer, cfg); + break; + } + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void osd_release_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (!win->is_allocated) { + spin_unlock_irqrestore(&osd->lock, flags); + return; + } + + spin_unlock_irqrestore(&osd->lock, flags); + osd_init_layer(sd, layer); + spin_lock_irqsave(&osd->lock, flags); + + win->is_allocated = 0; + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static int osd_request_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (win->is_allocated) { + spin_unlock_irqrestore(&osd->lock, flags); + return -1; + } + win->is_allocated = 1; + + spin_unlock_irqrestore(&osd->lock, flags); + return 0; +} + +static void _osd_init(struct osd_state *sd) +{ + osd_write(sd, 0, OSD_MODE); + osd_write(sd, 0, OSD_VIDWINMD); + osd_write(sd, 0, OSD_OSDWIN0MD); + osd_write(sd, 0, OSD_OSDWIN1MD); + osd_write(sd, 0, OSD_RECTCUR); + osd_write(sd, 0, OSD_MISCCTL); +} + +static void osd_set_left_margin(struct osd_state *sd, u32 val) +{ + osd_write(sd, val, OSD_BASEPX); +} + +static void osd_set_top_margin(struct osd_state *sd, u32 val) +{ + osd_write(sd, val, OSD_BASEPY); +} + +static int osd_initialize(struct osd_state *osd) +{ + if (osd == NULL) + return -ENODEV; + _osd_init(osd); + + /* set default Cb/Cr order */ + osd->yc_pixfmt = PIXFMT_YCbCrI; + + _osd_set_field_inversion(osd, osd->field_inversion); + _osd_set_rom_clut(osd, osd->rom_clut); + + osd_init_layer(osd, WIN_OSD0); + osd_init_layer(osd, WIN_VID0); + osd_init_layer(osd, WIN_OSD1); + osd_init_layer(osd, WIN_VID1); + + return 0; +} + +static const struct vpbe_osd_ops osd_ops = { + .initialize = osd_initialize, + .request_layer = osd_request_layer, + .release_layer = osd_release_layer, + .enable_layer = osd_enable_layer, + .disable_layer = osd_disable_layer, + .set_layer_config = osd_set_layer_config, + .get_layer_config = osd_get_layer_config, + .start_layer = osd_start_layer, + .set_left_margin = osd_set_left_margin, + .set_top_margin = osd_set_top_margin, +}; + +static int osd_probe(struct platform_device *pdev) +{ + struct osd_state *osd; + struct resource *res; + struct osd_platform_data *pdata; + int ret = 0; + + osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL); + if (osd == NULL) + return -ENOMEM; + + osd->dev = &pdev->dev; + pdata = (struct osd_platform_data *)pdev->dev.platform_data; + osd->vpbe_type = (enum vpbe_types)pdata->vpbe_type; + if (NULL == pdev->dev.platform_data) { + dev_err(osd->dev, "No platform data defined for OSD" + " sub device\n"); + ret = -ENOENT; + goto free_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(osd->dev, "Unable to get OSD register address map\n"); + ret = -ENODEV; + goto free_mem; + } + osd->osd_base_phys = res->start; + osd->osd_size = res->end - res->start + 1; + if (!request_mem_region(osd->osd_base_phys, osd->osd_size, + MODULE_NAME)) { + dev_err(osd->dev, "Unable to reserve OSD MMIO region\n"); + ret = -ENODEV; + goto free_mem; + } + osd->osd_base = (unsigned long)ioremap_nocache(res->start, + osd->osd_size); + if (!osd->osd_base) { + dev_err(osd->dev, "Unable to map the OSD region\n"); + ret = -ENODEV; + goto release_mem_region; + } + spin_lock_init(&osd->lock); + osd->ops = osd_ops; + platform_set_drvdata(pdev, osd); + dev_notice(osd->dev, "OSD sub device probe success\n"); + return ret; + +release_mem_region: + release_mem_region(osd->osd_base_phys, osd->osd_size); +free_mem: + kfree(osd); + return ret; +} + +static int osd_remove(struct platform_device *pdev) +{ + struct osd_state *osd = platform_get_drvdata(pdev); + + iounmap((void *)osd->osd_base); + release_mem_region(osd->osd_base_phys, osd->osd_size); + kfree(osd); + return 0; +} + +static struct platform_driver osd_driver = { + .probe = osd_probe, + .remove = osd_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int osd_init(void) +{ + if (platform_driver_register(&osd_driver)) { + printk(KERN_ERR "Unable to register davinci osd driver\n"); + return -ENODEV; + } + + return 0; +} + +static void osd_exit(void) +{ + platform_driver_unregister(&osd_driver); +} + +module_init(osd_init); +module_exit(osd_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DaVinci OSD Manager Driver"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/media/video/davinci/vpbe_osd_regs.h b/drivers/media/video/davinci/vpbe_osd_regs.h new file mode 100644 index 0000000..584520f --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2006-2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_OSD_REGS_H +#define _VPBE_OSD_REGS_H + +/* VPBE Global Registers */ +#define VPBE_PID 0x0 +#define VPBE_PCR 0x4 + +/* VPSS CLock Registers */ +#define VPSSCLK_PID 0x00 +#define VPSSCLK_CLKCTRL 0x04 + +/* VPSS Buffer Logic Registers */ +#define VPSSBL_PID 0x00 +#define VPSSBL_PCR 0x04 +#define VPSSBL_BCR 0x08 +#define VPSSBL_INTSTAT 0x0C +#define VPSSBL_INTSEL 0x10 +#define VPSSBL_EVTSEL 0x14 +#define VPSSBL_MEMCTRL 0x18 +#define VPSSBL_CCDCMUX 0x1C + +/* DM365 ISP5 system configuration */ +#define ISP5_PID 0x0 +#define ISP5_PCCR 0x4 +#define ISP5_BCR 0x8 +#define ISP5_INTSTAT 0xC +#define ISP5_INTSEL1 0x10 +#define ISP5_INTSEL2 0x14 +#define ISP5_INTSEL3 0x18 +#define ISP5_EVTSEL 0x1c +#define ISP5_CCDCMUX 0x20 + +/* VPBE On-Screen Display Subsystem Registers (OSD) */ +#define OSD_MODE 0x00 +#define OSD_VIDWINMD 0x04 +#define OSD_OSDWIN0MD 0x08 +#define OSD_OSDWIN1MD 0x0C +#define OSD_OSDATRMD 0x0C +#define OSD_RECTCUR 0x10 +#define OSD_VIDWIN0OFST 0x18 +#define OSD_VIDWIN1OFST 0x1C +#define OSD_OSDWIN0OFST 0x20 +#define OSD_OSDWIN1OFST 0x24 +#define OSD_VIDWINADH 0x28 +#define OSD_VIDWIN0ADL 0x2C +#define OSD_VIDWIN0ADR 0x2C +#define OSD_VIDWIN1ADL 0x30 +#define OSD_VIDWIN1ADR 0x30 +#define OSD_OSDWINADH 0x34 +#define OSD_OSDWIN0ADL 0x38 +#define OSD_OSDWIN0ADR 0x38 +#define OSD_OSDWIN1ADL 0x3C +#define OSD_OSDWIN1ADR 0x3C +#define OSD_BASEPX 0x40 +#define OSD_BASEPY 0x44 +#define OSD_VIDWIN0XP 0x48 +#define OSD_VIDWIN0YP 0x4C +#define OSD_VIDWIN0XL 0x50 +#define OSD_VIDWIN0YL 0x54 +#define OSD_VIDWIN1XP 0x58 +#define OSD_VIDWIN1YP 0x5C +#define OSD_VIDWIN1XL 0x60 +#define OSD_VIDWIN1YL 0x64 +#define OSD_OSDWIN0XP 0x68 +#define OSD_OSDWIN0YP 0x6C +#define OSD_OSDWIN0XL 0x70 +#define OSD_OSDWIN0YL 0x74 +#define OSD_OSDWIN1XP 0x78 +#define OSD_OSDWIN1YP 0x7C +#define OSD_OSDWIN1XL 0x80 +#define OSD_OSDWIN1YL 0x84 +#define OSD_CURXP 0x88 +#define OSD_CURYP 0x8C +#define OSD_CURXL 0x90 +#define OSD_CURYL 0x94 +#define OSD_W0BMP01 0xA0 +#define OSD_W0BMP23 0xA4 +#define OSD_W0BMP45 0xA8 +#define OSD_W0BMP67 0xAC +#define OSD_W0BMP89 0xB0 +#define OSD_W0BMPAB 0xB4 +#define OSD_W0BMPCD 0xB8 +#define OSD_W0BMPEF 0xBC +#define OSD_W1BMP01 0xC0 +#define OSD_W1BMP23 0xC4 +#define OSD_W1BMP45 0xC8 +#define OSD_W1BMP67 0xCC +#define OSD_W1BMP89 0xD0 +#define OSD_W1BMPAB 0xD4 +#define OSD_W1BMPCD 0xD8 +#define OSD_W1BMPEF 0xDC +#define OSD_VBNDRY 0xE0 +#define OSD_EXTMODE 0xE4 +#define OSD_MISCCTL 0xE8 +#define OSD_CLUTRAMYCB 0xEC +#define OSD_CLUTRAMCR 0xF0 +#define OSD_TRANSPVAL 0xF4 +#define OSD_TRANSPVALL 0xF4 +#define OSD_TRANSPVALU 0xF8 +#define OSD_TRANSPBMPIDX 0xFC +#define OSD_PPVWIN0ADR 0xFC + +/* bit definitions */ +#define VPBE_PCR_VENC_DIV (1 << 1) +#define VPBE_PCR_CLK_OFF (1 << 0) + +#define VPSSBL_INTSTAT_HSSIINT (1 << 14) +#define VPSSBL_INTSTAT_CFALDINT (1 << 13) +#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12) +#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11) +#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10) +#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9) +#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8) +#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7) +#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6) +#define VPSSBL_INTSTAT_OSDINT (1 << 5) +#define VPSSBL_INTSTAT_VENCINT (1 << 4) +#define VPSSBL_INTSTAT_H3AINT (1 << 3) +#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2) +#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1) +#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0) + +/* DM365 ISP5 bit definitions */ +#define ISP5_INTSTAT_VENCINT (1 << 21) +#define ISP5_INTSTAT_OSDINT (1 << 20) + +/* VMOD TVTYP options for HDMD=0 */ +#define SDTV_NTSC 0 +#define SDTV_PAL 1 +/* VMOD TVTYP options for HDMD=1 */ +#define HDTV_525P 0 +#define HDTV_625P 1 +#define HDTV_1080I 2 +#define HDTV_720P 3 + +#define OSD_MODE_CS (1 << 15) +#define OSD_MODE_OVRSZ (1 << 14) +#define OSD_MODE_OHRSZ (1 << 13) +#define OSD_MODE_EF (1 << 12) +#define OSD_MODE_VVRSZ (1 << 11) +#define OSD_MODE_VHRSZ (1 << 10) +#define OSD_MODE_FSINV (1 << 9) +#define OSD_MODE_BCLUT (1 << 8) +#define OSD_MODE_CABG_SHIFT 0 +#define OSD_MODE_CABG (0xff << 0) + +#define OSD_VIDWINMD_VFINV (1 << 15) +#define OSD_VIDWINMD_V1EFC (1 << 14) +#define OSD_VIDWINMD_VHZ1_SHIFT 12 +#define OSD_VIDWINMD_VHZ1 (3 << 12) +#define OSD_VIDWINMD_VVZ1_SHIFT 10 +#define OSD_VIDWINMD_VVZ1 (3 << 10) +#define OSD_VIDWINMD_VFF1 (1 << 9) +#define OSD_VIDWINMD_ACT1 (1 << 8) +#define OSD_VIDWINMD_V0EFC (1 << 6) +#define OSD_VIDWINMD_VHZ0_SHIFT 4 +#define OSD_VIDWINMD_VHZ0 (3 << 4) +#define OSD_VIDWINMD_VVZ0_SHIFT 2 +#define OSD_VIDWINMD_VVZ0 (3 << 2) +#define OSD_VIDWINMD_VFF0 (1 << 1) +#define OSD_VIDWINMD_ACT0 (1 << 0) + +#define OSD_OSDWIN0MD_ATN0E (1 << 14) +#define OSD_OSDWIN0MD_RGB0E (1 << 13) +#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13 +#define OSD_OSDWIN0MD_BMP0MD (3 << 13) +#define OSD_OSDWIN0MD_CLUTS0 (1 << 12) +#define OSD_OSDWIN0MD_OHZ0_SHIFT 10 +#define OSD_OSDWIN0MD_OHZ0 (3 << 10) +#define OSD_OSDWIN0MD_OVZ0_SHIFT 8 +#define OSD_OSDWIN0MD_OVZ0 (3 << 8) +#define OSD_OSDWIN0MD_BMW0_SHIFT 6 +#define OSD_OSDWIN0MD_BMW0 (3 << 6) +#define OSD_OSDWIN0MD_BLND0_SHIFT 3 +#define OSD_OSDWIN0MD_BLND0 (7 << 3) +#define OSD_OSDWIN0MD_TE0 (1 << 2) +#define OSD_OSDWIN0MD_OFF0 (1 << 1) +#define OSD_OSDWIN0MD_OACT0 (1 << 0) + +#define OSD_OSDWIN1MD_OASW (1 << 15) +#define OSD_OSDWIN1MD_ATN1E (1 << 14) +#define OSD_OSDWIN1MD_RGB1E (1 << 13) +#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13 +#define OSD_OSDWIN1MD_BMP1MD (3 << 13) +#define OSD_OSDWIN1MD_CLUTS1 (1 << 12) +#define OSD_OSDWIN1MD_OHZ1_SHIFT 10 +#define OSD_OSDWIN1MD_OHZ1 (3 << 10) +#define OSD_OSDWIN1MD_OVZ1_SHIFT 8 +#define OSD_OSDWIN1MD_OVZ1 (3 << 8) +#define OSD_OSDWIN1MD_BMW1_SHIFT 6 +#define OSD_OSDWIN1MD_BMW1 (3 << 6) +#define OSD_OSDWIN1MD_BLND1_SHIFT 3 +#define OSD_OSDWIN1MD_BLND1 (7 << 3) +#define OSD_OSDWIN1MD_TE1 (1 << 2) +#define OSD_OSDWIN1MD_OFF1 (1 << 1) +#define OSD_OSDWIN1MD_OACT1 (1 << 0) + +#define OSD_OSDATRMD_OASW (1 << 15) +#define OSD_OSDATRMD_OHZA_SHIFT 10 +#define OSD_OSDATRMD_OHZA (3 << 10) +#define OSD_OSDATRMD_OVZA_SHIFT 8 +#define OSD_OSDATRMD_OVZA (3 << 8) +#define OSD_OSDATRMD_BLNKINT_SHIFT 6 +#define OSD_OSDATRMD_BLNKINT (3 << 6) +#define OSD_OSDATRMD_OFFA (1 << 1) +#define OSD_OSDATRMD_BLNK (1 << 0) + +#define OSD_RECTCUR_RCAD_SHIFT 8 +#define OSD_RECTCUR_RCAD (0xff << 8) +#define OSD_RECTCUR_CLUTSR (1 << 7) +#define OSD_RECTCUR_RCHW_SHIFT 4 +#define OSD_RECTCUR_RCHW (7 << 4) +#define OSD_RECTCUR_RCVW_SHIFT 1 +#define OSD_RECTCUR_RCVW (7 << 1) +#define OSD_RECTCUR_RCACT (1 << 0) + +#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0) + +#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0) + +#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0) + +#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0) + +#define OSD_WINOFST_AH_SHIFT 9 + +#define OSD_VIDWIN0OFST_V0AH (0xf << 9) +#define OSD_VIDWIN1OFST_V1AH (0xf << 9) +#define OSD_OSDWIN0OFST_O0AH (0xf << 9) +#define OSD_OSDWIN1OFST_O1AH (0xf << 9) + +#define OSD_VIDWINADH_V1AH_SHIFT 8 +#define OSD_VIDWINADH_V1AH (0x7f << 8) +#define OSD_VIDWINADH_V0AH_SHIFT 0 +#define OSD_VIDWINADH_V0AH (0x7f << 0) + +#define OSD_VIDWIN0ADL_V0AL (0xffff << 0) + +#define OSD_VIDWIN1ADL_V1AL (0xffff << 0) + +#define OSD_OSDWINADH_O1AH_SHIFT 8 +#define OSD_OSDWINADH_O1AH (0x7f << 8) +#define OSD_OSDWINADH_O0AH_SHIFT 0 +#define OSD_OSDWINADH_O0AH (0x7f << 0) + +#define OSD_OSDWIN0ADL_O0AL (0xffff << 0) + +#define OSD_OSDWIN1ADL_O1AL (0xffff << 0) + +#define OSD_BASEPX_BPX (0x3ff << 0) + +#define OSD_BASEPY_BPY (0x1ff << 0) + +#define OSD_VIDWIN0XP_V0X (0x7ff << 0) + +#define OSD_VIDWIN0YP_V0Y (0x7ff << 0) + +#define OSD_VIDWIN0XL_V0W (0x7ff << 0) + +#define OSD_VIDWIN0YL_V0H (0x7ff << 0) + +#define OSD_VIDWIN1XP_V1X (0x7ff << 0) + +#define OSD_VIDWIN1YP_V1Y (0x7ff << 0) + +#define OSD_VIDWIN1XL_V1W (0x7ff << 0) + +#define OSD_VIDWIN1YL_V1H (0x7ff << 0) + +#define OSD_OSDWIN0XP_W0X (0x7ff << 0) + +#define OSD_OSDWIN0YP_W0Y (0x7ff << 0) + +#define OSD_OSDWIN0XL_W0W (0x7ff << 0) + +#define OSD_OSDWIN0YL_W0H (0x7ff << 0) + +#define OSD_OSDWIN1XP_W1X (0x7ff << 0) + +#define OSD_OSDWIN1YP_W1Y (0x7ff << 0) + +#define OSD_OSDWIN1XL_W1W (0x7ff << 0) + +#define OSD_OSDWIN1YL_W1H (0x7ff << 0) + +#define OSD_CURXP_RCSX (0x7ff << 0) + +#define OSD_CURYP_RCSY (0x7ff << 0) + +#define OSD_CURXL_RCSW (0x7ff << 0) + +#define OSD_CURYL_RCSH (0x7ff << 0) + +#define OSD_EXTMODE_EXPMDSEL (1 << 15) +#define OSD_EXTMODE_SCRNHEXP_SHIFT 13 +#define OSD_EXTMODE_SCRNHEXP (3 << 13) +#define OSD_EXTMODE_SCRNVEXP (1 << 12) +#define OSD_EXTMODE_OSD1BLDCHR (1 << 11) +#define OSD_EXTMODE_OSD0BLDCHR (1 << 10) +#define OSD_EXTMODE_ATNOSD1EN (1 << 9) +#define OSD_EXTMODE_ATNOSD0EN (1 << 8) +#define OSD_EXTMODE_OSDHRSZ15 (1 << 7) +#define OSD_EXTMODE_VIDHRSZ15 (1 << 6) +#define OSD_EXTMODE_ZMFILV1HEN (1 << 5) +#define OSD_EXTMODE_ZMFILV1VEN (1 << 4) +#define OSD_EXTMODE_ZMFILV0HEN (1 << 3) +#define OSD_EXTMODE_ZMFILV0VEN (1 << 2) +#define OSD_EXTMODE_EXPFILHEN (1 << 1) +#define OSD_EXTMODE_EXPFILVEN (1 << 0) + +#define OSD_MISCCTL_BLDSEL (1 << 15) +#define OSD_MISCCTL_S420D (1 << 14) +#define OSD_MISCCTL_BMAPT (1 << 13) +#define OSD_MISCCTL_DM365M (1 << 12) +#define OSD_MISCCTL_RGBEN (1 << 7) +#define OSD_MISCCTL_RGBWIN (1 << 6) +#define OSD_MISCCTL_DMANG (1 << 6) +#define OSD_MISCCTL_TMON (1 << 5) +#define OSD_MISCCTL_RSEL (1 << 4) +#define OSD_MISCCTL_CPBSY (1 << 3) +#define OSD_MISCCTL_PPSW (1 << 2) +#define OSD_MISCCTL_PPRV (1 << 1) + +#define OSD_CLUTRAMYCB_Y_SHIFT 8 +#define OSD_CLUTRAMYCB_Y (0xff << 8) +#define OSD_CLUTRAMYCB_CB_SHIFT 0 +#define OSD_CLUTRAMYCB_CB (0xff << 0) + +#define OSD_CLUTRAMCR_CR_SHIFT 8 +#define OSD_CLUTRAMCR_CR (0xff << 8) +#define OSD_CLUTRAMCR_CADDR_SHIFT 0 +#define OSD_CLUTRAMCR_CADDR (0xff << 0) + +#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0) + +#define OSD_TRANSPVALL_RGBL (0xffff << 0) + +#define OSD_TRANSPVALU_Y_SHIFT 8 +#define OSD_TRANSPVALU_Y (0xff << 8) +#define OSD_TRANSPVALU_RGBU_SHIFT 0 +#define OSD_TRANSPVALU_RGBU (0xff << 0) + +#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8 +#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8) +#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0 +#define OSD_TRANSPBMPIDX_BMP0 0xff + +#endif /* _DAVINCI_VPBE_H_ */ diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h new file mode 100644 index 0000000..7e0e34a --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2007-2009 Texas Instruments Inc + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Andy Lowe (alowe at mvista.com), MontaVista Software + * - Initial version + * Murali Karicheri (mkaricheri at gmail.com), Texas Instruments Ltd. + * - ported to sub device interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2.. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _OSD_H +#define _OSD_H + +#define VPBE_OSD_SUBDEV_NAME "vpbe-osd" + +/** + * enum osd_layer + * @WIN_OSD0: On-Screen Display Window 0 + * @WIN_VID0: Video Window 0 + * @WIN_OSD1: On-Screen Display Window 1 + * @WIN_VID1: Video Window 1 + * + * Description: + * An enumeration of the osd display layers. + */ +enum osd_layer { + WIN_OSD0, + WIN_VID0, + WIN_OSD1, + WIN_VID1, +}; + +/** + * enum osd_win_layer + * @OSDWIN_OSD0: On-Screen Display Window 0 + * @OSDWIN_OSD1: On-Screen Display Window 1 + * + * Description: + * An enumeration of the OSD Window layers. + */ +enum osd_win_layer { + OSDWIN_OSD0, + OSDWIN_OSD1, +}; + +/** + * enum osd_pix_format + * @PIXFMT_1BPP: 1-bit-per-pixel bitmap + * @PIXFMT_2BPP: 2-bits-per-pixel bitmap + * @PIXFMT_4BPP: 4-bits-per-pixel bitmap + * @PIXFMT_8BPP: 8-bits-per-pixel bitmap + * @PIXFMT_RGB565: 16-bits-per-pixel RGB565 + * @PIXFMT_YCbCrI: YUV 4:2:2 + * @PIXFMT_RGB888: 24-bits-per-pixel RGB888 + * @PIXFMT_YCrCbI: YUV 4:2:2 with chroma swap + * @PIXFMT_NV12: YUV 4:2:0 planar + * @PIXFMT_OSD_ATTR: OSD Attribute Window pixel format (4bpp) + * + * Description: + * An enumeration of the DaVinci pixel formats. + */ +enum osd_pix_format { + PIXFMT_1BPP = 0, + PIXFMT_2BPP, + PIXFMT_4BPP, + PIXFMT_8BPP, + PIXFMT_RGB565, + PIXFMT_YCbCrI, + PIXFMT_RGB888, + PIXFMT_YCrCbI, + PIXFMT_NV12, + PIXFMT_OSD_ATTR, +}; + +/** + * enum osd_h_exp_ratio + * @H_EXP_OFF: no expansion (1/1) + * @H_EXP_9_OVER_8: 9/8 expansion ratio + * @H_EXP_3_OVER_2: 3/2 expansion ratio + * + * Description: + * An enumeration of the available horizontal expansion ratios. + */ +enum osd_h_exp_ratio { + H_EXP_OFF, + H_EXP_9_OVER_8, + H_EXP_3_OVER_2, +}; + +/** + * enum osd_v_exp_ratio + * @V_EXP_OFF: no expansion (1/1) + * @V_EXP_6_OVER_5: 6/5 expansion ratio + * + * Description: + * An enumeration of the available vertical expansion ratios. + */ +enum osd_v_exp_ratio { + V_EXP_OFF, + V_EXP_6_OVER_5, +}; + +/** + * enum osd_zoom_factor + * @ZOOM_X1: no zoom (x1) + * @ZOOM_X2: x2 zoom + * @ZOOM_X4: x4 zoom + * + * Description: + * An enumeration of the available zoom factors. + */ +enum osd_zoom_factor { + ZOOM_X1, + ZOOM_X2, + ZOOM_X4, +}; + +/** + * enum osd_clut + * @ROM_CLUT: ROM CLUT + * @RAM_CLUT: RAM CLUT + * + * Description: + * An enumeration of the available Color Lookup Tables (CLUTs). + */ +enum osd_clut { + ROM_CLUT, + RAM_CLUT, +}; + +/** + * enum osd_rom_clut + * @ROM_CLUT0: Macintosh CLUT + * @ROM_CLUT1: CLUT from DM270 and prior devices + * + * Description: + * An enumeration of the ROM Color Lookup Table (CLUT) options. + */ +enum osd_rom_clut { + ROM_CLUT0, + ROM_CLUT1, +}; + +/** + * enum osd_blending_factor + * @OSD_0_VID_8: OSD pixels are fully transparent + * @OSD_1_VID_7: OSD pixels contribute 1/8, video pixels contribute 7/8 + * @OSD_2_VID_6: OSD pixels contribute 2/8, video pixels contribute 6/8 + * @OSD_3_VID_5: OSD pixels contribute 3/8, video pixels contribute 5/8 + * @OSD_4_VID_4: OSD pixels contribute 4/8, video pixels contribute 4/8 + * @OSD_5_VID_3: OSD pixels contribute 5/8, video pixels contribute 3/8 + * @OSD_6_VID_2: OSD pixels contribute 6/8, video pixels contribute 2/8 + * @OSD_8_VID_0: OSD pixels are fully opaque + * + * Description: + * An enumeration of the DaVinci pixel blending factor options. + */ +enum osd_blending_factor { + OSD_0_VID_8, + OSD_1_VID_7, + OSD_2_VID_6, + OSD_3_VID_5, + OSD_4_VID_4, + OSD_5_VID_3, + OSD_6_VID_2, + OSD_8_VID_0, +}; + +/** + * enum osd_blink_interval + * @BLINK_X1: blink interval is 1 vertical refresh cycle + * @BLINK_X2: blink interval is 2 vertical refresh cycles + * @BLINK_X3: blink interval is 3 vertical refresh cycles + * @BLINK_X4: blink interval is 4 vertical refresh cycles + * + * Description: + * An enumeration of the DaVinci pixel blinking interval options. + */ +enum osd_blink_interval { + BLINK_X1, + BLINK_X2, + BLINK_X3, + BLINK_X4, +}; + +/** + * enum osd_cursor_h_width + * @H_WIDTH_1: horizontal line width is 1 pixel + * @H_WIDTH_4: horizontal line width is 4 pixels + * @H_WIDTH_8: horizontal line width is 8 pixels + * @H_WIDTH_12: horizontal line width is 12 pixels + * @H_WIDTH_16: horizontal line width is 16 pixels + * @H_WIDTH_20: horizontal line width is 20 pixels + * @H_WIDTH_24: horizontal line width is 24 pixels + * @H_WIDTH_28: horizontal line width is 28 pixels + */ +enum osd_cursor_h_width { + H_WIDTH_1, + H_WIDTH_4, + H_WIDTH_8, + H_WIDTH_12, + H_WIDTH_16, + H_WIDTH_20, + H_WIDTH_24, + H_WIDTH_28, +}; + +/** + * enum davinci_cursor_v_width + * @V_WIDTH_1: vertical line width is 1 line + * @V_WIDTH_2: vertical line width is 2 lines + * @V_WIDTH_4: vertical line width is 4 lines + * @V_WIDTH_6: vertical line width is 6 lines + * @V_WIDTH_8: vertical line width is 8 lines + * @V_WIDTH_10: vertical line width is 10 lines + * @V_WIDTH_12: vertical line width is 12 lines + * @V_WIDTH_14: vertical line width is 14 lines + */ +enum osd_cursor_v_width { + V_WIDTH_1, + V_WIDTH_2, + V_WIDTH_4, + V_WIDTH_6, + V_WIDTH_8, + V_WIDTH_10, + V_WIDTH_12, + V_WIDTH_14, +}; + +/** + * struct osd_cursor_config + * @xsize: horizontal size in pixels + * @ysize: vertical size in lines + * @xpos: horizontal offset in pixels from the left edge of the display + * @ypos: vertical offset in lines from the top of the display + * @interlaced: Non-zero if the display is interlaced, or zero otherwise + * @h_width: horizontal line width + * @v_width: vertical line width + * @clut: the CLUT selector (ROM or RAM) for the cursor color + * @clut_index: an index into the CLUT for the cursor color + * + * Description: + * A structure describing the configuration parameters of the hardware + * rectangular cursor. + */ +struct osd_cursor_config { + unsigned xsize; + unsigned ysize; + unsigned xpos; + unsigned ypos; + int interlaced; + enum osd_cursor_h_width h_width; + enum osd_cursor_v_width v_width; + enum osd_clut clut; + unsigned char clut_index; +}; + +/** + * struct osd_layer_config + * @pixfmt: pixel format + * @line_length: offset in bytes between start of each line in memory + * @xsize: number of horizontal pixels displayed per line + * @ysize: number of lines displayed + * @xpos: horizontal offset in pixels from the left edge of the display + * @ypos: vertical offset in lines from the top of the display + * @interlaced: Non-zero if the display is interlaced, or zero otherwise + * + * Description: + * A structure describing the configuration parameters of an On-Screen Display + * (OSD) or video layer related to how the image is stored in memory. + * @line_length must be a multiple of the cache line size (32 bytes). + */ +struct osd_layer_config { + enum osd_pix_format pixfmt; + unsigned line_length; + unsigned xsize; + unsigned ysize; + unsigned xpos; + unsigned ypos; + int interlaced; +}; + +/* OSD events. Should match with that in vpbe_venc.h */ +#define OSD_END_OF_FRAME BIT(0) +#define OSD_FIRST_FIELD BIT(1) +#define OSD_SECOND_FIELD BIT(2) + +/* parameters that apply on a per-window (OSD or video) basis */ +struct osd_window_state { + int is_allocated; + int is_enabled; + unsigned long fb_base_phys; + enum osd_zoom_factor h_zoom; + enum osd_zoom_factor v_zoom; + struct osd_layer_config lconfig; +}; + +/* parameters that apply on a per-OSD-window basis */ +struct osd_osdwin_state { + enum osd_clut clut; + enum osd_blending_factor blend; + int colorkey_blending; + unsigned colorkey; + int rec601_attenuation; + /* index is pixel value */ + unsigned char palette_map[16]; +}; + +/* hardware rectangular cursor parameters */ +struct osd_cursor_state { + int is_enabled; + struct osd_cursor_config config; +}; + +struct osd_state; + +struct vpbe_osd_ops { + int (*initialize)(struct osd_state *sd); + int (*request_layer)(struct osd_state *sd, enum osd_layer layer); + void (*release_layer)(struct osd_state *sd, enum osd_layer layer); + int (*enable_layer)(struct osd_state *sd, enum osd_layer layer, + int otherwin); + void (*disable_layer)(struct osd_state *sd, enum osd_layer layer); + int (*set_layer_config)(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig); + void (*get_layer_config)(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig); + void (*start_layer)(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst); + void (*set_left_margin)(struct osd_state *sd, u32 val); + void (*set_top_margin)(struct osd_state *sd, u32 val); + void (*set_interpolation_filter)(struct osd_state *sd, int filter); + int (*set_vid_expansion)(struct osd_state *sd, + enum osd_h_exp_ratio h_exp, + enum osd_v_exp_ratio v_exp); + void (*get_vid_expansion)(struct osd_state *sd, + enum osd_h_exp_ratio *h_exp, + enum osd_v_exp_ratio *v_exp); + void (*set_zoom)(struct osd_state *sd, enum osd_layer layer, + enum osd_zoom_factor h_zoom, + enum osd_zoom_factor v_zoom); +}; + +struct osd_state { + enum vpbe_types vpbe_type; + spinlock_t lock; + struct device *dev; + dma_addr_t osd_base_phys; + unsigned long osd_base; + unsigned long osd_size; + /* 1-->the isr will toggle the VID0 ping-pong buffer */ + int pingpong; + int interpolation_filter; + int field_inversion; + enum osd_h_exp_ratio osd_h_exp; + enum osd_v_exp_ratio osd_v_exp; + enum osd_h_exp_ratio vid_h_exp; + enum osd_v_exp_ratio vid_v_exp; + enum osd_clut backg_clut; + unsigned backg_clut_index; + enum osd_rom_clut rom_clut; + int is_blinking; + /* attribute window blinking enabled */ + enum osd_blink_interval blink; + /* YCbCrI or YCrCbI */ + enum osd_pix_format yc_pixfmt; + /* columns are Y, Cb, Cr */ + unsigned char clut_ram[256][3]; + struct osd_cursor_state cursor; + /* OSD0, VID0, OSD1, VID1 */ + struct osd_window_state win[4]; + /* OSD0, OSD1 */ + struct osd_osdwin_state osdwin[2]; + /* OSD device Operations */ + struct vpbe_osd_ops ops; +}; + +struct osd_platform_data { + enum vpbe_types vpbe_type; + int field_inv_wa_enable; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:41:44 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:11:44 +0530 Subject: [PATCH v16 04/13] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1301737304-4163-1-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, which is responsible for the blending of all source planes and timing generation for Video modes like NTSC, PAL and other digital outputs. the VENC implementation currently supports COMPOSITE and COMPONENT outputs and NTSC and PAL resolutions through the analog DACs. The venc block is implemented as a subdevice, allowing for additional external and internal encoders of other kind to plug-in. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_venc.c | 556 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 ++++++++ include/media/davinci/vpbe_venc.h | 44 ++ 3 files changed, 777 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_venc.c create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h create mode 100644 include/media/davinci/vpbe_venc.h diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c new file mode 100644 index 0000000..1131e2d --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "vpbe_venc_regs.h" + +#define MODULE_NAME VPBE_VENC_SUBDEV_NAME + +static int debug = 2; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-2"); + +struct venc_state { + struct v4l2_subdev sd; + struct venc_callback *callback; + struct venc_platform_data *pdata; + struct device *pdev; + u32 output; + v4l2_std_id std; + spinlock_t lock; + void __iomem *venc_base; + void __iomem *vdaccfg_reg; +}; + +static inline struct venc_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct venc_state, sd); +} + +static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset) +{ + struct venc_state *venc = to_state(sd); + + return readl(venc->venc_base + offset); +} + +static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val) +{ + struct venc_state *venc = to_state(sd); + writel(val, (venc->venc_base + offset)); + return val; +} + +static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset, + u32 val, u32 mask) +{ + u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask); + + venc_write(sd, offset, new_val); + return new_val; +} + +static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val) +{ + struct venc_state *venc = to_state(sd); + + writel(val, venc->vdaccfg_reg); + + val = readl(venc->vdaccfg_reg); + return val; +} + +/* This function sets the dac of the VPBE for various outputs + */ +static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) +{ + int ret = 0; + + switch (out_index) { + case 0: + v4l2_dbg(debug, 1, sd, "Setting output to Composite\n"); + venc_write(sd, VENC_DACSEL, 0); + break; + case 1: + v4l2_dbg(debug, 1, sd, "Setting output to S-Video\n"); + venc_write(sd, VENC_DACSEL, 0x210); + break; + case 2: + venc_write(sd, VENC_DACSEL, 0x543); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); + + if (benable) { + venc_write(sd, VENC_VMOD, 0); + venc_write(sd, VENC_CVBS, 0); + venc_write(sd, VENC_LCDOUT, 0); + venc_write(sd, VENC_HSPLS, 0); + venc_write(sd, VENC_HSTART, 0); + venc_write(sd, VENC_HVALID, 0); + venc_write(sd, VENC_HINT, 0); + venc_write(sd, VENC_VSPLS, 0); + venc_write(sd, VENC_VSTART, 0); + venc_write(sd, VENC_VVALID, 0); + venc_write(sd, VENC_VINT, 0); + venc_write(sd, VENC_YCCCTL, 0); + venc_write(sd, VENC_DACSEL, 0); + + } else { + venc_write(sd, VENC_VMOD, 0); + /* disable VCLK output pin enable */ + venc_write(sd, VENC_VIDCTL, 0x141); + + /* Disable output sync pins */ + venc_write(sd, VENC_SYNCCTL, 0); + + /* Disable DCLOCK */ + venc_write(sd, VENC_DCLKCTL, 0); + venc_write(sd, VENC_DRGBX1, 0x0000057C); + + /* Disable LCD output control (accepting default polarity) */ + venc_write(sd, VENC_LCDOUT, 0); + venc_write(sd, VENC_CMPNT, 0x100); + venc_write(sd, VENC_HSPLS, 0); + venc_write(sd, VENC_HINT, 0); + venc_write(sd, VENC_HSTART, 0); + venc_write(sd, VENC_HVALID, 0); + + venc_write(sd, VENC_VSPLS, 0); + venc_write(sd, VENC_VINT, 0); + venc_write(sd, VENC_VSTART, 0); + venc_write(sd, VENC_VVALID, 0); + + venc_write(sd, VENC_HSDLY, 0); + venc_write(sd, VENC_VSDLY, 0); + + venc_write(sd, VENC_YCCCTL, 0); + venc_write(sd, VENC_VSTARTA, 0); + + /* Set OSD clock and OSD Sync Adavance registers */ + venc_write(sd, VENC_OSDCLK0, 1); + venc_write(sd, VENC_OSDCLK1, 2); + } +} + +/* + * setting NTSC mode + */ +static int venc_set_ntsc(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n"); + + /* Setup clock at VPSS & VENC for SD */ + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ + venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); + /* Set REC656 Mode */ + venc_write(sd, VENC_YCCCTL, 0x1); + venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS); + + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); + venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_write(sd, VENC_DACTST, 0x0); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +/* + * setting PAL mode + */ +static int venc_set_pal(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + + v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); + + /* Setup clock at VPSS & VENC for SD */ + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ + venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); + /* Set REC656 Mode */ + venc_write(sd, VENC_YCCCTL, 0x1); + + venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT, + VENC_SYNCCTL_OVD); + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, + (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, + (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); + venc_modify(sd, VENC_VMOD, + (1 << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_write(sd, VENC_DACTST, 0x0); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +/* + * venc_set_480p59_94 + * + * This function configures the video encoder to EDTV(525p) component setting. + */ +static int venc_set_480p59_94(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); + + /* Setup clock at VPSS & VENC for SD */ + if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_480P59_94) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + venc_write(sd, VENC_OSDCLK0, 0); + venc_write(sd, VENC_OSDCLK1, 1); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, + VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, + VENC_VDPRO_DAUPS); + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); + venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << + VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); + + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +/* + * venc_set_625p + * + * This function configures the video encoder to HDTV(625p) component setting + */ +static int venc_set_576p50(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); + + /* Setup clock at VPSS & VENC for SD */ + if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_576P50) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + venc_write(sd, VENC_OSDCLK0, 0); + venc_write(sd, VENC_OSDCLK1, 1); + + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, + VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, + VENC_VDPRO_DAUPS); + + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); + venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + + venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << + VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_525_60) + return venc_set_ntsc(sd); + else if (norm & V4L2_STD_625_50) + return venc_set_pal(sd); + return -EINVAL; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + v4l2_dbg(debug, 1, sd, "venc_s_dv_preset\n"); + + if (dv_preset->preset == V4L2_DV_576P50) + return venc_set_576p50(sd); + else if (dv_preset->preset == V4L2_DV_480P59_94) + return venc_set_480p59_94(sd); + return -EINVAL; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int max_output, lcd_out_index, ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + max_output = 2 + venc->pdata->num_lcd_outputs; + lcd_out_index = 3; + + if (output >= max_output) + return -EINVAL; + + if (output < lcd_out_index) + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + return ret; +} + +static long venc_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + u32 val; + switch (cmd) { + case VENC_GET_FLD: + val = venc_read(sd, VENC_VSTAT); + *((int *)arg) = ((val & VENC_VSTAT_FIDST) == + VENC_VSTAT_FIDST); + break; + default: + v4l2_err(sd, "Wrong IOCTL cmd\n"); + break; + } + return 0; +} + +static const struct v4l2_subdev_core_ops venc_core_ops = { + .ioctl = venc_ioctl, +}; + +static const struct v4l2_subdev_video_ops venc_video_ops = { + .s_routing = venc_s_routing, + .s_std_output = venc_s_std_output, + .s_dv_preset = venc_s_dv_preset, +}; + +static const struct v4l2_subdev_ops venc_ops = { + .core = &venc_core_ops, + .video = &venc_video_ops, +}; + +static int venc_initialize(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + int ret = 0; + + /* Set default to output to composite and std to NTSC */ + venc->output = 0; + venc->std = V4L2_STD_525_60; + + ret = venc_s_routing(sd, 0, venc->output, 0); + if (ret < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + ret = venc_s_std_output(sd, venc->std); + if (ret < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + return ret; +} + +static int venc_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct venc_state **venc = data; + + if (strcmp(MODULE_NAME, pdev->name) == 0) + *venc = platform_get_drvdata(pdev); + return 0; +} + +struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, + const char *venc_name) +{ + struct venc_state *venc; + int err; + + err = bus_for_each_dev(&platform_bus_type, NULL, &venc, + venc_device_get); + if (venc == NULL) + return NULL; + + v4l2_subdev_init(&venc->sd, &venc_ops); + + strcpy(venc->sd.name, venc_name); + if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) { + v4l2_err(v4l2_dev, + "vpbe unable to register venc sub device\n"); + return NULL; + } + if (venc_initialize(&venc->sd)) { + v4l2_err(v4l2_dev, + "vpbe venc initialization failed\n"); + return NULL; + } + return &venc->sd; +} +EXPORT_SYMBOL(venc_sub_dev_init); + +static int venc_probe(struct platform_device *pdev) +{ + struct venc_state *venc; + struct resource *res; + int ret; + + venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL); + if (venc == NULL) + return -ENOMEM; + + venc->pdev = &pdev->dev; + venc->pdata = pdev->dev.platform_data; + if (NULL == venc->pdata) { + dev_err(venc->pdev, "Unable to get platform data for" + " VENC sub device"); + ret = -ENOENT; + goto free_mem; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(venc->pdev, + "Unable to get VENC register address map\n"); + ret = -ENODEV; + goto free_mem; + } + + if (!request_mem_region(res->start, resource_size(res), "venc")) { + dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n"); + ret = -ENODEV; + goto free_mem; + } + + venc->venc_base = ioremap_nocache(res->start, resource_size(res)); + if (!venc->venc_base) { + dev_err(venc->pdev, "Unable to map VENC IO space\n"); + ret = -ENODEV; + goto release_venc_mem_region; + } + + spin_lock_init(&venc->lock); + platform_set_drvdata(pdev, venc); + dev_notice(venc->pdev, "VENC sub device probe success\n"); + return 0; + +release_venc_mem_region: + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); +free_mem: + kfree(venc); + return ret; +} + +static int venc_remove(struct platform_device *pdev) +{ + struct venc_state *venc = platform_get_drvdata(pdev); + struct resource *res; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap((void *)venc->venc_base); + release_mem_region(res->start, resource_size(res)); + kfree(venc); + return 0; +} + +static struct platform_driver venc_driver = { + .probe = venc_probe, + .remove = venc_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int venc_init(void) +{ + if (platform_driver_register(&venc_driver)) { + printk(KERN_ERR "Unable to register venc driver\n"); + return -ENODEV; + } + return 0; +} + +static void venc_exit(void) +{ + platform_driver_unregister(&venc_driver); + return; +} + +module_init(venc_init); +module_exit(venc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VPBE VENC Driver"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/media/video/davinci/vpbe_venc_regs.h b/drivers/media/video/davinci/vpbe_venc_regs.h new file mode 100644 index 0000000..947cb15 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2006-2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2.. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_VENC_REGS_H +#define _VPBE_VENC_REGS_H + +/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */ +#define VENC_VMOD 0x00 +#define VENC_VIDCTL 0x04 +#define VENC_VDPRO 0x08 +#define VENC_SYNCCTL 0x0C +#define VENC_HSPLS 0x10 +#define VENC_VSPLS 0x14 +#define VENC_HINT 0x18 +#define VENC_HSTART 0x1C +#define VENC_HVALID 0x20 +#define VENC_VINT 0x24 +#define VENC_VSTART 0x28 +#define VENC_VVALID 0x2C +#define VENC_HSDLY 0x30 +#define VENC_VSDLY 0x34 +#define VENC_YCCCTL 0x38 +#define VENC_RGBCTL 0x3C +#define VENC_RGBCLP 0x40 +#define VENC_LINECTL 0x44 +#define VENC_CULLLINE 0x48 +#define VENC_LCDOUT 0x4C +#define VENC_BRTS 0x50 +#define VENC_BRTW 0x54 +#define VENC_ACCTL 0x58 +#define VENC_PWMP 0x5C +#define VENC_PWMW 0x60 +#define VENC_DCLKCTL 0x64 +#define VENC_DCLKPTN0 0x68 +#define VENC_DCLKPTN1 0x6C +#define VENC_DCLKPTN2 0x70 +#define VENC_DCLKPTN3 0x74 +#define VENC_DCLKPTN0A 0x78 +#define VENC_DCLKPTN1A 0x7C +#define VENC_DCLKPTN2A 0x80 +#define VENC_DCLKPTN3A 0x84 +#define VENC_DCLKHS 0x88 +#define VENC_DCLKHSA 0x8C +#define VENC_DCLKHR 0x90 +#define VENC_DCLKVS 0x94 +#define VENC_DCLKVR 0x98 +#define VENC_CAPCTL 0x9C +#define VENC_CAPDO 0xA0 +#define VENC_CAPDE 0xA4 +#define VENC_ATR0 0xA8 +#define VENC_ATR1 0xAC +#define VENC_ATR2 0xB0 +#define VENC_VSTAT 0xB8 +#define VENC_RAMADR 0xBC +#define VENC_RAMPORT 0xC0 +#define VENC_DACTST 0xC4 +#define VENC_YCOLVL 0xC8 +#define VENC_SCPROG 0xCC +#define VENC_CVBS 0xDC +#define VENC_CMPNT 0xE0 +#define VENC_ETMG0 0xE4 +#define VENC_ETMG1 0xE8 +#define VENC_ETMG2 0xEC +#define VENC_ETMG3 0xF0 +#define VENC_DACSEL 0xF4 +#define VENC_ARGBX0 0x100 +#define VENC_ARGBX1 0x104 +#define VENC_ARGBX2 0x108 +#define VENC_ARGBX3 0x10C +#define VENC_ARGBX4 0x110 +#define VENC_DRGBX0 0x114 +#define VENC_DRGBX1 0x118 +#define VENC_DRGBX2 0x11C +#define VENC_DRGBX3 0x120 +#define VENC_DRGBX4 0x124 +#define VENC_VSTARTA 0x128 +#define VENC_OSDCLK0 0x12C +#define VENC_OSDCLK1 0x130 +#define VENC_HVLDCL0 0x134 +#define VENC_HVLDCL1 0x138 +#define VENC_OSDHADV 0x13C +#define VENC_CLKCTL 0x140 +#define VENC_GAMCTL 0x144 +#define VENC_XHINTVL 0x174 + +/* bit definitions */ +#define VPBE_PCR_VENC_DIV (1 << 1) +#define VPBE_PCR_CLK_OFF (1 << 0) + +#define VENC_VMOD_VDMD_SHIFT 12 +#define VENC_VMOD_VDMD_YCBCR16 0 +#define VENC_VMOD_VDMD_YCBCR8 1 +#define VENC_VMOD_VDMD_RGB666 2 +#define VENC_VMOD_VDMD_RGB8 3 +#define VENC_VMOD_VDMD_EPSON 4 +#define VENC_VMOD_VDMD_CASIO 5 +#define VENC_VMOD_VDMD_UDISPQVGA 6 +#define VENC_VMOD_VDMD_STNLCD 7 +#define VENC_VMOD_VIE_SHIFT 1 +#define VENC_VMOD_VDMD (7 << 12) +#define VENC_VMOD_ITLCL (1 << 11) +#define VENC_VMOD_ITLC (1 << 10) +#define VENC_VMOD_NSIT (1 << 9) +#define VENC_VMOD_HDMD (1 << 8) +#define VENC_VMOD_TVTYP_SHIFT 6 +#define VENC_VMOD_TVTYP (3 << 6) +#define VENC_VMOD_SLAVE (1 << 5) +#define VENC_VMOD_VMD (1 << 4) +#define VENC_VMOD_BLNK (1 << 3) +#define VENC_VMOD_VIE (1 << 1) +#define VENC_VMOD_VENC (1 << 0) + +/* VMOD TVTYP options for HDMD=0 */ +#define SDTV_NTSC 0 +#define SDTV_PAL 1 +/* VMOD TVTYP options for HDMD=1 */ +#define HDTV_525P 0 +#define HDTV_625P 1 +#define HDTV_1080I 2 +#define HDTV_720P 3 + +#define VENC_VIDCTL_VCLKP (1 << 14) +#define VENC_VIDCTL_VCLKE_SHIFT 13 +#define VENC_VIDCTL_VCLKE (1 << 13) +#define VENC_VIDCTL_VCLKZ_SHIFT 12 +#define VENC_VIDCTL_VCLKZ (1 << 12) +#define VENC_VIDCTL_SYDIR_SHIFT 8 +#define VENC_VIDCTL_SYDIR (1 << 8) +#define VENC_VIDCTL_DOMD_SHIFT 4 +#define VENC_VIDCTL_DOMD (3 << 4) +#define VENC_VIDCTL_YCDIR_SHIFT 0 +#define VENC_VIDCTL_YCDIR (1 << 0) + +#define VENC_VDPRO_ATYCC_SHIFT 5 +#define VENC_VDPRO_ATYCC (1 << 5) +#define VENC_VDPRO_ATCOM_SHIFT 4 +#define VENC_VDPRO_ATCOM (1 << 4) +#define VENC_VDPRO_DAFRQ (1 << 3) +#define VENC_VDPRO_DAUPS (1 << 2) +#define VENC_VDPRO_CUPS (1 << 1) +#define VENC_VDPRO_YUPS (1 << 0) + +#define VENC_SYNCCTL_VPL_SHIFT 3 +#define VENC_SYNCCTL_VPL (1 << 3) +#define VENC_SYNCCTL_HPL_SHIFT 2 +#define VENC_SYNCCTL_HPL (1 << 2) +#define VENC_SYNCCTL_SYEV_SHIFT 1 +#define VENC_SYNCCTL_SYEV (1 << 1) +#define VENC_SYNCCTL_SYEH_SHIFT 0 +#define VENC_SYNCCTL_SYEH (1 << 0) +#define VENC_SYNCCTL_OVD_SHIFT 14 +#define VENC_SYNCCTL_OVD (1 << 14) + +#define VENC_DCLKCTL_DCKEC_SHIFT 11 +#define VENC_DCLKCTL_DCKEC (1 << 11) +#define VENC_DCLKCTL_DCKPW_SHIFT 0 +#define VENC_DCLKCTL_DCKPW (0x3f << 0) + +#define VENC_VSTAT_FIDST (1 << 4) + +#define VENC_CMPNT_MRGB_SHIFT 14 +#define VENC_CMPNT_MRGB (1 << 14) + +#endif /* _VPBE_VENC_REGS_H */ diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h new file mode 100644 index 0000000..d081b5e --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_VENC_H +#define _VPBE_VENC_H + +#include + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME BIT(0) +#define VENC_FIRST_FIELD BIT(1) +#define VENC_SECOND_FIELD BIT(2) + +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + unsigned int mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + +enum venc_ioctls { + VENC_GET_FLD = 1, +}; + +/* exported functions */ +struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, + const char *venc_name); +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:41:57 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:11:57 +0530 Subject: [PATCH v16 05/13] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1301737317-4201-1-git-send-email-manjunath.hadli@ti.com> This patch adds the build infra-structure for Davinci VPBE dislay driver. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/Kconfig | 22 ++++++++++++++++++++++ drivers/media/video/davinci/Makefile | 2 ++ 2 files changed, 24 insertions(+), 0 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..a7f11e7 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,25 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. + +config VIDEO_DM644X_VPBE + tristate "DM644X VPBE HW module" + select VIDEO_VPSS_SYSTEM + select VIDEOBUF_DMA_CONTIG + help + Enables VPBE modules used for display on a DM644x + SoC. + + To compile this driver as a module, choose M here: the + module will be called vpbe. + + +config VIDEO_VPBE_DISPLAY + tristate "VPBE V4L2 Display driver" + select VIDEO_DM644X_VPBE + default y + help + Enables VPBE V4L2 Display driver on a DMXXX device + + To compile this driver as a module, choose M here: the + module will be called vpbe_display. diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile index a379557..ae7dafb 100644 --- a/drivers/media/video/davinci/Makefile +++ b/drivers/media/video/davinci/Makefile @@ -16,3 +16,5 @@ obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o obj-$(CONFIG_VIDEO_ISIF) += isif.o +obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o +obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:42:14 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:12:14 +0530 Subject: [PATCH v16 06/13] davinci vpbe: Readme text for Dm6446 vpbe Message-ID: <1301737334-4238-1-git-send-email-manjunath.hadli@ti.com> Please refer to this file for detailed documentation of davinci vpbe v4l2 driver. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- Documentation/video4linux/README.davinci-vpbe | 93 +++++++++++++++++++++++++ 1 files changed, 93 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/README.davinci-vpbe diff --git a/Documentation/video4linux/README.davinci-vpbe b/Documentation/video4linux/README.davinci-vpbe new file mode 100644 index 0000000..7a460b0 --- /dev/null +++ b/Documentation/video4linux/README.davinci-vpbe @@ -0,0 +1,93 @@ + + VPBE V4L2 driver design + ====================================================================== + + File partitioning + ----------------- + V4L2 display device driver + drivers/media/video/davinci/vpbe_display.c + drivers/media/video/davinci/vpbe_display.h + + VPBE display controller + drivers/media/video/davinci/vpbe.c + drivers/media/video/davinci/vpbe.h + + VPBE venc sub device driver + drivers/media/video/davinci/vpbe_venc.c + drivers/media/video/davinci/vpbe_venc.h + drivers/media/video/davinci/vpbe_venc_regs.h + + VPBE osd driver + drivers/media/video/davinci/vpbe_osd.c + drivers/media/video/davinci/vpbe_osd.h + drivers/media/video/davinci/vpbe_osd_regs.h + + Functional partitioning + ----------------------- + + Consists of the following (in the same order as the list under file + partitioning):- + + 1. V4L2 display driver + Implements creation of video2 and video3 device nodes and + provides v4l2 device interface to manage VID0 and VID1 layers. + + 2. Display controller + Loads up VENC, OSD and external encoders such as ths8200. It provides + a set of API calls to V4L2 drivers to set the output/standards + in the VENC or external sub devices. It also provides + a device object to access the services from OSD subdevice + using sub device ops. The connection of external encoders to VENC LCD + controller port is done at init time based on default output and standard + selection or at run time when application change the output through + V4L2 IOCTLs. + + When connected to an external encoder, vpbe controller is also responsible + for setting up the interface between VENC and external encoders based on + board specific settings (specified in board-xxx-evm.c). This allows + interfacing external encoders such as ths8200. The setup_if_config() + is implemented for this as well as configure_venc() (part of the next patch) + API to set timings in VENC for a specific display resolution. As of this + patch series, the interconnection and enabling and setting of the external + encoders is not present, and would be a part of the next patch series. + + 3. VENC subdevice module + Responsible for setting outputs provided through internal DACs and also + setting timings at LCD controller port when external encoders are connected + at the port or LCD panel timings required. When external encoder/LCD panel + is connected, the timings for a specific standard/preset is retrieved from + the board specific table and the values are used to set the timings in + venc using non-standard timing mode. + + Support LCD Panel displays using the VENC. For example to support a Logic + PD display, it requires setting up the LCD controller port with a set of + timings for the resolution supported and setting the dot clock. So we could + add the available outputs as a board specific entry (i.e add the "LogicPD" + output name to board-xxx-evm.c). A table of timings for various LCDs + supported can be maintained in the board specific setup file to support + various LCD displays.As of this patch a basic driver is present, and this + support for external encoders and displays forms a part of the next + patch series. + + 4. OSD module + OSD module implements all OSD layer management and hardware specific + features. The VPBE module interacts with the OSD for enabling and + disabling appropriate features of the OSD. + + Current status:- + + A fully functional working version of the V4L2 driver is available. This + driver has been tested with NTSC and PAL standards and buffer streaming. + + Following are TBDs. + + vpbe display controller + - Add support for external encoders. + - add support for selecting external encoder as default at probe time. + + vpbe venc sub device + - add timings for supporting ths8200 + - add support for LogicPD LCD. + + FB drivers + - Add support for fbdev drivers.- Ready and part of subsequent patches. -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:43:00 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:13:00 +0530 Subject: [PATCH v18 07/13] davinci: move DM64XX_VDD3P3V_PWDN to devices.c Message-ID: <1301737380-4288-1-git-send-email-manjunath.hadli@ti.com> Move the definition of DM64XX_VDD3P3V_PWDN from hardware.h to devices.c since it is used only there. Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori --- arch/arm/mach-davinci/devices.c | 1 + arch/arm/mach-davinci/include/mach/hardware.h | 3 --- 2 files changed, 1 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c index 22ebc64..4e1b663 100644 --- a/arch/arm/mach-davinci/devices.c +++ b/arch/arm/mach-davinci/devices.c @@ -182,6 +182,7 @@ static struct platform_device davinci_mmcsd1_device = { .resource = mmcsd1_resources, }; +#define DM64XX_VDD3P3V_PWDN 0x48 void __init davinci_setup_mmc(int module, struct davinci_mmc_config *config) { diff --git a/arch/arm/mach-davinci/include/mach/hardware.h b/arch/arm/mach-davinci/include/mach/hardware.h index c45ba1f..414e0b9 100644 --- a/arch/arm/mach-davinci/include/mach/hardware.h +++ b/arch/arm/mach-davinci/include/mach/hardware.h @@ -21,9 +21,6 @@ */ #define DAVINCI_SYSTEM_MODULE_BASE 0x01C40000 -/* System control register offsets */ -#define DM64XX_VDD3P3V_PWDN 0x48 - /* * I/O mapping */ -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:43:17 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:13:17 +0530 Subject: [PATCH v18 08/13] davinci: eliminate use of IO_ADDRESS() on sysmod Message-ID: <1301737397-4327-1-git-send-email-manjunath.hadli@ti.com> Current devices.c file has a number of instances where IO_ADDRESS() is used for system module register access. Eliminate this in favor of a ioremap() based access. Consequent to this, a new global pointer davinci_sysmodbase has been introduced which gets initialized during the initialization of each relevant SoC Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori --- arch/arm/mach-davinci/devices.c | 24 +++++++++++++++--------- arch/arm/mach-davinci/dm355.c | 1 + arch/arm/mach-davinci/dm365.c | 1 + arch/arm/mach-davinci/dm644x.c | 1 + arch/arm/mach-davinci/dm646x.c | 1 + arch/arm/mach-davinci/include/mach/hardware.h | 6 ++++++ 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c index 4e1b663..529b440 100644 --- a/arch/arm/mach-davinci/devices.c +++ b/arch/arm/mach-davinci/devices.c @@ -33,6 +33,14 @@ #define DM365_MMCSD0_BASE 0x01D11000 #define DM365_MMCSD1_BASE 0x01D00000 +void __iomem *davinci_sysmodbase; + +void davinci_map_sysmod(void) +{ + davinci_sysmodbase = ioremap_nocache(DAVINCI_SYSTEM_MODULE_BASE, 0x800); + WARN_ON(!davinci_sysmodbase); +} + static struct resource i2c_resources[] = { { .start = DAVINCI_I2C_BASE, @@ -210,12 +218,12 @@ void __init davinci_setup_mmc(int module, struct davinci_mmc_config *config) davinci_cfg_reg(DM355_SD1_DATA2); davinci_cfg_reg(DM355_SD1_DATA3); } else if (cpu_is_davinci_dm365()) { - void __iomem *pupdctl1 = - IO_ADDRESS(DAVINCI_SYSTEM_MODULE_BASE + 0x7c); - /* Configure pull down control */ - __raw_writel((__raw_readl(pupdctl1) & ~0xfc0), - pupdctl1); + void __iomem *pupdctl1 = DAVINCI_SYSMODULE_VIRT(0x7c); + unsigned v; + + v = __raw_readl(pupdctl1); + __raw_writel(v & ~0xfc0, pupdctl1); mmcsd1_resources[0].start = DM365_MMCSD1_BASE; mmcsd1_resources[0].end = DM365_MMCSD1_BASE + @@ -244,11 +252,9 @@ void __init davinci_setup_mmc(int module, struct davinci_mmc_config *config) mmcsd0_resources[2].start = IRQ_DM365_SDIOINT0; } else if (cpu_is_davinci_dm644x()) { /* REVISIT: should this be in board-init code? */ - void __iomem *base = - IO_ADDRESS(DAVINCI_SYSTEM_MODULE_BASE); - /* Power-on 3.3V IO cells */ - __raw_writel(0, base + DM64XX_VDD3P3V_PWDN); + __raw_writel(0, + DAVINCI_SYSMODULE_VIRT(DM64XX_VDD3P3V_PWDN)); /*Set up the pull regiter for MMC */ davinci_cfg_reg(DM644X_MSTK); } diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index a5f8a80..1baab94 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -874,6 +874,7 @@ void __init dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata) void __init dm355_init(void) { davinci_common_init(&davinci_soc_info_dm355); + davinci_map_sysmod(); } static int __init dm355_init_devices(void) diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 02d2cc3..a788980 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -1127,6 +1127,7 @@ void __init dm365_init_rtc(void) void __init dm365_init(void) { davinci_common_init(&davinci_soc_info_dm365); + davinci_map_sysmod(); } static struct resource dm365_vpss_resources[] = { diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 9a2376b..77dea11 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -779,6 +779,7 @@ void __init dm644x_init_asp(struct snd_platform_data *pdata) void __init dm644x_init(void) { davinci_common_init(&davinci_soc_info_dm644x); + davinci_map_sysmod(); } static int __init dm644x_init_devices(void) diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..ce93b83 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -903,6 +903,7 @@ void __init dm646x_init(void) { dm646x_board_setup_refclk(&ref_clk); davinci_common_init(&davinci_soc_info_dm646x); + davinci_map_sysmod(); } static int __init dm646x_init_devices(void) diff --git a/arch/arm/mach-davinci/include/mach/hardware.h b/arch/arm/mach-davinci/include/mach/hardware.h index 414e0b9..2a6b560 100644 --- a/arch/arm/mach-davinci/include/mach/hardware.h +++ b/arch/arm/mach-davinci/include/mach/hardware.h @@ -21,6 +21,12 @@ */ #define DAVINCI_SYSTEM_MODULE_BASE 0x01C40000 +#ifndef __ASSEMBLER__ +extern void __iomem *davinci_sysmodbase; +#define DAVINCI_SYSMODULE_VIRT(x) (davinci_sysmodbase + (x)) +void davinci_map_sysmod(void); +#endif + /* * I/O mapping */ -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:43:31 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:13:31 +0530 Subject: [PATCH v18 09/13] davinci: dm644x: Replace register base value with a defined macro Message-ID: <1301737411-4417-1-git-send-email-manjunath.hadli@ti.com> Replace hard coded value of vpss register base to a define macro DM644X_VPSS_REG_BASE for proper readability Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori --- arch/arm/mach-davinci/dm644x.c | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 77dea11..6edb5d1 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -586,13 +586,15 @@ static struct platform_device dm644x_asp_device = { .resource = dm644x_asp_resources, }; +#define DM644X_VPSS_REG_BASE 0x01c73400 + static struct resource dm644x_vpss_resources[] = { { /* VPSS Base address */ .name = "vpss", - .start = 0x01c73400, - .end = 0x01c73400 + 0xff, - .flags = IORESOURCE_MEM, + .start = DM644X_VPSS_REG_BASE, + .end = DM644X_VPSS_REG_BASE + 0xff, + .flags = IORESOURCE_MEM, }, }; -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:43:46 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:13:46 +0530 Subject: [PATCH v18 10/13] davinci: dm644x: change vpfe capture structure variables for consistency Message-ID: <1301737426-4459-1-git-send-email-manjunath.hadli@ti.com> Add SoC and board prefixes to variable names so that it is consistent with the rest of the file. Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori --- arch/arm/mach-davinci/board-dm644x-evm.c | 24 ++++++++++++------------ arch/arm/mach-davinci/dm644x.c | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 0ca90b8..6919f28 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -188,7 +188,7 @@ static struct platform_device davinci_fb_device = { .num_resources = 0, }; -static struct tvp514x_platform_data tvp5146_pdata = { +static struct tvp514x_platform_data dm644xevm_tvp5146_pdata = { .clk_polarity = 0, .hs_polarity = 1, .vs_polarity = 1 @@ -196,7 +196,7 @@ static struct tvp514x_platform_data tvp5146_pdata = { #define TVP514X_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) /* Inputs available at the TVP5146 */ -static struct v4l2_input tvp5146_inputs[] = { +static struct v4l2_input dm644xevm_tvp5146_inputs[] = { { .index = 0, .name = "Composite", @@ -216,7 +216,7 @@ static struct v4l2_input tvp5146_inputs[] = { * ouput that goes to vpfe. There is a one to one correspondence * with tvp5146_inputs */ -static struct vpfe_route tvp5146_routes[] = { +static struct vpfe_route dm644xevm_tvp5146_routes[] = { { .input = INPUT_CVBS_VI2B, .output = OUTPUT_10BIT_422_EMBEDDED_SYNC, @@ -227,13 +227,13 @@ static struct vpfe_route tvp5146_routes[] = { }, }; -static struct vpfe_subdev_info vpfe_sub_devs[] = { +static struct vpfe_subdev_info dm644xevm_vpfe_sub_devs[] = { { .name = "tvp5146", .grp_id = 0, - .num_inputs = ARRAY_SIZE(tvp5146_inputs), - .inputs = tvp5146_inputs, - .routes = tvp5146_routes, + .num_inputs = ARRAY_SIZE(dm644xevm_tvp5146_inputs), + .inputs = dm644xevm_tvp5146_inputs, + .routes = dm644xevm_tvp5146_routes, .can_route = 1, .ccdc_if_params = { .if_type = VPFE_BT656, @@ -242,15 +242,15 @@ static struct vpfe_subdev_info vpfe_sub_devs[] = { }, .board_info = { I2C_BOARD_INFO("tvp5146", 0x5d), - .platform_data = &tvp5146_pdata, + .platform_data = &dm644xevm_tvp5146_pdata, }, }, }; -static struct vpfe_config vpfe_cfg = { - .num_subdevs = ARRAY_SIZE(vpfe_sub_devs), +static struct vpfe_config dm644xevm_capture_cfg = { + .num_subdevs = ARRAY_SIZE(dm644xevm_vpfe_sub_devs), .i2c_adapter_id = 1, - .sub_devs = vpfe_sub_devs, + .sub_devs = dm644xevm_vpfe_sub_devs, .card_name = "DM6446 EVM", .ccdc = "DM6446 CCDC", }; @@ -629,7 +629,7 @@ static void __init davinci_evm_map_io(void) { /* setup input configuration for VPFE input devices */ - dm644x_set_vpfe_config(&vpfe_cfg); + dm644x_set_vpfe_config(&dm644xevm_capture_cfg); dm644x_init(); } diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 6edb5d1..e258c54 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -606,7 +606,7 @@ static struct platform_device dm644x_vpss_device = { .resource = dm644x_vpss_resources, }; -static struct resource vpfe_resources[] = { +static struct resource dm644x_vpfe_resources[] = { { .start = IRQ_VDINT0, .end = IRQ_VDINT0, @@ -640,11 +640,11 @@ static struct platform_device dm644x_ccdc_dev = { }, }; -static struct platform_device vpfe_capture_dev = { +static struct platform_device dm644x_vpfe_dev = { .name = CAPTURE_DRV_NAME, .id = -1, - .num_resources = ARRAY_SIZE(vpfe_resources), - .resource = vpfe_resources, + .num_resources = ARRAY_SIZE(dm644x_vpfe_resources), + .resource = dm644x_vpfe_resources, .dev = { .dma_mask = &vpfe_capture_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), @@ -653,7 +653,7 @@ static struct platform_device vpfe_capture_dev = { void dm644x_set_vpfe_config(struct vpfe_config *cfg) { - vpfe_capture_dev.dev.platform_data = cfg; + dm644x_vpfe_dev.dev.platform_data = cfg; } /*----------------------------------------------------------------------*/ @@ -801,7 +801,7 @@ static int __init dm644x_init_devices(void) platform_device_register(&dm644x_vpss_device); platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&vpfe_capture_dev); + platform_device_register(&dm644x_vpfe_dev); return 0; } -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:43:59 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:13:59 +0530 Subject: [PATCH v18 11/13] davinci: dm644x: move vpfe init from soc to board specific files Message-ID: <1301737439-4498-1-git-send-email-manjunath.hadli@ti.com> Move all vpfe platform device registrations to the board specific file like the rest of the devices, and have all of them together. This would remove the restriction of inclusion and registration of vpfe platform devices for non-vpfe boards. Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori --- arch/arm/mach-davinci/board-dm644x-evm.c | 3 +- arch/arm/mach-davinci/dm644x.c | 29 +++++++++++++++----------- arch/arm/mach-davinci/include/mach/dm644x.h | 2 +- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 6919f28..d1542b1 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -628,8 +628,6 @@ static struct davinci_uart_config uart_config __initdata = { static void __init davinci_evm_map_io(void) { - /* setup input configuration for VPFE input devices */ - dm644x_set_vpfe_config(&dm644xevm_capture_cfg); dm644x_init(); } @@ -701,6 +699,7 @@ static __init void davinci_evm_init(void) evm_init_i2c(); davinci_setup_mmc(0, &dm6446evm_mmc_config); + dm644x_init_video(&dm644xevm_capture_cfg); davinci_serial_init(&uart_config); dm644x_init_asp(&dm644x_evm_snd_data); diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index e258c54..36d4d72 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -651,11 +651,6 @@ static struct platform_device dm644x_vpfe_dev = { }, }; -void dm644x_set_vpfe_config(struct vpfe_config *cfg) -{ - dm644x_vpfe_dev.dev.platform_data = cfg; -} - /*----------------------------------------------------------------------*/ static struct map_desc dm644x_io_desc[] = { @@ -784,14 +779,28 @@ void __init dm644x_init(void) davinci_map_sysmod(); } +static struct platform_device *dm644x_video_devices[] __initdata = { + &dm644x_vpss_device, + &dm644x_ccdc_dev, + &dm644x_vpfe_dev, +}; + +int __init dm644x_init_video(struct vpfe_config *vpfe_cfg) +{ + dm644x_vpfe_dev.dev.platform_data = vpfe_cfg; + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); + platform_add_devices(dm644x_video_devices, + ARRAY_SIZE(dm644x_video_devices)); + return 0; +} + static int __init dm644x_init_devices(void) { if (!cpu_is_davinci_dm644x()) return 0; - /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); platform_device_register(&dm644x_mdio_device); @@ -799,10 +808,6 @@ static int __init dm644x_init_devices(void) clk_add_alias(NULL, dev_name(&dm644x_mdio_device.dev), NULL, &dm644x_emac_device.dev); - platform_device_register(&dm644x_vpss_device); - platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&dm644x_vpfe_dev); - return 0; } postcore_initcall(dm644x_init_devices); diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 5a1b26d..29a9e24 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -42,6 +42,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); -void dm644x_set_vpfe_config(struct vpfe_config *cfg); +int __init dm644x_init_video(struct vpfe_config *); #endif /* __ASM_ARCH_DM644X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:44:10 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:14:10 +0530 Subject: [PATCH v18 12/13] davinci: dm644x: add support for v4l2 video display Message-ID: <1301737450-4541-1-git-send-email-manjunath.hadli@ti.com> Create platform devices for various video modules like venc,osd, vpbe and v4l2 driver for dm644x. Change the dm644x_init_video to make room for display config, and register the vpfe or vpbe devices based on the config pointer validity to make sure boards without vpfe or vpbe can be built with minimal changes. Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori --- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/dm644x.c | 163 ++++++++++++++++++++++++--- arch/arm/mach-davinci/include/mach/dm644x.h | 6 +- 3 files changed, 153 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index d1542b1..afa88ac 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -699,7 +699,7 @@ static __init void davinci_evm_init(void) evm_init_i2c(); davinci_setup_mmc(0, &dm6446evm_mmc_config); - dm644x_init_video(&dm644xevm_capture_cfg); + dm644x_init_video(&dm644xevm_capture_cfg, NULL); davinci_serial_init(&uart_config); dm644x_init_asp(&dm644x_evm_snd_data); diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 36d4d72..91e979d 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -619,7 +619,7 @@ static struct resource dm644x_vpfe_resources[] = { }, }; -static u64 vpfe_capture_dma_mask = DMA_BIT_MASK(32); +static u64 dm644x_video_dma_mask = DMA_BIT_MASK(32); static struct resource dm644x_ccdc_resource[] = { /* CCDC Base address */ { @@ -635,7 +635,7 @@ static struct platform_device dm644x_ccdc_dev = { .num_resources = ARRAY_SIZE(dm644x_ccdc_resource), .resource = dm644x_ccdc_resource, .dev = { - .dma_mask = &vpfe_capture_dma_mask, + .dma_mask = &dm644x_video_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), }, }; @@ -646,7 +646,127 @@ static struct platform_device dm644x_vpfe_dev = { .num_resources = ARRAY_SIZE(dm644x_vpfe_resources), .resource = dm644x_vpfe_resources, .dev = { - .dma_mask = &vpfe_capture_dma_mask, + .dma_mask = &dm644x_video_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define DM644X_OSD_REG_BASE 0x01C72600 + +static struct resource dm644x_osd_resources[] = { + { + .start = DM644X_OSD_REG_BASE, + .end = DM644X_OSD_REG_BASE + 0x1ff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct osd_platform_data dm644x_osd_data = { + .vpbe_type = VPBE_VERSION_1, +}; + +static struct platform_device dm644x_osd_dev = { + .name = VPBE_OSD_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_osd_resources), + .resource = dm644x_osd_resources, + .dev = { + .dma_mask = &dm644x_video_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &dm644x_osd_data, + }, +}; + +#define DM644X_VENC_REG_BASE 0x01C72400 + +static struct resource dm644x_venc_resources[] = { + { + .start = DM644X_VENC_REG_BASE, + .end = DM644X_VENC_REG_BASE + 0x17f, + .flags = IORESOURCE_MEM, + }, +}; + +static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, + unsigned int mode) +{ + int ret = 0; + void __iomem *vpss_clkctl_reg; + + vpss_clkctl_reg = DAVINCI_SYSMODULE_VIRT(0x44); + + switch (type) { + case VPBE_ENC_STD: + writel(0x18, vpss_clkctl_reg); + break; + case VPBE_ENC_DV_PRESET: + switch ((unsigned int)mode) { + case V4L2_DV_480P59_94: + case V4L2_DV_576P50: + writel(0x19, vpss_clkctl_reg); + break; + case V4L2_DV_720P60: + case V4L2_DV_1080I60: + case V4L2_DV_1080P30: + /* + * For HD, use external clock source since + * HD requires higher clock rate + */ + writel(0xa, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + break; + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static struct resource dm644x_v4l2_disp_resources[] = { + { + .start = IRQ_VENCINT, + .end = IRQ_VENCINT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device dm644x_vpbe_display = { + .name = "vpbe-v4l2", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), + .resource = dm644x_v4l2_disp_resources, + .dev = { + .dma_mask = &dm644x_video_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct venc_platform_data dm644x_venc_pdata = { + .venc_type = VPBE_VERSION_1, + .setup_clock = dm644x_venc_setup_clock, +}; + +static struct platform_device dm644x_venc_dev = { + .name = VPBE_VENC_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_venc_resources), + .resource = dm644x_venc_resources, + .dev = { + .dma_mask = &dm644x_video_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &dm644x_venc_pdata, + }, +}; + +static struct platform_device dm644x_vpbe_dev = { + .name = "vpbe_controller", + .id = -1, + .dev = { + .dma_mask = &dm644x_video_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), }, }; @@ -779,20 +899,31 @@ void __init dm644x_init(void) davinci_map_sysmod(); } -static struct platform_device *dm644x_video_devices[] __initdata = { - &dm644x_vpss_device, - &dm644x_ccdc_dev, - &dm644x_vpfe_dev, -}; - -int __init dm644x_init_video(struct vpfe_config *vpfe_cfg) +int __init dm644x_init_video(struct vpfe_config *vpfe_cfg, + struct vpbe_config *vpbe_cfg) { - dm644x_vpfe_dev.dev.platform_data = vpfe_cfg; - /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); - platform_add_devices(dm644x_video_devices, - ARRAY_SIZE(dm644x_video_devices)); + if (vpfe_cfg || vpbe_cfg) + platform_device_register(&dm644x_vpss_device); + + if (vpfe_cfg) { + dm644x_vpfe_dev.dev.platform_data = vpfe_cfg; + platform_device_register(&dm644x_ccdc_dev); + platform_device_register(&dm644x_vpfe_dev); + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, + "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, + "vpss_slave", NULL); + } + + if (vpbe_cfg) { + dm644x_vpbe_dev.dev.platform_data = vpbe_cfg; + platform_device_register(&dm644x_osd_dev); + platform_device_register(&dm644x_venc_dev); + platform_device_register(&dm644x_vpbe_dev); + platform_device_register(&dm644x_vpbe_display); + } + return 0; } diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 29a9e24..a405702 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -26,6 +26,10 @@ #include #include #include +#include +#include +#include +#include #define DM644X_EMAC_BASE (0x01C80000) #define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) @@ -42,6 +46,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); -int __init dm644x_init_video(struct vpfe_config *); +int __init dm644x_init_video(struct vpfe_config *, struct vpbe_config *); #endif /* __ASM_ARCH_DM644X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Sat Apr 2 03:44:23 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 2 Apr 2011 15:14:23 +0530 Subject: [PATCH v18 13/13] davinci: dm644x EVM: add support for VPBE display Message-ID: <1301737463-4589-1-git-send-email-manjunath.hadli@ti.com> This patch adds support for V4L2 video display to DM6446 EVM. Support for SD and ED modes is provided, along with Composite and Component outputs.Also added vpbe_config as a parameter for dm644x_init_video to allow for registration of vpbe platform devices. Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori --- arch/arm/mach-davinci/board-dm644x-evm.c | 108 +++++++++++++++++++++++++++++- 1 files changed, 107 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index afa88ac..4a642dd 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -616,6 +616,112 @@ static void __init evm_init_i2c(void) i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); } +#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) + +/* venc standard timings */ +static struct vpbe_enc_mode_info dm644xevm_enc_std_timing[] = { + { + .name = "ntsc", + .timings_type = VPBE_ENC_STD, + .timings = {V4L2_STD_525_60}, + .interlaced = 1, + .xres = 720, + .yres = 480, + .aspect = {11, 10}, + .fps = {30000, 1001}, + .left_margin = 0x79, + .upper_margin = 0x10, + }, + { + .name = "pal", + .timings_type = VPBE_ENC_STD, + .timings = {V4L2_STD_625_50}, + .interlaced = 1, + .xres = 720, + .yres = 576, + .aspect = {54, 59}, + .fps = {25, 1}, + .left_margin = 0x7E, + .upper_margin = 0x16, + }, +}; + +/* venc dv preset timings */ +static struct vpbe_enc_mode_info dm644xevm_enc_preset_timing[] = { + { + .name = "480p59_94", + .timings_type = VPBE_ENC_DV_PRESET, + .timings = {V4L2_DV_480P59_94}, + .interlaced = 0, + .xres = 720, + .yres = 480, + .aspect = {1, 1}, + .fps = {5994, 100}, + .left_margin = 0x80, + .upper_margin = 0x20, + }, + { + .name = "576p50", + .timings_type = VPBE_ENC_DV_PRESET, + .timings = {V4L2_DV_576P50}, + .interlaced = 0, + .xres = 720, + .yres = 576, + .aspect = {1, 1}, + .fps = {50, 1}, + .left_margin = 0x7E, + .upper_margin = 0x30, + }, +}; + +/* + * The outputs available from VPBE + encoders. Keep the order same + * as that of encoders. First those from venc followed by that from + * encoders. Index in the output refers to index on a particular encoder. + * Driver uses this index to pass it to encoder when it supports more than + * one output. Application uses index of the array to set an output. + */ +static struct vpbe_output dm644xevm_vpbe_outputs[] = { + { + .output = { + .index = 0, + .name = "Composite", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .std = VENC_STD_ALL, + .capabilities = V4L2_OUT_CAP_STD, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "ntsc", + .num_modes = ARRAY_SIZE(dm644xevm_enc_std_timing), + .modes = dm644xevm_enc_std_timing, + }, + { + .output = { + .index = 1, + .name = "Component", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .capabilities = V4L2_OUT_CAP_PRESETS, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "480p59_94", + .num_modes = ARRAY_SIZE(dm644xevm_enc_preset_timing), + .modes = dm644xevm_enc_preset_timing, + }, +}; + +static struct vpbe_config dm644xevm_display_cfg = { + .module_name = "dm644x-vpbe-display", + .i2c_adapter_id = 1, + .osd = { + .module_name = VPBE_OSD_SUBDEV_NAME, + }, + .venc = { + .module_name = VPBE_VENC_SUBDEV_NAME, + }, + .num_outputs = ARRAY_SIZE(dm644xevm_vpbe_outputs), + .outputs = dm644xevm_vpbe_outputs, +}; + static struct platform_device *davinci_evm_devices[] __initdata = { &davinci_fb_device, &rtc_dev, @@ -699,7 +805,7 @@ static __init void davinci_evm_init(void) evm_init_i2c(); davinci_setup_mmc(0, &dm6446evm_mmc_config); - dm644x_init_video(&dm644xevm_capture_cfg, NULL); + dm644x_init_video(&dm644xevm_capture_cfg, &dm644xevm_display_cfg); davinci_serial_init(&uart_config); dm644x_init_asp(&dm644x_evm_snd_data); -- 1.6.2.4 From michael.williamson at criticallink.com Sat Apr 2 06:00:25 2011 From: michael.williamson at criticallink.com (Michael Williamson) Date: Sat, 02 Apr 2011 08:00:25 -0400 Subject: RFC: More NR_IRQS? Message-ID: <4D970FD9.1080304@criticallink.com> Hi, I'm working with a couple of gpio expanders (for example, the PCA953X) that have the capability to plug into the gpiolib kernel infrastructure and include support for interrupt notification. For the interrupts, they appear to function similarly to the davinci gpio.c driver. I.E., they provide for "soft" IRQs in order to provide one-to-one mapping to support gpio_to_irq(). The problem is that the NR_IRQS in mach/irqs.h is fixed to only accommodate the SOC interrupt handler and it's local GPIOs. There are no extra entries to support expanders. I was going to propose an integer Kconfig option (e.g., CONFIG_DAVINCI_EXTRA_IRQS, default 0) that would be added to NR_IRQS to support this. Is there a better way? Or an alternate solutione? Thanks. -Mike From sshtylyov at mvista.com Mon Apr 4 13:35:42 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 04 Apr 2011 22:35:42 +0400 Subject: [PATCH v4 1/4] davinci: da8xx: add spi resources and registration routine In-Reply-To: <1298486651-18882-2-git-send-email-michael.williamson@criticallink.com> References: <1298486651-18882-1-git-send-email-michael.williamson@criticallink.com> <1298486651-18882-2-git-send-email-michael.williamson@criticallink.com> Message-ID: <4D9A0F7E.1050606@mvista.com> Michael Williamson wrote: > Add IO resource structures, platform data, and a registration > routine in order to support spi device on DA850/OMAP-L138/AM18x > and DA830/OMAP-L137/AM17x platforms. > Signed-off-by: Michael Williamson > --- > arch/arm/mach-davinci/devices-da8xx.c | 104 ++++++++++++++++++++++++++++ > arch/arm/mach-davinci/include/mach/da8xx.h | 4 + > 2 files changed, 108 insertions(+), 0 deletions(-) > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > index 119d46e..625d4b6 100644 > --- a/arch/arm/mach-davinci/devices-da8xx.c > +++ b/arch/arm/mach-davinci/devices-da8xx.c > @@ -38,14 +38,20 @@ > #define DA8XX_EMAC_MDIO_BASE 0x01e24000 > #define DA8XX_GPIO_BASE 0x01e26000 > #define DA8XX_I2C1_BASE 0x01e28000 > +#define DA8XX_SPI0_BASE 0x01c41000 > +#define DA8XX_SPI1_BASE 0x01f0e000 Unfortunately, DA830 and DA850 have SPI#1 mapped at different addresses, so this will work only on DA850. I'll send a patch... WBR, Sergei From subhasish at mistralsolutions.com Tue Apr 5 01:40:46 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 5 Apr 2011 12:10:46 +0530 Subject: [PATCH v3 1/7] mfd: add pruss mfd driver. In-Reply-To: <201103301259.28483.arnd@arndb.de> References: <1299592667-21367-1-git-send-email-subhasish@mistralsolutions.com> <201103111628.57549.arnd@arndb.de> <17BF3847C06240EC921FB684D3120DEC@subhasishg> <201103301259.28483.arnd@arndb.de> Message-ID: <34890DB1679E41119ADB156E2CFEC4C9@subhasishg> > On Wednesday 30 March 2011, Subhasish Ghosh wrote: >> >> >> +s32 pruss_disable(struct device *dev, u8 pruss_num) >> >> +{ >> >> + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); >> >> + struct da8xx_prusscore_regs *h_pruss; >> >> + struct pruss_map *pruss_mmap = (struct pruss_map *)pruss->ioaddr; >> >> + u32 temp_reg; >> >> + u32 delay_cnt; >> > >> > Can you explain the significance of pruss_num? As far as I >> > can tell, you always pass constants in here, so it should >> > be possible to determine the number from the device. >> >> SG - The number is not programmed in the device, I need something to >> decide >> which PRU to disable or enable. > > I still don't understand. Please explain how the devices > relate to the multiple PRUs in hardware. There are two devices, CAN and UART, in our case we use the PRU as follows: 1. CAN-TX on PRU0, CAN-RX on PRU1 2. SUART-TX on PRU0, SUART-RX on PRU1 3. SUART-TXRX on PRU0, SUART-TXRX on PRU1 From nsekhar at ti.com Tue Apr 5 05:58:33 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 5 Apr 2011 16:28:33 +0530 Subject: [PATCH v18 08/13] davinci: eliminate use of IO_ADDRESS() on sysmod In-Reply-To: <1301737397-4327-1-git-send-email-manjunath.hadli@ti.com> References: <1301737397-4327-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Hi Manju, On Sat, Apr 02, 2011 at 15:13:17, Hadli, Manjunath wrote: > Current devices.c file has a number of instances where > IO_ADDRESS() is used for system module register > access. Eliminate this in favor of a ioremap() > based access. > > Consequent to this, a new global pointer davinci_sysmodbase > has been introduced which gets initialized during > the initialization of each relevant SoC > > Signed-off-by: Manjunath Hadli > Acked-by: Sekhar Nori > --- > diff --git a/arch/arm/mach-davinci/include/mach/hardware.h b/arch/arm/mach-davinci/include/mach/hardware.h > index 414e0b9..2a6b560 100644 > --- a/arch/arm/mach-davinci/include/mach/hardware.h > +++ b/arch/arm/mach-davinci/include/mach/hardware.h > @@ -21,6 +21,12 @@ > */ > #define DAVINCI_SYSTEM_MODULE_BASE 0x01C40000 > > +#ifndef __ASSEMBLER__ > +extern void __iomem *davinci_sysmodbase; > +#define DAVINCI_SYSMODULE_VIRT(x) (davinci_sysmodbase + (x)) > +void davinci_map_sysmod(void); > +#endif Russell has posted[1] that the hardware.h file should not be polluted with platform private stuff like this. Your patch 7/13 actually helped towards that goal, but this one takes us back. This patch cannot be used in the current form. Currently there are separate header files for dm644x, dm355, dm646x and dm365. I would like to start by removing unnecessary code from these files and trying to consolidate them into a single file. Example, the EMAC base address definitions in dm365.h should be moved into dm365.c. Similarly, there is a lot of VPIF specific stuff in dm646x.h which is not really specific to dm646x.h and so should probably be moved to include/media/ or arch/arm/mach-davinci/include/mach/vpif.h Once consolidated into a single file, davinci_sysmodbase can be moved into that file. Also, Russell has said[2] that at least for this merge window only consolidation and bug fixes will go through his tree. This means that as far as mach-davinci is concerned, the clean-up part of this series can go to 2.6.40 - but not the stuff which adds new support. Thanks, Sekhar [1] http://www.spinics.net/lists/arm-kernel/msg120410.html [2] http://www.spinics.net/lists/arm-kernel/msg120606.html From bengardiner at nanometrics.ca Tue Apr 5 16:38:03 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 5 Apr 2011 17:38:03 -0400 Subject: [PATCH 0/6] i2c-davinci gpio pulsed SCL recovery with ICPFUNC Message-ID: This series for the i2c-davinci driver implements both a register dump and a pulsed-SCL recovery of the bus on those controllers that have ICPFUNC registers which is, so far, the da850 and da830 based platforms. I2C "controller timed out" messages were being observed by both myself on the da850evm and Bastian Ruppert on some custom da850 hardware [1]. Originally I thought it was the existing pulse-SCL routine but was quickly proven wrong; my apologies to John Povey and Philby John for jumping to conclusions. A discussion was spawned on the e2e forums [2] where Brad Griffis was instrumental in the development of the recovery routine proposed by this patch series. It was pointed out by him that the da850's i2c controller has the ability to control the SDA and SCL pins as GPIOS through its ICPFUNC registers. The recovery routine implemented by the patch series toggles the SCL pin and reads the SDA state using the ICPFUNC registers. The changes in this series are staged in increments of features and each patch depends on the changes introduced by the patch before it with the exception of patch 2/6 which is largely independent of 1/6. The real meat of the series in in patch 4/6. First we introduce a register dump routine in 1/6 since this information was requested immeadiately by Brad Griffis when the conversation began; then the i2c-davinci platform data structure's comments are converted to kerneldoc in 2/6. Then in 3/6 a level of indirection is introduced so that the implementation of the recovery procedure can be switched at runtime; this level of indirection is used in the subsequent patch, 4/6, to execute a pulsed-SCL recovery routine using the ICPFUNC registers if they are present. Where the presence of the registers is indicated by the platform in a has_pfunc flag in platform data. Finally since all da8xx platforms' i2c controllers have the ICPFUNC registers, the da8xx utility function to register i2c controllers is modified to set the flag so that the new recovery routine is used in all da8xx platforms. I developed this patch against v2.6.38 but I have also tested it against v2.6.39-rc1: it applies cleanly and the recovery routine is executed as it was in v2.6.38. When creating this series I noticed that there are obvious similarities between the existing recovery routine implemented by Philby John and John Povey and the recovery routine proposed in this series. Testing was performed using the ICPFUNC regsiters to toggle SCL as prescribed by the existing routine and it was found that the recovery did not work. The method described initially by Brad Griffis had the initial state of SCL high and delays of 5us with a maximum of 8 pulses with a check of SDA each time as compared to the existing routine with undetermined SCL initial state, 20us delays and a maximum of 8 pulses. I tested and found that if I changed the initial state of SCL or the number of pulses (Bastian had success with 16) that the recovery did not occur as expected; furthermore Brad pointed out that it was important to check the state of SDA after each pulse to see if the slave had released the line. Indeed, adding the check of SDA resulted in a quicker recovery. On my da850evm, at least, the recovery took only one SCL pulse every time. It would be nice to consolidate the two recovery routines and -- since they are gpio-based -- put the shared recovery routine in i2c-algo-bit so it can also be used by bitbanging i2c masters. I did not undertake the former because I don't have access to the hardware on which the existing recovery routine was tested. I did not undertake the latter since 1) it seems premature until the recovery routines are consolidated (or not) and 2) it would require more changes of a broad scope which I feared might mire the review process of this series which is, at its core, a functioning workaround of an observed problem with da850evm hardware (plus some regsiter dumps). It is my hope that both the consolidation of the recovery routines and the extraction to common bitbanging code can be done in a later series. [1] http://permalink.gmane.org/gmane.linux.davinci/22291 [2] http://e2e.ti.com/support/dsp/omap_applications_processors/f/42/p/99895/350610.aspx Ben Gardiner (6): i2c-davinci: register dump before attempted bus recovery i2c-davinci: convert davinci_i2c_platform_data to kerneldoc i2c-davinci: introduce a dev-> function pointer for scl pulsing i2c-davinci: use the DA8xx's ICPFUNC to toggle I2C as gpio i2c-davinci: if device has pfunc register, dump that group also da8xx: enable the use of the ICPFUNC in i2c-davinci arch/arm/mach-davinci/devices-da8xx.c | 6 + arch/arm/mach-davinci/include/mach/i2c.h | 21 +++- drivers/i2c/busses/i2c-davinci.c | 155 ++++++++++++++++++++++++++++-- 3 files changed, 170 insertions(+), 12 deletions(-) From bengardiner at nanometrics.ca Tue Apr 5 16:38:04 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 5 Apr 2011 17:38:04 -0400 Subject: [PATCH 1/6] i2c-davinci: register dump before attempted bus recovery In-Reply-To: References: Message-ID: <1f1cfc8d37d01097ccfd860098807d1aafd89a49.1302031487.git.bengardiner@nanometrics.ca> Produce a dump of the register contents of the I2C controller (at the debug level) whenever an i2c recovery is initiated. Signed-off-by: Ben Gardiner Cc: Bastian Ruppert Cc: Brad Griffis Cc: Sekhar Nori Cc: Ben Dooks --- The motivation for the introduction of this additional debugging information is that when I2C recovery on da850 began discussion in the e2e forums [1] a register dump was requested. [1] http://e2e.ti.com/support/dsp/omap_applications_processors/f/42/p/99895/350610.aspx#350610 drivers/i2c/busses/i2c-davinci.c | 32 ++++++++++++++++++++++++++++++++ 1 files changed, 32 insertions(+), 0 deletions(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 5795c83..7011222 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -133,6 +133,37 @@ static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg) return __raw_readw(i2c_dev->base + reg); } +static void i2c_davinci_dump_regs(struct davinci_i2c_dev *dev) +{ + dev_dbg(dev->dev, "PSC = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG)); + dev_dbg(dev->dev, "CLKL = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKL_REG)); + dev_dbg(dev->dev, "CLKH = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKH_REG)); + dev_dbg(dev->dev, "MDR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG)); + + dev_dbg(dev->dev, "OAR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_OAR_REG)); + dev_dbg(dev->dev, "IMR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG)); + dev_dbg(dev->dev, "STR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG)); + dev_dbg(dev->dev, "CNT = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_CNT_REG)); + dev_dbg(dev->dev, "DRR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_DRR_REG)); + dev_dbg(dev->dev, "SAR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_SAR_REG)); + dev_dbg(dev->dev, "DXR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_DXR_REG)); + dev_dbg(dev->dev, "IVR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG)); + dev_dbg(dev->dev, "EMDR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_EMDR_REG)); +} + /* Generate a pulse on the i2c clock pin. */ static void generic_i2c_clock_pulse(unsigned int scl_pin) { @@ -157,6 +188,7 @@ static void i2c_recover_bus(struct davinci_i2c_dev *dev) u32 flag = 0; struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + i2c_davinci_dump_regs(dev); dev_err(dev->dev, "initiating i2c bus recovery\n"); /* Send NACK to the slave */ flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); -- 1.7.1 From bengardiner at nanometrics.ca Tue Apr 5 16:38:06 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 5 Apr 2011 17:38:06 -0400 Subject: [PATCH 3/6] i2c-davinci: introduce a dev-> function pointer for scl pulsing In-Reply-To: References: Message-ID: <392ee718990c7f0f315d2273a1c74337149a0e1b.1302031487.git.bengardiner@nanometrics.ca> The current implementation of the I2C recovery routine checks whether platform data ->scl_pin has been assigned and if so, the generic_i2c_clock_pulse routine is executed. In preparation for an alternative recovery routine; introduce a pulse_scl function pointer to the driver private structure and assign it the value of generic_i2c_clock_pulse on init if the scl_pin is assigned in platform data. Signed-off-by: Ben Gardiner Cc: Sekhar Nori Cc: Ben Dooks --- drivers/i2c/busses/i2c-davinci.c | 20 +++++++++++++------- 1 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 7011222..0a2c697 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -110,6 +110,7 @@ struct davinci_i2c_dev { int stop; u8 terminate; struct i2c_adapter adapter; + void (*pulse_scl) (struct davinci_i2c_dev *dev); #ifdef CONFIG_CPU_FREQ struct completion xfr_complete; struct notifier_block freq_transition; @@ -165,16 +166,17 @@ static void i2c_davinci_dump_regs(struct davinci_i2c_dev *dev) } /* Generate a pulse on the i2c clock pin. */ -static void generic_i2c_clock_pulse(unsigned int scl_pin) +static void generic_i2c_clock_pulse(struct davinci_i2c_dev *dev) { + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; u16 i; - if (scl_pin) { + if (pdata->scl_pin) { /* Send high and low on the SCL line */ for (i = 0; i < 9; i++) { - gpio_set_value(scl_pin, 0); + gpio_set_value(pdata->scl_pin, 0); udelay(20); - gpio_set_value(scl_pin, 1); + gpio_set_value(pdata->scl_pin, 1); udelay(20); } } @@ -186,7 +188,6 @@ static void generic_i2c_clock_pulse(unsigned int scl_pin) static void i2c_recover_bus(struct davinci_i2c_dev *dev) { u32 flag = 0; - struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; i2c_davinci_dump_regs(dev); dev_err(dev->dev, "initiating i2c bus recovery\n"); @@ -195,8 +196,8 @@ static void i2c_recover_bus(struct davinci_i2c_dev *dev) flag |= DAVINCI_I2C_MDR_NACK; /* write the data into mode register */ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); - if (pdata) - generic_i2c_clock_pulse(pdata->scl_pin); + if (dev->pulse_scl) + dev->pulse_scl(dev); /* Send STOP */ flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); flag |= DAVINCI_I2C_MDR_STP; @@ -669,6 +670,7 @@ static struct i2c_algorithm i2c_davinci_algo = { static int davinci_i2c_probe(struct platform_device *pdev) { + struct davinci_i2c_platform_data *pdata; struct davinci_i2c_dev *dev; struct i2c_adapter *adap; struct resource *mem, *irq, *ioarea; @@ -751,6 +753,10 @@ static int davinci_i2c_probe(struct platform_device *pdev) goto err_free_irq; } + pdata = dev->dev->platform_data; + if (pdata->scl_pin) + dev->pulse_scl = generic_i2c_clock_pulse; + return 0; err_free_irq: -- 1.7.1 From bengardiner at nanometrics.ca Tue Apr 5 16:38:05 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 5 Apr 2011 17:38:05 -0400 Subject: [PATCH 2/6] i2c-davinci: convert davinci_i2c_platform_data to kerneldoc In-Reply-To: References: Message-ID: <0710b48a8514dface179b54c615b7cc0bba20d42.1302031487.git.bengardiner@nanometrics.ca> The davinci_i2c_platform_data struct is mach-specific but still deserves to be documented using kernel doc. Signed-off-by: Ben Gardiner Cc: Sekhar Nori Cc: Ben Dooks --- arch/arm/mach-davinci/include/mach/i2c.h | 17 ++++++++++++----- 1 files changed, 12 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/i2c.h b/arch/arm/mach-davinci/include/mach/i2c.h index 2312d19..ab07a44 100644 --- a/arch/arm/mach-davinci/include/mach/i2c.h +++ b/arch/arm/mach-davinci/include/mach/i2c.h @@ -12,12 +12,19 @@ #ifndef __ASM_ARCH_I2C_H #define __ASM_ARCH_I2C_H -/* All frequencies are expressed in kHz */ +/** + * davinci_i2c_platform_data - Platform data for I2C master device on DaVinci + * + * @bus_freq: standard bus frequency (kHz) + * @bus_delay: post-transaction delay (usec) + * @sda_pin: GPIO pin ID to use for SDA + * @scl_pin: GPIO pin ID to use for SCL + */ struct davinci_i2c_platform_data { - unsigned int bus_freq; /* standard bus frequency (kHz) */ - unsigned int bus_delay; /* post-transaction delay (usec) */ - unsigned int sda_pin; /* GPIO pin ID to use for SDA */ - unsigned int scl_pin; /* GPIO pin ID to use for SCL */ + unsigned int bus_freq; + unsigned int bus_delay; + unsigned int sda_pin; + unsigned int scl_pin; }; /* for board setup code */ -- 1.7.1 From bengardiner at nanometrics.ca Tue Apr 5 16:38:08 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 5 Apr 2011 17:38:08 -0400 Subject: [PATCH 5/6] i2c-davinci: if device has pfunc register, dump that group also In-Reply-To: References: Message-ID: <3a177f7c5f48db679db25035911abd2f53203122.1302031487.git.bengardiner@nanometrics.ca> If the platform has indicated that this I2c controller has the ICPFUNC regsiters then also dump their contents when producing a debug register dump. Signed-off-by: Ben Gardiner Cc: Bastian Ruppert Cc: Brad Griffis Cc: Sekhar Nori Cc: Ben Dooks --- drivers/i2c/busses/i2c-davinci.c | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 5bdc98c..a084e50 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -164,6 +164,8 @@ static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg) static void i2c_davinci_dump_regs(struct davinci_i2c_dev *dev) { + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + dev_dbg(dev->dev, "PSC = %08x\n", davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG)); dev_dbg(dev->dev, "CLKL = %08x\n", @@ -191,6 +193,19 @@ static void i2c_davinci_dump_regs(struct davinci_i2c_dev *dev) davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG)); dev_dbg(dev->dev, "EMDR = %08x\n", davinci_i2c_read_reg(dev, DAVINCI_I2C_EMDR_REG)); + + if (pdata->has_pfunc) { + dev_dbg(dev->dev, "PFUNC = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_PFUNC_REG)); + dev_dbg(dev->dev, "PDIR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_PDIR_REG)); + dev_dbg(dev->dev, "PDIN = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_PDIN_REG)); + dev_dbg(dev->dev, "DSET = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_DSET_REG)); + dev_dbg(dev->dev, "DCLR = %08x\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_DCLR_REG)); + } } /* Generate a pulse on the i2c clock pin. */ -- 1.7.1 From bengardiner at nanometrics.ca Tue Apr 5 16:38:09 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 5 Apr 2011 17:38:09 -0400 Subject: [PATCH 6/6] da8xx: enable the use of the ICPFUNC in i2c-davinci In-Reply-To: References: Message-ID: Both the da850 and da830 have an I2C controller which has the ICPFUNC registers. Indicate this for all da830 and da850 boards by setting the has_pfunc flag true in the da8xx utility setup routine for registering the I2C controller Signed-off-by: Ben Gardiner Cc: Sekhar Nori Cc: Ben Dooks --- arch/arm/mach-davinci/devices-da8xx.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index beda8a4..da01558 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -324,6 +324,12 @@ int __init da8xx_register_i2c(int instance, else return -EINVAL; + /* + * Both the DA850 and DA830 have an I2C controller which has the + * ICPFUNC et. al. registers + */ + pdata->has_pfunc = 1; + pdev->dev.platform_data = pdata; return platform_device_register(pdev); } -- 1.7.1 From bengardiner at nanometrics.ca Tue Apr 5 16:38:07 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 5 Apr 2011 17:38:07 -0400 Subject: [PATCH 4/6] i2c-davinci: use the DA8xx's ICPFUNC to toggle I2C as gpio In-Reply-To: References: Message-ID: On the da850evm, with the default config of polling keys enabled, several "controller timed out" errors were observed on the console; as well as the same error observed on custom hardware when communicating with a i2c touchscreen device. [1] Discussion of the causes and potential workarounds began on the e2e forums [2] where Brad Griffis pointed out that the da850 (and da830) has an i2c controller whose SCL and SDA may be manipulated as GPIOs by using the ICPFUNC registers of the i2c controller. He further suggested a means of using this feature to send clock pulses on the I2C bus to free the frozen slave device. Implement the suggested procedure by toggling SCL and checking SDA using the ICPFUNC registers of the I2C controller when present. Allow platforms to indicate the presence of the ICPFUNC registers with a has_pfunc platform data flag. [1] http://permalink.gmane.org/gmane.linux.davinci/22291 [2] http://e2e.ti.com/support/dsp/omap_applications_processors/f/42/p/99895/350610.aspx Signed-off-by: Ben Gardiner Cc: Bastian Ruppert Cc: Brad Griffis Cc: Jon Povey Cc: Philby John Cc: Sekhar Nori Cc: Ben Dooks --- arch/arm/mach-davinci/include/mach/i2c.h | 6 ++- drivers/i2c/busses/i2c-davinci.c | 88 ++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/i2c.h b/arch/arm/mach-davinci/include/mach/i2c.h index ab07a44..858c5c6 100644 --- a/arch/arm/mach-davinci/include/mach/i2c.h +++ b/arch/arm/mach-davinci/include/mach/i2c.h @@ -19,12 +19,16 @@ * @bus_delay: post-transaction delay (usec) * @sda_pin: GPIO pin ID to use for SDA * @scl_pin: GPIO pin ID to use for SCL + * @has_pfunc: set this to true if the i2c controller on your chip has a + * ICPFUNC register which allows the SDA and SCL pins to be + * controlled as gpio (like in DA850) */ struct davinci_i2c_platform_data { unsigned int bus_freq; unsigned int bus_delay; unsigned int sda_pin; unsigned int scl_pin; + bool has_pfunc; }; /* for board setup code */ diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 0a2c697..5bdc98c 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -65,6 +65,11 @@ #define DAVINCI_I2C_IVR_REG 0x28 #define DAVINCI_I2C_EMDR_REG 0x2c #define DAVINCI_I2C_PSC_REG 0x30 +#define DAVINCI_I2C_PFUNC_REG 0x48 +#define DAVINCI_I2C_PDIR_REG 0x4c +#define DAVINCI_I2C_PDIN_REG 0x50 +#define DAVINCI_I2C_DSET_REG 0x58 +#define DAVINCI_I2C_DCLR_REG 0x5c #define DAVINCI_I2C_IVR_AAS 0x07 #define DAVINCI_I2C_IVR_SCD 0x06 @@ -98,6 +103,29 @@ #define DAVINCI_I2C_IMR_NACK BIT(1) #define DAVINCI_I2C_IMR_AL BIT(0) +/* set SDA and SCL as GPIO */ +#define DAVINCI_I2C_PFUNC_PFUNC0 BIT(0) + +/* set SCL as output when used as GPIO*/ +#define DAVINCI_I2C_PDIR_PDIR0 BIT(0) +/* set SDA as output when used as GPIO*/ +#define DAVINCI_I2C_PDIR_PDIR1 BIT(1) + +/* read SCL GPIO level */ +#define DAVINCI_I2C_PDIN_PDIN0 BIT(0) +/* read SDA GPIO level */ +#define DAVINCI_I2C_PDIN_PDIN1 BIT(1) + +/*set the SCL GPIO high */ +#define DAVINCI_I2C_DSET_PDSET0 BIT(0) +/*set the SDA GPIO high */ +#define DAVINCI_I2C_DSET_PDSET1 BIT(1) + +/* set the SCL GPIO low */ +#define DAVINCI_I2C_DCLR_PDCLR0 BIT(0) +/* set the SDA GPIO low */ +#define DAVINCI_I2C_DCLR_PDCLR1 BIT(1) + struct davinci_i2c_dev { struct device *dev; void __iomem *base; @@ -182,6 +210,64 @@ static void generic_i2c_clock_pulse(struct davinci_i2c_dev *dev) } } +static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev, + int val); + +/* Generate gpio a pulse on the i2c clock pin. */ +static void i2c_davinci_pfunc_i2c_clock_pulse(struct davinci_i2c_dev *dev) +{ + u32 flag = 0; + u16 i; + + davinci_i2c_reset_ctrl(dev, 0); + + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, 0x00); + + /* SCL output, SDA input */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_PDIR_REG, + DAVINCI_I2C_PDIR_PDIR0); + + /* change to GPIO mode */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_PFUNC_REG, + DAVINCI_I2C_PFUNC_PFUNC0); + + /* SCL high */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_DSET_REG, + DAVINCI_I2C_DSET_PDSET0); + udelay(5); + for (i = 0; i < 16; i++) { + /* SCL low */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_DCLR_REG, + DAVINCI_I2C_DCLR_PDCLR0); + udelay(5); + /* SCL high */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_DSET_REG, + DAVINCI_I2C_DSET_PDSET0); + udelay(5); + + /* read the state of SDA */ + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_PDIN_REG); + if (flag & DAVINCI_I2C_PDIN_PDIN1) { + dev_dbg(dev->dev, "recovered after %d SCL pulses", + i + 1); + break; + } + } + + /* change back to I2C mode */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_PFUNC_REG, 0); + + /* take the I2C dev out of reset */ + davinci_i2c_reset_ctrl(dev, 1); + + /* read the state of SDA */ + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_PDIN_REG); + if (flag & DAVINCI_I2C_PDIN_PDIN1) + return; + + dev_err(dev->dev, "I2C slave will not release SDA.\n"); +} + /* This routine does i2c bus recovery as specified in the * i2c protocol Rev. 03 section 3.16 titled "Bus clear" */ @@ -756,6 +842,8 @@ static int davinci_i2c_probe(struct platform_device *pdev) pdata = dev->dev->platform_data; if (pdata->scl_pin) dev->pulse_scl = generic_i2c_clock_pulse; + else if (pdata->has_pfunc) + dev->pulse_scl = i2c_davinci_pfunc_i2c_clock_pulse; return 0; -- 1.7.1 From r&d4 at dave-tech.it Wed Apr 6 09:24:40 2011 From: r&d4 at dave-tech.it (r&d4 at dave-tech.it) Date: Wed, 06 Apr 2011 16:24:40 +0200 Subject: PATCH: DaVinci EMAC MII ioctl support Message-ID: <4D9C77A8.3040209@dave-tech.it> Dear DaVince developer, I'm currently working with a TI AM35xx on our embedded SOM. Due the fact that I need to use mii-tool on our platform I've done a little patch to your driver to add mii ioctl support. It has been tested on our Lizard board (SMSC LAN8700 as Ethernet phy) with the following version of mii-tool: root at lizard:/mnt/ubifs# mii-tool -V mii-tool.c 1.9 2000/04/28 00:56:08 (David Hinds) eth0: 100 Mbit, full duplex, link ok Feel free to add this to patch to the next release of your driver, I think it will be useful to many developers. Best Regards, -- Andrea SCIAN DAVE www.dave.eu -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: davinci_emac_mii_ioctl_support.patch URL: From khilman at ti.com Wed Apr 6 09:49:02 2011 From: khilman at ti.com (Kevin Hilman) Date: Wed, 06 Apr 2011 07:49:02 -0700 Subject: RFC: More NR_IRQS? In-Reply-To: <4D970FD9.1080304@criticallink.com> (Michael Williamson's message of "Sat, 02 Apr 2011 08:00:25 -0400") References: <4D970FD9.1080304@criticallink.com> Message-ID: <87k4f7juzl.fsf@ti.com> Michael Williamson writes: > I'm working with a couple of gpio expanders (for example, the PCA953X) > that have the capability to plug into the gpiolib kernel > infrastructure and include support for interrupt notification. For > the interrupts, they appear to function similarly to the davinci > gpio.c driver. I.E., they provide for "soft" IRQs in order to provide > one-to-one mapping to support gpio_to_irq(). > > The problem is that the NR_IRQS in mach/irqs.h is fixed to only > accommodate the SOC interrupt handler and it's local GPIOs. There are > no extra entries to support expanders. > > I was going to propose an integer Kconfig option (e.g., > CONFIG_DAVINCI_EXTRA_IRQS, default 0) that would be added to NR_IRQS > to support this. Is there a better way? Or an alternate solutione? Just increase NR_IRQS. Check arch/arm/plat-omap/include/plat/irqs.h for an example. Also, we now have sparse IRQ support in ARM, which means we don't actully have to allocate and irq_desc for NR_IRQS interrupts. Only those that are requested get an irq_desc. Kevin From henryzhang62 at yahoo.com Wed Apr 6 11:30:05 2011 From: henryzhang62 at yahoo.com (hong zhang) Date: Wed, 6 Apr 2011 09:30:05 -0700 (PDT) Subject: PATCH: DaVinci EMAC MII ioctl support In-Reply-To: <4D9C77A8.3040209@dave-tech.it> Message-ID: <20223.29410.qm@web161716.mail.bf1.yahoo.com> ethtool is not supported by davinci-emac? --- On Wed, 4/6/11, r&d4 at dave-tech.it wrote: > From: r&d4 at dave-tech.it > Subject: PATCH: DaVinci EMAC MII ioctl support > To: davinci-linux-open-source at linux.davincidsp.com > Date: Wednesday, April 6, 2011, 9:24 AM > > Dear DaVince developer, > > I'm currently working with a TI AM35xx on our embedded > SOM. > > Due the fact that I need to use mii-tool on our platform > I've done a > little patch to your driver to add mii ioctl support. > > It has been tested on our Lizard board (SMSC LAN8700 as > Ethernet phy) > with the following version of mii-tool: > > root at lizard:/mnt/ubifs# mii-tool -V > mii-tool.c 1.9 2000/04/28 00:56:08 (David Hinds) > eth0: 100 Mbit, full duplex, link ok > > Feel free to add this to patch to the next release of your > driver, I > think it will be useful to many developers. > > Best Regards, > > -- > > Andrea SCIAN > DAVE > www.dave.eu > > > -----Inline Attachment Follows----- > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > From r&d4 at dave-tech.it Wed Apr 6 11:45:57 2011 From: r&d4 at dave-tech.it (r&d4 at dave-tech.it) Date: Wed, 06 Apr 2011 18:45:57 +0200 Subject: PATCH: DaVinci EMAC MII ioctl support In-Reply-To: <20223.29410.qm@web161716.mail.bf1.yahoo.com> References: <20223.29410.qm@web161716.mail.bf1.yahoo.com> Message-ID: <4D9C98C5.9010807@dave-tech.it> On 06/04/2011 18.30, hong zhang wrote: > ethtool is not supported by davinci-emac? > I try ethtool on my platform and it's working. Anyway I need a tool to write mii register directly and I already have a modified source of mii-tool that allow me to do that, so it was easier to add a line to DaVinci driver instead than learing ethtool sources. Regards, Andrea From sshtylyov at ru.mvista.com Wed Apr 6 12:17:21 2011 From: sshtylyov at ru.mvista.com (Sergei Shtylyov) Date: Wed, 6 Apr 2011 21:17:21 +0400 Subject: [PATCH] DA830: fix SPI1 base address Message-ID: <201104062117.22238.sshtylyov@ru.mvista.com> Commit 54ce6883d29630ff334bee4256a25e3f8719a181 (davinci: da8xx: add spi resources and registration routine) wrongly assumed that SPI1 is mapped at the same address on DA830/OMAP-L137 and DA850/OMAP-L138; actually, the base address was valid only for the latter SoC. Teach the code to pass the correct SPI1 memory resource for both SoCs... Signed-off-by: Sergei Shtylyov --- The patch is against the recent DaVinci tree. It's needed in 2.6.39. arch/arm/mach-davinci/devices-da8xx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) Index: linux-davinci/arch/arm/mach-davinci/devices-da8xx.c =================================================================== --- linux-davinci.orig/arch/arm/mach-davinci/devices-da8xx.c +++ linux-davinci/arch/arm/mach-davinci/devices-da8xx.c @@ -39,7 +39,8 @@ #define DA8XX_GPIO_BASE 0x01e26000 #define DA8XX_I2C1_BASE 0x01e28000 #define DA8XX_SPI0_BASE 0x01c41000 -#define DA8XX_SPI1_BASE 0x01f0e000 +#define DA830_SPI1_BASE 0x01e12000 +#define DA850_SPI1_BASE 0x01f0e000 #define DA8XX_EMAC_CTRL_REG_OFFSET 0x3000 #define DA8XX_EMAC_MOD_REG_OFFSET 0x2000 @@ -762,8 +763,8 @@ static struct resource da8xx_spi0_resour static struct resource da8xx_spi1_resources[] = { [0] = { - .start = DA8XX_SPI1_BASE, - .end = DA8XX_SPI1_BASE + SZ_4K - 1, + .start = DA830_SPI1_BASE, + .end = DA830_SPI1_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -832,5 +833,10 @@ int __init da8xx_register_spi(int instan da8xx_spi_pdata[instance].num_chipselect = len; + if (instance == 1 && cpu_is_davinci_da850()) { + da8xx_spi1_resources[0].start = DA850_SPI1_BASE; + da8xx_spi1_resources[0].end = DA850_SPI1_BASE + SZ_4K - 1; + } + return platform_device_register(&da8xx_spi_device[instance]); } From sshtylyov at ru.mvista.com Wed Apr 6 12:24:31 2011 From: sshtylyov at ru.mvista.com (Sergei Shtylyov) Date: Wed, 6 Apr 2011 21:24:31 +0400 Subject: [PATCH 1/3] DA8xx: kill duplicate #define DA8XX_GPIO_BASE Message-ID: <201104062124.31206.sshtylyov@ru.mvista.com> DA8XX_GPIO_BASE is #define'd in both and devices-da8xx.c; moreover, it's not even used in the latter file... Signed-off-by: Sergei Shtylyov --- The patch is against the recent DaVinci tree plus the fix that I've posted earlier today. arch/arm/mach-davinci/devices-da8xx.c | 1 - 1 file changed, 1 deletion(-) Index: linux-davinci/arch/arm/mach-davinci/devices-da8xx.c =================================================================== --- linux-davinci.orig/arch/arm/mach-davinci/devices-da8xx.c +++ linux-davinci/arch/arm/mach-davinci/devices-da8xx.c @@ -36,7 +36,6 @@ #define DA8XX_EMAC_CPGMACSS_BASE 0x01e22000 #define DA8XX_EMAC_CPGMAC_BASE 0x01e23000 #define DA8XX_EMAC_MDIO_BASE 0x01e24000 -#define DA8XX_GPIO_BASE 0x01e26000 #define DA8XX_I2C1_BASE 0x01e28000 #define DA8XX_SPI0_BASE 0x01c41000 #define DA830_SPI1_BASE 0x01e12000 From sshtylyov at ru.mvista.com Wed Apr 6 12:26:06 2011 From: sshtylyov at ru.mvista.com (Sergei Shtylyov) Date: Wed, 6 Apr 2011 21:26:06 +0400 Subject: [PATCH 2/3] DA8xx: kill duplicate #define DA8XX_PLL1_BASE Message-ID: <201104062126.06877.sshtylyov@ru.mvista.com> Commit 044ca01521d077a35b46a445b02b93f413109a4b (davinci: da850/omap-l138: add support for SoC suspend) introduced DA8XX_PLL1_BASE despite PLL1 exists only on DA850/OMAP-L138 and da850.c even already #define'd DA850_PLL1_BASE. Kill the duplicate macro, renaming an existing reference to it... Signed-off-by: Sergei Shtylyov --- The patch is against the recent DaVinci tree. arch/arm/mach-davinci/da850.c | 2 +- arch/arm/mach-davinci/include/mach/da8xx.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) Index: linux-davinci/arch/arm/mach-davinci/da850.c =================================================================== --- linux-davinci.orig/arch/arm/mach-davinci/da850.c +++ linux-davinci/arch/arm/mach-davinci/da850.c @@ -1055,7 +1055,7 @@ int da850_register_pm(struct platform_de if (!pdata->cpupll_reg_base) return -ENOMEM; - pdata->ddrpll_reg_base = ioremap(DA8XX_PLL1_BASE, SZ_4K); + pdata->ddrpll_reg_base = ioremap(DA850_PLL1_BASE, SZ_4K); if (!pdata->ddrpll_reg_base) { ret = -ENOMEM; goto no_ddrpll_mem; Index: linux-davinci/arch/arm/mach-davinci/include/mach/da8xx.h =================================================================== --- linux-davinci.orig/arch/arm/mach-davinci/include/mach/da8xx.h +++ linux-davinci/arch/arm/mach-davinci/include/mach/da8xx.h @@ -65,7 +65,6 @@ extern unsigned int da850_max_speed; #define DA8XX_GPIO_BASE 0x01e26000 #define DA8XX_PSC1_BASE 0x01e27000 #define DA8XX_LCD_CNTRL_BASE 0x01e13000 -#define DA8XX_PLL1_BASE 0x01e1a000 #define DA8XX_MMCSD0_BASE 0x01c40000 #define DA8XX_AEMIF_CS2_BASE 0x60000000 #define DA8XX_AEMIF_CS3_BASE 0x62000000 From sshtylyov at ru.mvista.com Wed Apr 6 12:29:24 2011 From: sshtylyov at ru.mvista.com (Sergei Shtylyov) Date: Wed, 6 Apr 2011 21:29:24 +0400 Subject: [PATCH 3/3] DA8xx: move base address #define's to their proper place Message-ID: <201104062129.25085.sshtylyov@ru.mvista.com> Move DA8XX_MMCSD0_BASE, DA8XX_LCD_CNTRL_BASE, and DA8XX_DDR2_CTL_BASE from to devices-da8xx.c as the latter file is the only place where these macros are used. While at it, restore sorting the base address macros by address value in devices-da8xx.c... Signed-off-by: Sergei Shtylyov --- The patch is against the recent DaVinci tree plus the fix that I've posted earlier today. arch/arm/mach-davinci/devices-da8xx.c | 15 +++++++++------ arch/arm/mach-davinci/include/mach/da8xx.h | 3 --- 2 files changed, 9 insertions(+), 9 deletions(-) Index: linux-davinci/arch/arm/mach-davinci/devices-da8xx.c =================================================================== --- linux-davinci.orig/arch/arm/mach-davinci/devices-da8xx.c +++ linux-davinci/arch/arm/mach-davinci/devices-da8xx.c @@ -24,22 +24,25 @@ #include "clock.h" #define DA8XX_TPCC_BASE 0x01c00000 -#define DA850_MMCSD1_BASE 0x01e1b000 -#define DA850_TPCC1_BASE 0x01e30000 #define DA8XX_TPTC0_BASE 0x01c08000 #define DA8XX_TPTC1_BASE 0x01c08400 -#define DA850_TPTC2_BASE 0x01e38000 #define DA8XX_WDOG_BASE 0x01c21000 /* DA8XX_TIMER64P1_BASE */ #define DA8XX_I2C0_BASE 0x01c22000 -#define DA8XX_RTC_BASE 0x01C23000 +#define DA8XX_RTC_BASE 0x01c23000 +#define DA8XX_MMCSD0_BASE 0x01c40000 +#define DA8XX_SPI0_BASE 0x01c41000 +#define DA830_SPI1_BASE 0x01e12000 +#define DA8XX_LCD_CNTRL_BASE 0x01e13000 +#define DA850_MMCSD1_BASE 0x01e1b000 #define DA8XX_EMAC_CPPI_PORT_BASE 0x01e20000 #define DA8XX_EMAC_CPGMACSS_BASE 0x01e22000 #define DA8XX_EMAC_CPGMAC_BASE 0x01e23000 #define DA8XX_EMAC_MDIO_BASE 0x01e24000 #define DA8XX_I2C1_BASE 0x01e28000 -#define DA8XX_SPI0_BASE 0x01c41000 -#define DA830_SPI1_BASE 0x01e12000 +#define DA850_TPCC1_BASE 0x01e30000 +#define DA850_TPTC2_BASE 0x01e38000 #define DA850_SPI1_BASE 0x01f0e000 +#define DA8XX_DDR2_CTL_BASE 0xb0000000 #define DA8XX_EMAC_CTRL_REG_OFFSET 0x3000 #define DA8XX_EMAC_MOD_REG_OFFSET 0x2000 Index: linux-davinci/arch/arm/mach-davinci/include/mach/da8xx.h =================================================================== --- linux-davinci.orig/arch/arm/mach-davinci/include/mach/da8xx.h +++ linux-davinci/arch/arm/mach-davinci/include/mach/da8xx.h @@ -64,12 +64,9 @@ extern unsigned int da850_max_speed; #define DA8XX_TIMER64P1_BASE 0x01c21000 #define DA8XX_GPIO_BASE 0x01e26000 #define DA8XX_PSC1_BASE 0x01e27000 -#define DA8XX_LCD_CNTRL_BASE 0x01e13000 -#define DA8XX_MMCSD0_BASE 0x01c40000 #define DA8XX_AEMIF_CS2_BASE 0x60000000 #define DA8XX_AEMIF_CS3_BASE 0x62000000 #define DA8XX_AEMIF_CTL_BASE 0x68000000 -#define DA8XX_DDR2_CTL_BASE 0xb0000000 #define DA8XX_ARM_RAM_BASE 0xffff0000 void __init da830_init(void); From laurent.pinchart at ideasonboard.com Thu Apr 7 06:58:20 2011 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Thu, 7 Apr 2011 13:58:20 +0200 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1301737249-4012-1-git-send-email-manjunath.hadli@ti.com> References: <1301737249-4012-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201104071358.21451.laurent.pinchart@ideasonboard.com> Hi Manjunath, On Saturday 02 April 2011 11:40:49 Manjunath Hadli wrote: > This is the display driver for Texas Instruments's DM644X family > SoC. This patch contains the main implementation of the driver with the > V4L2 interface. The driver implements the streaming model with > support for both kernel allocated buffers and user pointers. It also > implements all of the necessary IOCTLs necessary and supported by the > video display device. > > Signed-off-by: Manjunath Hadli > Acked-by: Muralidharan Karicheri > Acked-by: Hans Verkuil [snip] > diff --git a/drivers/media/video/davinci/vpbe_display.c > b/drivers/media/video/davinci/vpbe_display.c new file mode 100644 > index 0000000..dde5f8a > --- /dev/null > +++ b/drivers/media/video/davinci/vpbe_display.c > @@ -0,0 +1,2085 @@ > +/* > + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation version 2. > + * > + * This program is distributed WITHOUT ANY WARRANTY of any > + * kind, whether express or implied; without even the implied warranty > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > +#include > +#include > +#include > +#include > +#include That looks suspicious, do you really need to include linux/fs.h ? > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include "vpbe_venc_regs.h" > + > +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" > + > +static int debug; > +static u32 video2_numbuffers = 3; > +static u32 video3_numbuffers = 3; Why is the number of buffers set by a module parameter ? It should be negotiated dynamically with REQBUFS. > +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) > +#define VPBE_DEFAULT_NUM_BUFS 3 > + > +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; > +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; Those two variables are assigned but never read. You either forgot to read them, or to remove them. > +module_param(video2_numbuffers, uint, S_IRUGO); > +module_param(video3_numbuffers, uint, S_IRUGO); > +module_param(video2_bufsize, uint, S_IRUGO); > +module_param(video3_bufsize, uint, S_IRUGO); > +module_param(debug, int, 0644); > + > +static struct buf_config_params display_buf_config_params = { > + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, > + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, > + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, > + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, > + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, > + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, > + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, > +}; This also looks like something that shouldn't be hardcoded. > +static struct vpbe_device *vpbe_dev; > +static struct osd_state *osd_device; No such global variables please. Pass the pointers around between functions instead. > +static int vpbe_display_nr[] = { 2, 3 }; Is there a reason to register video devices with a specific number ? video_register_device() doesn't guarantee, that the requested number will be used, so it looks a bit pointless to me. > +static struct v4l2_capability vpbe_display_videocap = { > + .driver = VPBE_DISPLAY_DRIVER, > + .bus_info = "platform", > + .version = VPBE_DISPLAY_VERSION_CODE, > + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, > +}; Please fill v4l2_capability dynamically in vpbe_display_querycap() instead of using a global variable. > +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; This belongs to your vpbe_display_obj object, not to a global variable. > +static int venc_is_second_field() > +{ > + int ret = 0; > + int val; > + ret = v4l2_subdev_call(vpbe_dev->venc, > + core, > + ioctl, > + VENC_GET_FLD, > + &val); > + if (ret < 0) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Error in getting Field ID 0\n"); > + } > + return val; > +} > + > +/* > + * vpbe_display_isr() > + * ISR function. It changes status of the displayed buffer, takes next > buffer + * from the queue and sets its address in VPBE registers > + */ > +static void vpbe_display_isr(unsigned int event, void *disp_obj) You can pass a vpbe_display object directly tp vpbe_display_isr() instead of using void *. > +{ > + unsigned long jiffies_time = get_jiffies_64(); > + struct timeval timevalue; > + int i, fid; > + unsigned long addr = 0; > + struct vpbe_display_obj *layer = NULL; No need to initialize addr and layer to NULL. > + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; > + > + /* Convert time represention from jiffies to timeval */ > + jiffies_to_timeval(jiffies_time, &timevalue); Please use ktime_get_ts() or ktime_get_real_ts() to get the timestamp. > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > + layer = disp_dev->dev[i]; > + /* If streaming is started in this layer */ > + if (!layer->started) > + continue; > + /* Check the field format */ > + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && > + (event & OSD_END_OF_FRAME)) { > + /* Progressive mode */ > + if (layer_first_int[i]) { > + layer_first_int[i] = 0; > + continue; > + } > + /* > + * Mark status of the cur_frm to > + * done and unlock semaphore on it > + */ > + > + if (layer->cur_frm != layer->next_frm) { > + layer->cur_frm->ts = timevalue; > + layer->cur_frm->state = VIDEOBUF_DONE; Please use videobuf2. > + wake_up_interruptible( > + &layer->cur_frm->done); > + /* Make cur_frm pointing to next_frm */ > + layer->cur_frm = layer->next_frm; > + } > + /* Get the next buffer from buffer queue */ > + spin_lock(&disp_dev->dma_queue_lock); > + if (!list_empty(&layer->dma_queue)) { > + layer->next_frm = > + list_entry(layer->dma_queue.next, > + struct videobuf_buffer, queue); > + /* Remove that buffer from the buffer queue */ > + list_del(&layer->next_frm->queue); Do the next statements until the end of the if () {} block need to be protected by the spinlock ? > + /* Mark status of the buffer as active */ > + layer->next_frm->state = VIDEOBUF_ACTIVE; > + > + addr = videobuf_to_dma_contig(layer->next_frm); > + osd_device->ops.start_layer(osd_device, > + layer->layer_info.id, > + addr, disp_dev->cbcr_ofst); > + } > + spin_unlock(&disp_dev->dma_queue_lock); > + } else { > + /* > + * Interlaced mode > + * If it is first interrupt, ignore it > + */ > + if (layer_first_int[i]) { > + layer_first_int[i] = 0; > + return; > + } > + > + layer->field_id ^= 1; > + if (event & OSD_FIRST_FIELD) > + fid = 0; > + else if (event & OSD_SECOND_FIELD) > + fid = 1; Did you mean VENC_FIRST_FIELD and VENC_SECOND_FIELD ? > + else > + return; I don't think this can happen. > + > + /* > + * If field id does not match with stored > + * field id > + */ > + if (fid != layer->field_id) { > + /* Make them in sync */ > + if (0 == fid) > + layer->field_id = fid; > + > + return; > + } > + /* > + * device field id and local field id are > + * in sync. If this is even field > + */ > + if (0 == fid) { > + if (layer->cur_frm == layer->next_frm) > + continue; > + /* > + * one frame is displayed If next frame is > + * available, release cur_frm and move on > + * copy frame display time > + */ > + layer->cur_frm->ts = timevalue; > + /* Change status of the cur_frm */ > + layer->cur_frm->state = VIDEOBUF_DONE; > + /* unlock semaphore on cur_frm */ > + wake_up_interruptible(&layer->cur_frm->done); > + /* Make cur_frm pointing to next_frm */ > + layer->cur_frm = layer->next_frm; > + } else if (1 == fid) { /* odd field */ > + > + if (list_empty(&layer->dma_queue) > + || (layer->cur_frm != layer->next_frm)) > + continue; Indentation is wrong here. Doesn't the list_empty() check need to be protected by a spinlock ? > + > + /* > + * one field is displayed configure > + * the next frame if it is available > + * otherwise hold on current frame > + * Get next from the buffer queue > + */ > + spin_lock(&disp_dev->dma_queue_lock); > + layer->next_frm = list_entry( > + layer->dma_queue.next, > + struct videobuf_buffer, > + queue); > + > + /* Remove that from the buffer queue */ > + list_del(&layer->next_frm->queue); > + Do the next statements need to be protected by the spinlock ? > + /* Mark state of the frame to active */ > + layer->next_frm->state = VIDEOBUF_ACTIVE; > + addr = videobuf_to_dma_contig(layer->next_frm); > + osd_device->ops.start_layer(osd_device, > + layer->layer_info.id, > + addr, > + disp_dev->cbcr_ofst); > + spin_unlock(&disp_dev->dma_queue_lock); > + } > + } > + } > +} > + > +/* interrupt service routine */ > +static irqreturn_t venc_isr(int irq, void *arg) > +{ > + static unsigned last_event; > + unsigned event = 0; > + > + if (venc_is_second_field()) > + event |= VENC_SECOND_FIELD; > + else > + event |= VENC_FIRST_FIELD; > + > + if (event == (last_event & ~VENC_END_OF_FRAME)) { > + /* > + * If the display is non-interlaced, then we need to flag the > + * end-of-frame event at every interrupt regardless of the > + * value of the FIDST bit. We can conclude that the display is > + * non-interlaced if the value of the FIDST bit is unchanged > + * from the previous interrupt. > + */ What about checking pix_fmt.field instead ? > + event |= VENC_END_OF_FRAME; > + } else if (event == VENC_SECOND_FIELD) { > + /* end-of-frame for interlaced display */ > + event |= VENC_END_OF_FRAME; > + } > + last_event = event; > + > + vpbe_display_isr(event, arg); > + return IRQ_HANDLED; > +} [snip] > +static int vpbe_set_video_display_params(struct vpbe_display *disp_dev, > + struct vpbe_display_obj *layer) This function seems to enable OSD. Shouldn't it be renamed it make that clear ? > +{ > + struct osd_layer_config *cfg = &layer->layer_info.config; > + unsigned long addr; > + int ret = 0; > + > + addr = videobuf_to_dma_contig(layer->cur_frm); > + /* Set address in the display registers */ > + osd_device->ops.start_layer(osd_device, > + layer->layer_info.id, > + addr, > + disp_dev->cbcr_ofst); > + > + ret = osd_device->ops.enable_layer(osd_device, > + layer->layer_info.id, 0); > + if (ret < 0) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Error in enabling osd window layer 0\n"); > + return -1; > + } > + > + /* Enable the window */ Could you please explain how this work ? I fail to understand the relationship between layers, windows, OSD, ... > + layer->layer_info.enable = 1; > + if (cfg->pixfmt == PIXFMT_NV12) { > + struct vpbe_display_obj *otherlayer = > + _vpbe_display_get_other_win(disp_dev, layer); The _vpbe_display_get_other_win() function returns a vpbe_display_obj that you call otherlayer. Is it a layer, a window or an 'obj' ? Please keep the code consistent and rename functions, types and variables where needed. > + > + ret = osd_device->ops.enable_layer(osd_device, > + otherlayer->layer_info.id, 1); > + if (ret < 0) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Error in enabling osd window layer 1\n"); > + return -1; > + } > + otherlayer->layer_info.enable = 1; > + } > + return 0; > +} > + > +static void > +vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, > + struct vpbe_display_obj *layer, > + int expected_xsize, int expected_ysize) > +{ > + struct display_layer_info *layer_info = &layer->layer_info; > + struct v4l2_pix_format *pixfmt = &layer->pix_fmt; > + struct osd_layer_config *cfg = &layer->layer_info.config; > + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; Please split the variable declaration on multiple lines, this gets hard to read. No need to initialize h_scale and v_scale to 0. temp is a bad name. > + v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id; > + > + /* > + * Application initially set the image format. Current display > + * size is obtained from the vpbe display controller. expected_xsize > + * and expected_ysize are set through S_CROP ioctl. Based on this, > + * driver will calculate the scale factors for vertical and > + * horizontal direction so that the image is displayed scaled > + * and expanded. Application uses expansion to display the image > + * in a square pixel. Otherwise it is displayed using displays > + * pixel aspect ratio.It is expected that application chooses > + * the crop coordinates for cropped or scaled display. if crop > + * size is less than the image size, it is displayed cropped or > + * it is displayed scaled and/or expanded. > + * > + * to begin with, set the crop window same as expected. Later we > + * will override with scaled window size > + */ > + > + cfg->xsize = pixfmt->width; > + cfg->ysize = pixfmt->height; > + layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */ > + layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */ > + layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */ > + layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */ > + > + if (pixfmt->width < expected_xsize) { > + h_scale = vpbe_dev->current_timings.xres / pixfmt->width; > + if (h_scale < 2) > + h_scale = 1; > + else if (h_scale >= 4) > + h_scale = 4; > + else > + h_scale = 2; > + cfg->xsize *= h_scale; > + if (cfg->xsize < expected_xsize) { > + if ((standard_id & V4L2_STD_525_60) || > + (standard_id & V4L2_STD_625_50)) { > + temp = (cfg->xsize * > + VPBE_DISPLAY_H_EXP_RATIO_N) / > + VPBE_DISPLAY_H_EXP_RATIO_D; > + if (temp <= expected_xsize) { > + h_exp = 1; > + cfg->xsize = temp; > + } > + } > + } > + if (h_scale == 2) > + layer_info->h_zoom = ZOOM_X2; > + else if (h_scale == 4) > + layer_info->h_zoom = ZOOM_X4; > + if (h_exp) > + layer_info->h_exp = H_EXP_9_OVER_8; > + } else { > + /* no scaling, only cropping. Set display area to crop area */ > + cfg->xsize = expected_xsize; > + } > + > + if (pixfmt->height < expected_ysize) { > + v_scale = expected_ysize / pixfmt->height; > + if (v_scale < 2) > + v_scale = 1; > + else if (v_scale >= 4) > + v_scale = 4; > + else > + v_scale = 2; > + cfg->ysize *= v_scale; > + if (cfg->ysize < expected_ysize) { > + if ((standard_id & V4L2_STD_625_50)) { > + temp = (cfg->ysize * > + VPBE_DISPLAY_V_EXP_RATIO_N) / > + VPBE_DISPLAY_V_EXP_RATIO_D; > + if (temp <= expected_ysize) { > + v_exp = 1; > + cfg->ysize = temp; > + } > + } > + } > + if (v_scale == 2) > + layer_info->v_zoom = ZOOM_X2; > + else if (v_scale == 4) > + layer_info->v_zoom = ZOOM_X4; > + if (v_exp) > + layer_info->h_exp = V_EXP_6_OVER_5; > + } else { > + /* no scaling, only cropping. Set display area to crop area */ > + cfg->ysize = expected_ysize; > + } > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "crop display xsize = %d, ysize = %d\n", > + cfg->xsize, cfg->ysize); > +} > + > +static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, > + struct vpbe_display_obj *layer, > + int top, int left) > +{ > + struct osd_layer_config *cfg = &layer->layer_info.config; > + > + cfg->xpos = cfg->ypos = 0; Please split this on two lines. > + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) > + cfg->xpos = left; > + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) > + cfg->ypos = top; Shouldn't you set cfg->xpos to min(left, vpbe_dev->current_timings.xres - cfg- >xsize) unconditionally instead ? Same for ypos. > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "new xpos = %d, ypos = %d\n", > + cfg->xpos, cfg->ypos); > +} > + > +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, > + struct v4l2_rect *c) > +{ > + if ((c->width == 0) || > + ((c->width + c->left) > vpbe_dev->current_timings.xres) || > + (c->height == 0) || ((c->height + c->top) > > + vpbe_dev->current_timings.yres)) { > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); > + return -1; > + } > + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "window height must be even for interlaced display\n"); > + return -1; > + } Instead of returning an error when crop setting are invalid, you should adjust them are return the adjusted value to userspace. > + return 0; > +} > + > +/** > + * vpbe_try_format() > + * If user application provides width and height, and have bytesperline > set > + * to zero, driver calculates bytesperline and sizeimage based on hardware > + * limits. If application likes to add pads at the end of each line and > + * end of the buffer , it can set bytesperline to line size and sizeimage > to > + * bytesperline * height of the buffer. If driver fills zero for active > + * video width and height, and has requested user bytesperline and > sizeimage, > + * width and height is adjusted to maximum display limit or buffer width > + * height which ever is lower Filling width and height based on the bytesperline and sizeimage fields isn't a standard V4L2 behaviour. Do you have a use case for that ? > + */ > +static int vpbe_try_format(struct vpbe_display *disp_dev, > + struct v4l2_pix_format *pixfmt, int check) > +{ > + int min_sizeimage, bpp, min_height = 1, min_width = 32, > + max_width, max_height, user_info = 0; Split this please. > + > + if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) && > + (pixfmt->pixelformat != V4L2_PIX_FMT_NV12)) > + /* choose default as V4L2_PIX_FMT_UYVY */ > + pixfmt->pixelformat = V4L2_PIX_FMT_UYVY; > + > + /* Check the field format */ > + if (pixfmt->field == V4L2_FIELD_ANY) { > + if (vpbe_dev->current_timings.interlaced) > + pixfmt->field = V4L2_FIELD_INTERLACED; > + else > + pixfmt->field = V4L2_FIELD_NONE; > + } > + > + if (pixfmt->field == V4L2_FIELD_INTERLACED) > + min_height = 2; > + > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) > + bpp = 1; > + else > + bpp = 2; > + > + max_width = vpbe_dev->current_timings.xres; > + max_height = vpbe_dev->current_timings.yres; > + > + min_width /= bpp; > + > + if (!pixfmt->width && !pixfmt->bytesperline) { > + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" > + " cannot be zero\n"); > + return -EINVAL; Don't fail, just set sane default values. This comment applies to the rest of the function as well. > + } > + > + /* if user provided bytesperline, it must provide sizeimage as well */ > + if (pixfmt->bytesperline && !pixfmt->sizeimage) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "sizeimage must be non zero, when user" > + " provides bytesperline\n"); > + return -EINVAL; If sizeimage is not provided, the driver should compute it. > + } > + > + /* adjust bytesperline as per hardware - multiple of 32 */ > + if (!pixfmt->width) > + pixfmt->width = pixfmt->bytesperline / bpp; > + > + if (!pixfmt->bytesperline) > + pixfmt->bytesperline = pixfmt->width * bpp; > + else > + user_info = 1; > + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); > + > + if (pixfmt->width < min_width) { > + if (check) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "height is less than minimum," > + "input width = %d, min_width = %d\n", > + pixfmt->width, min_width); > + return -EINVAL; > + } > + pixfmt->width = min_width; > + } > + > + if (pixfmt->width > max_width) { > + if (check) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "width is more than maximum," > + "input width = %d, max_width = %d\n", > + pixfmt->width, max_width); > + return -EINVAL; > + } > + pixfmt->width = max_width; > + } > + > + /* > + * If height is zero, then atleast we need to have sizeimage > + * to calculate height > + */ > + if (!pixfmt->height) { > + if (user_info) { > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { > + /* > + * for NV12 format, sizeimage is y-plane size > + * + CbCr plane which is half of y-plane > + */ > + pixfmt->height = pixfmt->sizeimage / > + (pixfmt->bytesperline + > + (pixfmt->bytesperline >> 1)); > + } else > + pixfmt->height = pixfmt->sizeimage/ > + pixfmt->bytesperline; > + } > + } > + > + if (pixfmt->height > max_height) { > + if (check && !user_info) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "height is more than maximum," > + "input height = %d, max_height = %d\n", > + pixfmt->height, max_height); > + return -EINVAL; > + } > + pixfmt->height = max_height; > + } > + > + if (pixfmt->height < min_height) { > + if (check && !user_info) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "width is less than minimum," > + "input height = %d, min_height = %d\n", > + pixfmt->height, min_height); > + return -EINVAL; > + } > + pixfmt->height = min_width; > + } > + > + /* if user has not provided bytesperline calculate it based on width */ > + if (!user_info) > + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); > + > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) > + min_sizeimage = pixfmt->bytesperline * pixfmt->height + > + (pixfmt->bytesperline * pixfmt->height >> 1); > + else > + min_sizeimage = pixfmt->bytesperline * pixfmt->height; > + > + if (pixfmt->sizeimage < min_sizeimage) { > + if (check && user_info) { > + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", > + min_sizeimage); > + return -EINVAL; > + } > + pixfmt->sizeimage = min_sizeimage; > + } > + return 0; > +} [snip] > +static int vpbe_display_querycap(struct file *file, void *priv, > + struct v4l2_capability *cap) > +{ > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_display_obj *layer = fh->layer; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); Do you really need a debugging call here ? > + *cap = vpbe_display_videocap; > + > + return 0; > +} > + > +static int vpbe_display_s_crop(struct file *file, void *priv, > + struct v4l2_crop *crop) > +{ > + int ret = 0; > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_display_obj *layer = fh->layer; > + struct vpbe_display *disp_dev = video_drvdata(file); > + struct osd_layer_config *cfg = &layer->layer_info.config; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); > + > + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { Do it the other way around. If crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT return an error. That will save an indentation level for the rest of the function. This comment appleis to the whole driver, you have several occurences of the same issue (such as in vpbe_display_g_crop()). > + struct v4l2_rect *rect = &crop->c; > + > + if (rect->top < 0 || rect->left < 0) { > + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); > + return -EINVAL; > + } Don't fail, adjust the values. > + > + if (vpbe_disp_check_window_params(disp_dev, rect)) { > + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); > + return -EINVAL; > + } > + osd_device->ops.get_layer_config(osd_device, > + layer->layer_info.id, cfg); > + > + vpbe_disp_calculate_scale_factor(disp_dev, layer, > + rect->width, > + rect->height); > + vpbe_disp_adj_position(disp_dev, layer, rect->top, > + rect->left); > + ret = osd_device->ops.set_layer_config(osd_device, > + layer->layer_info.id, cfg); > + if (ret < 0) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Error in set layer config:\n"); > + return -EINVAL; > + } > + > + /* apply zooming and h or v expansion */ > + osd_device->ops.set_zoom(osd_device, > + layer->layer_info.id, > + layer->layer_info.h_zoom, > + layer->layer_info.v_zoom); > + ret = osd_device->ops.set_vid_expansion(osd_device, > + layer->layer_info.h_exp, > + layer->layer_info.v_exp); > + if (ret < 0) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Error in set vid expansion:\n"); > + return -EINVAL; > + } > + > + if ((layer->layer_info.h_zoom != ZOOM_X1) || > + (layer->layer_info.v_zoom != ZOOM_X1) || > + (layer->layer_info.h_exp != H_EXP_OFF) || > + (layer->layer_info.v_exp != V_EXP_OFF)) > + /* Enable expansion filter */ > + osd_device->ops.set_interpolation_filter(osd_device, 1); > + else > + osd_device->ops.set_interpolation_filter(osd_device, 0); > + > + } else { > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); > + return -EINVAL; > + } > + > + return ret; > +} [snip] > +static int vpbe_display_g_fmt(struct file *file, void *priv, > + struct v4l2_format *fmt) > +{ > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_display_obj *layer = fh->layer; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "VIDIOC_G_FMT, layer id = %d\n", > + layer->device_id); > + > + /* If buffer type is video output */ > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > + /* Fill in the information about format */ > + *pixfmt = layer->pix_fmt; I don't see anything wrong in doing fmt->fmt.pix = layer->pix_fmt; directly. > + } else { > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int vpbe_display_enum_fmt(struct file *file, void *priv, > + struct v4l2_fmtdesc *fmt) > +{ > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_display_obj *layer = fh->layer; > + unsigned int index = 0; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "VIDIOC_ENUM_FMT, layer id = %d\n", > + layer->device_id); > + if (fmt->index > 0) { You support two formats below, should this be fmt->index > 1 ? > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n"); > + return -EINVAL; > + } > + > + /* Fill in the information about format */ > + index = fmt->index; > + memset(fmt, 0, sizeof(*fmt)); > + fmt->index = index; > + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; > + if (index == 0) { > + strcpy(fmt->description, "YUV 4:2:2 - UYVY"); > + fmt->pixelformat = V4L2_PIX_FMT_UYVY; > + } else if (index == 1) { A simple else will be enough. > + strcpy(fmt->description, "Y/CbCr 4:2:0"); > + fmt->pixelformat = V4L2_PIX_FMT_NV12; > + } > + return 0; > +} > + > +static int vpbe_display_s_fmt(struct file *file, void *priv, > + struct v4l2_format *fmt) > +{ > + int ret = 0; No need to initialize ret to 0. > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_display *disp_dev = video_drvdata(file); > + struct vpbe_display_obj *layer = fh->layer; > + struct osd_layer_config *cfg = &layer->layer_info.config; Variables are often declared in longuest to shortest line order in kernel drivers. It might not be a requirement though, but I find it to make code more readable. > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "VIDIOC_S_FMT, layer id = %d\n", > + layer->device_id); > + > + /* If streaming is started, return error */ > + if (layer->started) { I'm pretty sure there's a race condition here. > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > + return -EBUSY; > + } > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); > + return -EINVAL; > + } else { No need for an else as the first branch of the if returns. > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > + /* Check for valid pixel format */ > + ret = vpbe_try_format(disp_dev, pixfmt, 1); > + if (ret) > + return ret; > + > + /* YUV420 is requested, check availability of the > + other video window */ > + > + layer->pix_fmt = *pixfmt; There's probably a race condition here as well if two applications (or threads) call S_FMT at the same time. > + > + /* Get osd layer config */ > + osd_device->ops.get_layer_config(osd_device, > + layer->layer_info.id, cfg); > + /* Store the pixel format in the layer object */ > + cfg->xsize = pixfmt->width; > + cfg->ysize = pixfmt->height; > + cfg->line_length = pixfmt->bytesperline; > + cfg->ypos = 0; > + cfg->xpos = 0; > + cfg->interlaced = vpbe_dev->current_timings.interlaced; > + > + /* Change of the default pixel format for both video windows */ > + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { > + struct vpbe_display_obj *otherlayer; If the requested format isn't NV12, cfg->pixfmt won't be modified. If it has been set to NV12 by a previous S_FMT call, it won't become YUYV. Is that intentional ? > + cfg->pixfmt = PIXFMT_NV12; > + otherlayer = _vpbe_display_get_other_win(disp_dev, > + layer); > + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; > + } > + > + /* Set the layer config in the osd window */ > + ret = osd_device->ops.set_layer_config(osd_device, > + layer->layer_info.id, cfg); > + if (ret < 0) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Error in S_FMT params:\n"); > + return -EINVAL; > + } > + > + /* Readback and fill the local copy of current pix format */ > + osd_device->ops.get_layer_config(osd_device, > + layer->layer_info.id, cfg); > + > + /* verify if readback values are as expected */ > + if (layer->pix_fmt.width != cfg->xsize || > + layer->pix_fmt.height != cfg->ysize || > + layer->pix_fmt.bytesperline != layer->layer_info. > + config.line_length || (cfg->interlaced && > + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || > + (!cfg->interlaced && layer->pix_fmt.field != > + V4L2_FIELD_NONE)) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "mismatch:layer conf params:\n"); > + return -EINVAL; Can this happen ? If so, why ? > + } > + } > + > + return 0; > +} > + > +static int vpbe_display_try_fmt(struct file *file, void *priv, > + struct v4l2_format *fmt) > +{ > + struct vpbe_display *disp_dev = video_drvdata(file); > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); > + > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > + /* Check for valid field format */ > + return vpbe_try_format(disp_dev, pixfmt, 0); Other way around here as well please. Return an error if the type is not correct, and continue otherwise. > + } > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > + return -EINVAL; > +} > + > +/** > + * vpbe_display_s_std - Set the given standard in the encoder > + * > + * Sets the standard if supported by the current encoder. Return the > status. + * 0 - success & -EINVAL on error > + */ > +static int vpbe_display_s_std(struct file *file, void *priv, > + v4l2_std_id *std_id) > +{ > + struct vpbe_fh *fh = priv; > + struct vpbe_display_obj *layer = fh->layer; > + int ret = 0; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); > + > + /* If streaming is started, return error */ > + if (layer->started) { Race condition here too. > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > + return -EBUSY; > + } > + if (NULL != vpbe_dev->ops.s_std) { > + ret = vpbe_dev->ops.s_std(vpbe_dev, std_id); > + if (ret) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Failed to set standard for sub devices\n"); > + return -EINVAL; > + } > + } > + return 0; > +} > + > +/** > + * vpbe_display_g_std - Get the standard in the current encoder > + * > + * Get the standard in the current encoder. Return the status. 0 - success > + * -EINVAL on error > + */ > +static int vpbe_display_g_std(struct file *file, void *priv, > + v4l2_std_id *std_id) > +{ > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); > + > + /* Get the standard from the current encoder */ > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > + *std_id = vpbe_dev->current_timings.timings.std_id; > + return 0; > + } > + return -EINVAL; Where do you set timings_type ? When can this return an error ? > +} > + > +/** > + * vpbe_display_enum_output - enumerate outputs > + * > + * Enumerates the outputs available at the vpbe display > + * returns the status, -EINVAL if end of output list > + */ > +static int vpbe_display_enum_output(struct file *file, void *priv, > + struct v4l2_output *output) > +{ > + int ret = 0; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); > + > + /* Enumerate outputs */ > + > + if (NULL != vpbe_dev->ops.enum_outputs) { > + ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); > + if (ret) { > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "Failed to enumerate outputs\n"); > + return -EINVAL; > + } > + } > + return 0; Shouldn't this return an error if there's no enum_outputs operation ? > +} > + > +/** > + * vpbe_display_s_output - Set output to > + * the output specified by the index > + */ > +static int vpbe_display_s_output(struct file *file, void *priv, > + unsigned int i) > +{ > + struct vpbe_fh *fh = priv; > + struct vpbe_display_obj *layer = fh->layer; > + int ret = 0; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n"); > + /* If streaming is started, return error */ > + if (layer->started) { Race condition. > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > + return -EBUSY; > + } > + if (NULL != vpbe_dev->ops.set_output) { > + ret = vpbe_dev->ops.set_output(vpbe_dev, i); > + if (ret) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Failed to set output for sub devices\n"); > + return -EINVAL; > + } > + } > + return ret; Shouldn't this return an error if there's no set_output operation ? > +} [snip] > +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, > + struct vpbe_display *disp_dev) > +{ > + int err = 0; No need to initialize err to 0. > + struct osd_layer_config *layer_config; > + struct vpbe_display_obj *layer = disp_dev->dev[id]; > + struct osd_layer_config *cfg = &layer->layer_info.config; > + > + /* First claim the layer for this device */ > + err = osd_device->ops.request_layer(osd_device, > + layer->layer_info.id); > + if (err < 0) { > + /* Couldn't get layer */ > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Display Manager failed to allocate layer\n"); > + return -EBUSY; > + } > + > + layer_config = cfg; > + /* Set the default image and crop values */ > + layer_config->pixfmt = PIXFMT_YCbCrI; > + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; > + layer->pix_fmt.bytesperline = layer_config->line_length = > + vpbe_dev->current_timings.xres * 2; > + > + layer->pix_fmt.width = layer_config->xsize = > + vpbe_dev->current_timings.xres; > + layer->pix_fmt.height = layer_config->ysize = > + vpbe_dev->current_timings.yres; > + layer->pix_fmt.sizeimage = > + layer->pix_fmt.bytesperline * layer->pix_fmt.height; > + layer_config->xpos = 0; > + layer_config->ypos = 0; > + layer_config->interlaced = vpbe_dev->current_timings.interlaced; You shouldn't reinitialized the format every time the device is opened. The previously set format should be kept. > + > + /* > + * turn off ping-pong buffer and field inversion to fix > + * the image shaking problem in 1080I mode > + */ > + > + if (cfg->interlaced) > + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; > + else > + layer->pix_fmt.field = V4L2_FIELD_NONE; > + > + err = osd_device->ops.set_layer_config(osd_device, > + layer->layer_info.id, > + layer_config); > + if (err < 0) { > + /* Couldn't set layer */ > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Display Manager failed to set osd layer\n"); > + return -EBUSY; > + } > + > + return 0; > +} > + > +/* > + * vpbe_display_open() > + * It creates object of file handle structure and stores it in > private_data + * member of filepointer > + */ > +static int vpbe_display_open(struct file *file) > +{ > + int minor = iminor(file->f_path.dentry->d_inode); > + struct vpbe_display *disp_dev = video_drvdata(file); > + struct vpbe_display_obj *layer; > + struct vpbe_fh *fh = NULL; > + int found = -1; > + int i = 0; > + > + /* Check for valid minor number */ > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > + /* Get the pointer to the layer object */ > + layer = disp_dev->dev[i]; > + if (minor == layer->video_dev->minor) { > + found = i; > + break; > + } > + } Don't rely on minors, store the vpbe_display_obj object into the video device private data using video_set_drvdata() at registration time, and retrieve it here with video_drvdata(). > + > + /* If not found, return error no device */ > + if (0 > found) { > + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); > + return -ENODEV; > + } > + > + /* Allocate memory for the file handle object */ > + fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL); > + if (fh == NULL) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "unable to allocate memory for file handle object\n"); > + return -ENOMEM; > + } > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "vpbe display open plane = %d\n", > + layer->device_id); > + > + /* store pointer to fh in private_data member of filep */ > + file->private_data = fh; > + fh->layer = layer; > + fh->disp_dev = disp_dev; > + > + if (!layer->usrs) { > + /* Configure the default values for the layer */ > + if (vpbe_display_cfg_layer_default(layer->device_id, > + disp_dev)) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Unable to configure video layer" > + " for id = %d\n", layer->device_id); > + return -EINVAL; Memory leak, you need to free fh. > + } > + } > + /* Increment layer usrs counter */ > + layer->usrs++; Race condition. > + /* Set io_allowed member to false */ > + fh->io_allowed = 0; > + /* Initialize priority of this instance to default priority */ > + fh->prio = V4L2_PRIORITY_UNSET; > + v4l2_prio_open(&layer->prio, &fh->prio); > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "vpbe display device opened successfully\n"); > + return 0; > +} > + > +/* > + * vpbe_display_release() > + * This function deletes buffer queue, frees the buffers and the davinci > + * display file * handle > + */ > +static int vpbe_display_release(struct file *file) > +{ > + /* Get the layer object and file handle object */ > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_display_obj *layer = fh->layer; > + struct osd_layer_config *cfg = &layer->layer_info.config; > + struct vpbe_display *disp_dev = video_drvdata(file); > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); > + /* If this is doing IO and other layer are not closed */ > + if ((layer->usrs != 1) && fh->io_allowed) { > + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); > + return -EAGAIN; Don't do that. You can't deny a close() call because other file handles are still present. You need to handle that inside the driver. > + } > + > + /* if this instance is doing IO */ > + if (fh->io_allowed) { > + /* Reset io_usrs member of layer object */ > + layer->io_usrs = 0; > + > + osd_device->ops.disable_layer(osd_device, > + layer->layer_info.id); > + layer->started = 0; > + /* Free buffers allocated */ > + videobuf_queue_cancel(&layer->buffer_queue); > + videobuf_mmap_free(&layer->buffer_queue); > + } > + > + /* Decrement layer usrs counter */ > + layer->usrs--; > + /* If this file handle has initialize encoder device, reset it */ > + if (!layer->usrs) { > + if (cfg->pixfmt == PIXFMT_NV12) { > + struct vpbe_display_obj *otherlayer; > + otherlayer = > + _vpbe_display_get_other_win(disp_dev, layer); > + osd_device->ops.disable_layer(osd_device, > + otherlayer->layer_info.id); > + osd_device->ops.release_layer(osd_device, > + otherlayer->layer_info.id); > + } > + osd_device->ops.disable_layer(osd_device, > + layer->layer_info.id); > + osd_device->ops.release_layer(osd_device, > + layer->layer_info.id); > + } > + /* Close the priority */ > + v4l2_prio_close(&layer->prio, fh->prio); > + file->private_data = NULL; > + > + /* Free memory allocated to file handle object */ > + kfree(fh); > + > + disp_dev->cbcr_ofst = 0; > + > + return 0; > +} [snip] > +/* > + * vpbe_display_probe() > + * This function creates device entries by register itself to the V4L2 > driver + * and initializes fields of each layer objects > + */ > +static __devinit int vpbe_display_probe(struct platform_device *pdev) > +{ > + int i, j = 0, k, err = 0; > + struct vpbe_display *disp_dev; > + struct video_device *vbd = NULL; > + struct vpbe_display_obj *vpbe_display_layer = NULL; > + struct resource *res; > + int irq; > + > + printk(KERN_DEBUG "vpbe_display_probe\n"); > + > + /* Allocate memory for vpbe_display */ > + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); > + if (!disp_dev) { > + printk(KERN_ERR "ran out of memory\n"); > + return -ENOMEM; > + } > + > + /* Allocate memory for four plane display objects */ > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > + disp_dev->dev[i] = > + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); > + /* If memory allocation fails, return error */ > + if (!disp_dev->dev[i]) { > + printk(KERN_ERR "ran out of memory\n"); > + err = -ENOMEM; > + goto probe_out; > + } > + spin_lock_init(&disp_dev->dev[i]->irqlock); > + mutex_init(&disp_dev->dev[i]->opslock); > + } > + spin_lock_init(&disp_dev->dma_queue_lock); > + > + err = init_vpbe_layer_objects(i); > + if (err) { > + printk(KERN_ERR "Error initializing vpbe display\n"); > + return err; > + } > + > + /* > + * Scan all the platform devices to find the vpbe > + * controller device and get the vpbe_dev object > + */ > + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, > + vpbe_device_get); > + if (err < 0) > + return err; > + > + /* Initialize the vpbe display controller */ > + if (NULL != vpbe_dev->ops.initialize) { > + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); > + if (err) { > + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); > + err = -ENOMEM; > + goto probe_out; > + } > + } > + > + /* check the name of davinci device */ > + if (vpbe_dev->cfg->module_name != NULL) > + strcpy(vpbe_display_videocap.card, > + vpbe_dev->cfg->module_name); > + > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { Please move the content of this loop to a separate function (but not the loop itself), it will make the code more readable. > + /* Get the pointer to the layer object */ > + vpbe_display_layer = disp_dev->dev[i]; > + /* Allocate memory for video device */ > + vbd = video_device_alloc(); > + if (vbd == NULL) { > + for (j = 0; j < i; j++) { > + video_device_release( > + disp_dev->dev[j]->video_dev); > + } > + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); > + err = -ENOMEM; > + goto probe_out; > + } > + /* Initialize field of video device */ > + vbd->release = video_device_release; > + vbd->fops = &vpbe_fops; > + vbd->ioctl_ops = &vpbe_ioctl_ops; > + vbd->minor = -1; > + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; > + vbd->lock = &vpbe_display_layer->opslock; > + > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); > + vbd->current_norm = > + vpbe_dev->current_timings.timings.std_id; > + } else > + vbd->current_norm = 0; > + > + snprintf(vbd->name, sizeof(vbd->name), > + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", > + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, > + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, > + (VPBE_DISPLAY_VERSION_CODE) & 0xff); > + > + /* Set video_dev to the video device */ > + vpbe_display_layer->video_dev = vbd; > + vpbe_display_layer->device_id = i; > + > + vpbe_display_layer->layer_info.id = > + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); > + if (display_buf_config_params.numbuffers[i] == 0) > + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; > + else > + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; > + > + /* Initialize field of the display layer objects */ > + vpbe_display_layer->usrs = 0; > + vpbe_display_layer->io_usrs = 0; > + vpbe_display_layer->started = 0; > + > + /* Initialize prio member of layer object */ > + v4l2_prio_init(&vpbe_display_layer->prio); > + > + /* Register video device */ > + v4l2_info(&vpbe_dev->v4l2_dev, > + "Trying to register VPBE display device.\n"); > + v4l2_info(&vpbe_dev->v4l2_dev, > + "layer=%x,layer->video_dev=%x\n", > + (int)vpbe_display_layer, > + (int)&vpbe_display_layer->video_dev); > + > + err = video_register_device(vpbe_display_layer-> > + video_dev, > + VFL_TYPE_GRABBER, > + vpbe_display_nr[i]); > + if (err) > + goto probe_out; > + /* set the driver data in platform device */ > + platform_set_drvdata(pdev, disp_dev); > + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); > + } > + > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > + if (!res) { > + v4l2_err(&vpbe_dev->v4l2_dev, > + "Unable to get VENC interrupt resource\n"); > + err = -ENODEV; > + goto probe_out; > + } > + irq = res->start; > + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, > + disp_dev)) { > + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); > + err = -ENODEV; > + goto probe_out; > + } You probably want to get the resources and register the interrupt handler before registering the V4L2 devices, otherwise userspace will be able to open devices before you're done with the initialization. > + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 > device\n"); + return 0; > +probe_out: > + kfree(disp_dev); > + > + for (k = 0; k < j; k++) { > + /* Get the pointer to the layer object */ > + vpbe_display_layer = disp_dev->dev[k]; > + /* Unregister video device */ > + video_unregister_device(vpbe_display_layer->video_dev); > + /* Release video device */ > + video_device_release(vpbe_display_layer->video_dev); > + vpbe_display_layer->video_dev = NULL; > + } > + return err; > +} [snip] > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); Should this be "TI DM644x" instead ? > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Texas Instruments"); > diff --git a/include/media/davinci/vpbe_display.h > b/include/media/davinci/vpbe_display.h new file mode 100644 > index 0000000..d5cce40 > --- /dev/null > +++ b/include/media/davinci/vpbe_display.h > @@ -0,0 +1,146 @@ > +/* > + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation version 2. > + * > + * This program is distributed WITHOUT ANY WARRANTY of any > + * kind, whether express or implied; without even the implied warranty > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > +#ifndef VPBE_DISPLAY_H > +#define VPBE_DISPLAY_H > + > +#ifdef __KERNEL__ This is a kernel header, you don't need to guard it with #ifdef __KERNEL__ > + > +/* Header files */ > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define VPBE_DISPLAY_MAX_DEVICES 2 > + > +enum vpbe_display_device_id { > + VPBE_DISPLAY_DEVICE_0, > + VPBE_DISPLAY_DEVICE_1 > +}; > + > +#define VPBE_DISPLAY_DRV_NAME "vpbe-display" > + > +#define VPBE_DISPLAY_MAJOR_RELEASE 1 > +#define VPBE_DISPLAY_MINOR_RELEASE 0 > +#define VPBE_DISPLAY_BUILD 1 > +#define VPBE_DISPLAY_VERSION_CODE ((VPBE_DISPLAY_MAJOR_RELEASE << 16) | \ > + (VPBE_DISPLAY_MINOR_RELEASE << 8) | \ > + VPBE_DISPLAY_BUILD) > + > +#define VPBE_DISPLAY_VALID_FIELD(field) ((V4L2_FIELD_NONE == field) || \ > + (V4L2_FIELD_ANY == field) || (V4L2_FIELD_INTERLACED == field)) > + > +/* Exp ratio numerator and denominator constants */ > +#define VPBE_DISPLAY_H_EXP_RATIO_N (9) > +#define VPBE_DISPLAY_H_EXP_RATIO_D (8) > +#define VPBE_DISPLAY_V_EXP_RATIO_N (6) > +#define VPBE_DISPLAY_V_EXP_RATIO_D (5) > + > +/* Zoom multiplication factor */ > +#define VPBE_DISPLAY_ZOOM_4X (4) > +#define VPBE_DISPLAY_ZOOM_2X (2) No need for () around the constants. > + > +/* Structures */ > +struct display_layer_info { > + int enable; > + /* Layer ID used by Display Manager */ > + enum osd_layer id; > + struct osd_layer_config config; > + enum osd_zoom_factor h_zoom; > + enum osd_zoom_factor v_zoom; > + enum osd_h_exp_ratio h_exp; > + enum osd_v_exp_ratio v_exp; > +}; > + > +/* vpbe display object structure */ > +struct vpbe_display_obj { > + /* number of buffers in fbuffers */ > + unsigned int numbuffers; > + /* Pointer pointing to current v4l2_buffer */ > + struct videobuf_buffer *cur_frm; > + /* Pointer pointing to next v4l2_buffer */ > + struct videobuf_buffer *next_frm; > + /* videobuf specific parameters > + * Buffer queue used in video-buf > + */ > + struct videobuf_queue buffer_queue; > + /* Queue of filled frames */ > + struct list_head dma_queue; > + /* Used in video-buf */ > + spinlock_t irqlock; > + /* V4l2 specific parameters */ > + /* Identifies video device for this layer */ > + struct video_device *video_dev; > + /* This field keeps track of type of buffer exchange mechanism user > + * has selected > + */ > + enum v4l2_memory memory; > + /* Used to keep track of state of the priority */ > + struct v4l2_prio_state prio; > + /* Used to store pixel format */ > + struct v4l2_pix_format pix_fmt; > + enum v4l2_field buf_field; > + /* Video layer configuration params */ > + struct display_layer_info layer_info; > + /* vpbe specific parameters > + * enable window for display > + */ > + unsigned char window_enable; > + /* number of open instances of the layer */ > + unsigned int usrs; > + /* number of users performing IO */ > + unsigned int io_usrs; > + /* Indicates id of the field which is being displayed */ > + unsigned int field_id; > + /* Indicates whether streaming started */ > + unsigned char started; > + /* Identifies device object */ > + enum vpbe_display_device_id device_id; > + /* facilitation of ioctl ops lock by v4l2*/ > + struct mutex opslock; > +}; > + > +/* vpbe device structure */ > +struct vpbe_display { > + /* layer specific parameters */ > + /* lock for isr updates to buf layers*/ > + spinlock_t dma_queue_lock; > + /* C-Plane offset from start of y-plane */ > + unsigned int cbcr_ofst; > + struct vpbe_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; > +}; > + > +/* File handle structure */ > +struct vpbe_fh { > + /* vpbe device structure */ > + struct vpbe_display *disp_dev; > + /* pointer to layer object for opened device */ > + struct vpbe_display_obj *layer; > + /* Indicates whether this file handle is doing IO */ > + unsigned char io_allowed; > + /* Used to keep track priority of this instance */ > + enum v4l2_priority prio; > +}; > + > +struct buf_config_params { > + unsigned char min_numbuffers; > + unsigned char numbuffers[VPBE_DISPLAY_MAX_DEVICES]; > + unsigned int min_bufsize[VPBE_DISPLAY_MAX_DEVICES]; > + unsigned int layer_bufsize[VPBE_DISPLAY_MAX_DEVICES]; > +}; > + > +static int venc_is_second_field(void); If the function is static, there's no need to add it to the header. > +#endif /* end of __KERNEL__ */ > +#endif /* VPBE_DISPLAY_H */ > diff --git a/include/media/davinci/vpbe_types.h > b/include/media/davinci/vpbe_types.h new file mode 100644 > index 0000000..24a358b > --- /dev/null > +++ b/include/media/davinci/vpbe_types.h > @@ -0,0 +1,91 @@ > +/* > + * Copyright (C) 2010 Texas Instruments Inc > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation version 2. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > +#ifndef _VPBE_TYPES_H > +#define _VPBE_TYPES_H > + > +enum vpbe_types { > + VPBE_VERSION_1 = 1, > + VPBE_VERSION_2, > + VPBE_VERSION_3, > +}; Should it be renamed to enum vpbe_version ? > + > +/* vpbe_timing_type - Timing types used in vpbe device */ > +enum vpbe_enc_timings_type { > + VPBE_ENC_STD = 0x1, > + VPBE_ENC_DV_PRESET = 0x2, > + VPBE_ENC_CUSTOM_TIMINGS = 0x4, > + /* Used when set timings through FB device interface */ > + VPBE_ENC_TIMINGS_INVALID = 0x8, > +}; > + > +union vpbe_timings { > + v4l2_std_id std_id; > + unsigned int dv_preset; > +}; > + > +/* > + * struct vpbe_enc_mode_info > + * @name: ptr to name string of the standard, "NTSC", "PAL" etc > + * @std: standard or non-standard mode. 1 - standard, 0 - nonstandard > + * @interlaced: 1 - interlaced, 0 - non interlaced/progressive > + * @xres: x or horizontal resolution of the display > + * @yres: y or vertical resolution of the display > + * @fps: frame per second > + * @left_margin: left margin of the display > + * @right_margin: right margin of the display > + * @upper_margin: upper margin of the display > + * @lower_margin: lower margin of the display > + * @hsync_len: h-sync length > + * @vsync_len: v-sync length > + * @flags: bit field: bit usage is documented below > + * > + * Description: > + * Structure holding timing and resolution information of a standard. > + * Used by vpbe_device to set required non-standard timing in the > + * venc when lcd controller output is connected to a external encoder. > + * A table of timings is maintained in vpbe device to set this in > + * venc when external encoder is connected to lcd controller output. > + * Encoder may provide a g_dv_timings() API to override these values > + * as needed. > + * > + * Notes > + * ------ > + * if_type should be used only by encoder manager and encoder. > + * flags usage > + * b0 (LSB) - hsync polarity, 0 - negative, 1 - positive > + * b1 - vsync polarity, 0 - negative, 1 - positive > + * b2 - field id polarity, 0 - negative, 1 - positive > + */ > +struct vpbe_enc_mode_info { > + unsigned char *name; > + enum vpbe_enc_timings_type timings_type; > + union vpbe_timings timings; > + unsigned int interlaced; > + unsigned int xres; > + unsigned int yres; > + struct v4l2_fract aspect; > + struct v4l2_fract fps; > + unsigned int left_margin; > + unsigned int right_margin; > + unsigned int upper_margin; > + unsigned int lower_margin; > + unsigned int hsync_len; > + unsigned int vsync_len; > + unsigned int flags; > +}; > + > +#endif -- Regards, Laurent Pinchart From srk at ti.com Mon Apr 11 23:42:31 2011 From: srk at ti.com (Sriramakrishnan A G) Date: Tue, 12 Apr 2011 10:12:31 +0530 Subject: [PATCH] net: davinci_emac: fix spinlock bug with dma channel cleanup Message-ID: <1302583351-10449-1-git-send-email-srk@ti.com> The DMA cleanup function was holding the spinlock across a busy loop where it waits for HW to indicate teardown is complete. This generates a backtrace, when DEBUG_SPINLOCK is enabled. Make the locking more granular. Signed-off-by: Sriramakrishnan A G --- drivers/net/davinci_cpdma.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/drivers/net/davinci_cpdma.c b/drivers/net/davinci_cpdma.c index ae47f23..57fd0fc 100644 --- a/drivers/net/davinci_cpdma.c +++ b/drivers/net/davinci_cpdma.c @@ -824,6 +824,8 @@ int cpdma_chan_stop(struct cpdma_chan *chan) /* trigger teardown */ dma_reg_write(ctlr, chan->td, chan->chan_num); + spin_unlock_irqrestore(&chan->lock, flags); + /* wait for teardown complete */ timeout = jiffies + HZ/10; /* 100 msec */ while (time_before(jiffies, timeout)) { @@ -843,6 +845,7 @@ int cpdma_chan_stop(struct cpdma_chan *chan) } while ((ret & CPDMA_DESC_TD_COMPLETE) == 0); /* remaining packets haven't been tx/rx'ed, clean them up */ + spin_lock_irqsave(&chan->lock, flags); while (chan->head) { struct cpdma_desc __iomem *desc = chan->head; dma_addr_t next_dma; -- 1.6.2.4 From davem at davemloft.net Tue Apr 12 16:33:29 2011 From: davem at davemloft.net (David Miller) Date: Tue, 12 Apr 2011 14:33:29 -0700 (PDT) Subject: [PATCH] net: davinci_emac: fix spinlock bug with dma channel cleanup In-Reply-To: <1302583351-10449-1-git-send-email-srk@ti.com> References: <1302583351-10449-1-git-send-email-srk@ti.com> Message-ID: <20110412.143329.59682750.davem@davemloft.net> From: Sriramakrishnan A G Date: Tue, 12 Apr 2011 10:12:31 +0530 > The DMA cleanup function was holding the spinlock across > a busy loop where it waits for HW to indicate teardown is complete. > This generates a backtrace, when DEBUG_SPINLOCK is enabled. Make the > locking more granular. > > Signed-off-by: Sriramakrishnan A G You can't really sleep in this function at all. For example, it gets invoked from the ->ndo_tx_timeout() method in davinci_emac.c, which runs from a timer. So even if you hack the spinlock usage, there are still major issues here. We could conditionalize the timeout handling, but I don't think so much complexity is worth it here. Just do a udelay()/mdelay() or similar, instead of the timed sleeps. Thanks. From nsekhar at ti.com Wed Apr 13 09:30:19 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 13 Apr 2011 20:00:19 +0530 Subject: DM365 deep sleep In-Reply-To: References: <4D4C697F.2080400@criticallink.com> <4D52FBCF.3040702@criticallink.com> <4D553D78.3000404@ridgerun.com> Message-ID: Hi Todd, On Tue, Feb 15, 2011 at 18:11:03, Nori, Sekhar wrote: > Hi Todd, > > On Fri, Feb 11, 2011 at 19:15:28, Todd Fischer wrote: > > Sekhar, > > > > What is the status for deep sleep support on the DM365? If it is > > available, where can I find it? If not available, I was planning on > > AFAIK, DeepSleep support is not available on DM365. I am also > not aware of anyone working on it. > > > starting adding DM365 deep sleep support using the mach-davinci pm.c and > > sleep.S code from the latest davinci-linux git repo unless you have > > another suggestion. > > Yes, that's a good idea. Thanks for taking this up. If you are working on this can you also please take a look at the generic CPU suspend support that Russell pushed recently[1] and see how DaVinci can be converted to use it? The generic support is already included in 2.6.39. There is currently a lot of focus on clean-up within the ARM tree and any migration to generic code will be greatly appreciated. Thanks, Sekhar [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2011-February/041678.html From nsekhar at ti.com Wed Apr 13 10:10:37 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 13 Apr 2011 20:40:37 +0530 Subject: [PATCH 6/6] da8xx: enable the use of the ICPFUNC in i2c-davinci In-Reply-To: References: Message-ID: Hi Ben, On Wed, Apr 06, 2011 at 03:08:09, Ben Gardiner wrote: > Both the da850 and da830 have an I2C controller which has the ICPFUNC > registers. Indicate this for all da830 and da850 boards by setting the > has_pfunc flag true in the da8xx utility setup routine for registering the > I2C controller > > Signed-off-by: Ben Gardiner > Cc: Sekhar Nori > Cc: Ben Dooks > > --- > arch/arm/mach-davinci/devices-da8xx.c | 6 ++++++ > 1 files changed, 6 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > index beda8a4..da01558 100644 > --- a/arch/arm/mach-davinci/devices-da8xx.c > +++ b/arch/arm/mach-davinci/devices-da8xx.c > @@ -324,6 +324,12 @@ int __init da8xx_register_i2c(int instance, > else > return -EINVAL; > > + /* > + * Both the DA850 and DA830 have an I2C controller which has the > + * ICPFUNC et. al. registers > + */ > + pdata->has_pfunc = 1; The I2C driver implements a default platform data so it should actually be legal for a DA8x board to pass NULL platform data. In that case this line will crash. You should either check for pdata to be NULL or just let each board choose whether it needs recovery (I think the better option). Thanks, Sekhar From bengardiner at nanometrics.ca Wed Apr 13 10:22:05 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 13 Apr 2011 11:22:05 -0400 Subject: [PATCH 6/6] da8xx: enable the use of the ICPFUNC in i2c-davinci In-Reply-To: References: Message-ID: Hi Sekhar, On Wed, Apr 13, 2011 at 11:10 AM, Nori, Sekhar wrote: > Hi Ben, > > On Wed, Apr 06, 2011 at 03:08:09, Ben Gardiner wrote: >> Both the da850 and da830 have an I2C controller which has the ICPFUNC >> registers. Indicate this for all da830 and da850 boards by setting the >> has_pfunc flag true in the da8xx utility setup routine for registering the >> I2C controller >> >> Signed-off-by: Ben Gardiner >> Cc: Sekhar Nori >> Cc: Ben Dooks >> >> --- >> ?arch/arm/mach-davinci/devices-da8xx.c | ? ?6 ++++++ >> ?1 files changed, 6 insertions(+), 0 deletions(-) >> >> diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c >> index beda8a4..da01558 100644 >> --- a/arch/arm/mach-davinci/devices-da8xx.c >> +++ b/arch/arm/mach-davinci/devices-da8xx.c >> @@ -324,6 +324,12 @@ int __init da8xx_register_i2c(int instance, >> ? ? ? else >> ? ? ? ? ? ? ? return -EINVAL; >> >> + ? ? /* >> + ? ? ?* Both the DA850 and DA830 have an I2C controller which has the >> + ? ? ?* ICPFUNC et. al. registers >> + ? ? ?*/ >> + ? ? pdata->has_pfunc = 1; > > The I2C driver implements a default platform data > so it should actually be legal for a DA8x board to > pass NULL platform data. In that case this line > will crash. Good catch, thanks. > [...] You should either check for pdata to > be NULL or just let each board choose whether it > needs recovery (I think the better option). I understand "check for pdata to be NULL." If you think it is the better option I'd be happy to implement it but I don't understand how to implement "let each board choose whether it needs recovery." Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From nsekhar at ti.com Wed Apr 13 10:42:05 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 13 Apr 2011 21:12:05 +0530 Subject: [PATCH 6/6] da8xx: enable the use of the ICPFUNC in i2c-davinci In-Reply-To: References: Message-ID: On Wed, Apr 13, 2011 at 20:52:05, Ben Gardiner wrote: > >> --- a/arch/arm/mach-davinci/devices-da8xx.c > >> +++ b/arch/arm/mach-davinci/devices-da8xx.c > >> @@ -324,6 +324,12 @@ int __init da8xx_register_i2c(int instance, > >> ? ? ? else > >> ? ? ? ? ? ? ? return -EINVAL; > >> > >> + ? ? /* > >> + ? ? ?* Both the DA850 and DA830 have an I2C controller which has the > >> + ? ? ?* ICPFUNC et. al. registers > >> + ? ? ?*/ > >> + ? ? pdata->has_pfunc = 1; > > > > The I2C driver implements a default platform data > > so it should actually be legal for a DA8x board to > > pass NULL platform data. In that case this line > > will crash. > > Good catch, thanks. > > > [...] You should either check for pdata to > > be NULL or just let each board choose whether it > > needs recovery (I think the better option). > > I understand "check for pdata to be NULL." If you think it is the > better option I'd be happy to implement it but I don't understand how > to implement "let each board choose whether it needs recovery." This is done by just setting has_pfunc = 1 in the davinci_i2c_platform_data defined in the board file. Thanks, Sekhar From bengardiner at nanometrics.ca Wed Apr 13 11:03:39 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 13 Apr 2011 12:03:39 -0400 Subject: [PATCH 6/6] da8xx: enable the use of the ICPFUNC in i2c-davinci In-Reply-To: References: Message-ID: On Wed, Apr 13, 2011 at 11:42 AM, Nori, Sekhar wrote: > On Wed, Apr 13, 2011 at 20:52:05, Ben Gardiner wrote: >> I understand "check for pdata to be NULL." ?If you think it is the >> better option I'd be happy to implement it but I don't understand how >> to implement "let each board choose whether it needs recovery." > > This is done by just setting has_pfunc = 1 in the > davinci_i2c_platform_data defined in the board file. Ok -- now I get it, thanks. I spin a V2 shortly. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From nsekhar at ti.com Wed Apr 13 11:25:23 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 13 Apr 2011 21:55:23 +0530 Subject: [PATCH 0/6] i2c-davinci gpio pulsed SCL recovery with ICPFUNC In-Reply-To: References: Message-ID: Hi Ben, On Wed, Apr 06, 2011 at 03:08:03, Ben Gardiner wrote: > This series for the i2c-davinci driver implements both a register dump and > a pulsed-SCL recovery of the bus on those controllers that have ICPFUNC > registers which is, so far, the da850 and da830 based platforms. > > I2C "controller timed out" messages were being observed by both myself on the > da850evm and Bastian Ruppert on some custom da850 hardware [1]. Originally I > thought it was the existing pulse-SCL routine but was quickly proven wrong; my > apologies to John Povey and Philby John for jumping to conclusions. > > A discussion was spawned on the e2e forums [2] where Brad Griffis was > instrumental in the development of the recovery routine proposed by this patch > series. It was pointed out by him that the da850's i2c controller has the > ability to control the SDA and SCL pins as GPIOS through its ICPFUNC registers. > The recovery routine implemented by the patch series toggles the SCL pin and > reads the SDA state using the ICPFUNC registers. [...] > > When creating this series I noticed that there are obvious similarities between > the existing recovery routine implemented by Philby John and John Povey and the > recovery routine proposed in this series. Testing was performed using the > ICPFUNC regsiters to toggle SCL as prescribed by the existing routine and it > was found that the recovery did not work. The method described initially by Brad > Griffis had the initial state of SCL high and delays of 5us with a maximum of > 8 pulses with a check of SDA each time as compared to the existing routine with > undetermined SCL initial state, 20us delays and a maximum of 8 pulses. I tested I wonder how these values (5us or 20us) are derived. The document[1] referred to in the existing code says that "the master should send 9 clock pulses". I think 5us suggested by Brad assumes a 100KHz bus. If that is so, this number should be derived out of the bus_freq platform data. [1] http://www.nxp.com/documents/user_manual/UM10204.pdf > and found that if I changed the initial state of SCL or the number of pulses > (Bastian had success with 16) that the recovery did not occur as expected; Was this testing done with the original PinMuxed GPIO based approach or with the new internal GPIO based approach? > furthermore Brad pointed out that it was important to check the state of SDA > after each pulse to see if the slave had released the line. Indeed, adding the > check of SDA resulted in a quicker recovery. On my da850evm, at least, the > recovery took only one SCL pulse every time. Yes, the document referred to above suggests this too. I guess the existing recovery mechanism and the new ICPFUNC based approach shouldn't be functionally different - correct? In that case, it would really be worthwhile to fix the existing recovery method as well. > > It would be nice to consolidate the two recovery routines and -- since they > are gpio-based -- put the shared recovery routine in i2c-algo-bit so it can > also be used by bitbanging i2c masters. I did not undertake the former because > I don't have access to the hardware on which the existing recovery routine was > tested. I think if you see issues with existing code, you should just go ahead and fix. AFAICT, the existing approach should have worked for you. You can test on your hardware and those with other hardware can test your patches as well and send their acks. Thanks, Sekhar > I did not undertake the latter since 1) it seems premature until the > recovery routines are consolidated (or not) and 2) it would require more > changes of a broad scope which I feared might mire the review process of this > series which is, at its core, a functioning workaround of an observed problem > with da850evm hardware (plus some regsiter dumps). It is my hope that both the > consolidation of the recovery routines and the extraction to common bitbanging > code can be done in a later series. > > [1] http://permalink.gmane.org/gmane.linux.davinci/22291 > [2] http://e2e.ti.com/support/dsp/omap_applications_processors/f/42/p/99895/350610.aspx > > Ben Gardiner (6): > i2c-davinci: register dump before attempted bus recovery > i2c-davinci: convert davinci_i2c_platform_data to kerneldoc > i2c-davinci: introduce a dev-> function pointer for scl pulsing > i2c-davinci: use the DA8xx's ICPFUNC to toggle I2C as gpio > i2c-davinci: if device has pfunc register, dump that group also > da8xx: enable the use of the ICPFUNC in i2c-davinci > > arch/arm/mach-davinci/devices-da8xx.c | 6 + > arch/arm/mach-davinci/include/mach/i2c.h | 21 +++- > drivers/i2c/busses/i2c-davinci.c | 155 ++++++++++++++++++++++++++++-- > 3 files changed, 170 insertions(+), 12 deletions(-) > > From michael.williamson at criticallink.com Wed Apr 13 18:04:34 2011 From: michael.williamson at criticallink.com (Mike Williamson) Date: Wed, 13 Apr 2011 19:04:34 -0400 Subject: [PATCH 6/6] da8xx: enable the use of the ICPFUNC in i2c-davinci In-Reply-To: References: Message-ID: On Wed, Apr 13, 2011 at 11:10 AM, Nori, Sekhar wrote: > > Hi Ben, > > On Wed, Apr 06, 2011 at 03:08:09, Ben Gardiner wrote: > > Both the da850 and da830 have an I2C controller which has the ICPFUNC > > registers. Indicate this for all da830 and da850 boards by setting the > > has_pfunc flag true in the da8xx utility setup routine for registering the > > I2C controller > > > > Signed-off-by: Ben Gardiner > > Cc: Sekhar Nori > > Cc: Ben Dooks > > > > --- > > ?arch/arm/mach-davinci/devices-da8xx.c | ? ?6 ++++++ > > ?1 files changed, 6 insertions(+), 0 deletions(-) > > > > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > > index beda8a4..da01558 100644 > > --- a/arch/arm/mach-davinci/devices-da8xx.c > > +++ b/arch/arm/mach-davinci/devices-da8xx.c > > @@ -324,6 +324,12 @@ int __init da8xx_register_i2c(int instance, > > ? ? ? else > > ? ? ? ? ? ? ? return -EINVAL; > > > > + ? ? /* > > + ? ? ?* Both the DA850 and DA830 have an I2C controller which has the > > + ? ? ?* ICPFUNC et. al. registers > > + ? ? ?*/ > > + ? ? pdata->has_pfunc = 1; > > The I2C driver implements a default platform data > so it should actually be legal for a DA8x board to > pass NULL platform data. In that case this line > will crash. You should either check for pdata to > be NULL or just let each board choose whether it > needs recovery (I think the better option). > I actually had a problem with using NULL for pdata with davinci I2C and had submitted a patch here that sort of fell on the floor. The problem was that the i2c_davinci_calc_clk_dividers is using platform_data without a check as described above. So at the moment using NULL doesn't really work, best as I can tell... https://lkml.org/lkml/2010/9/4/119 FWIW. -Mike From nsekhar at ti.com Thu Apr 14 04:26:38 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 14 Apr 2011 14:56:38 +0530 Subject: [PATCH 6/6] da8xx: enable the use of the ICPFUNC in i2c-davinci In-Reply-To: References: Message-ID: Hi Mike, On Thu, Apr 14, 2011 at 04:34:34, Mike Williamson wrote: > > The I2C driver implements a default platform data > > so it should actually be legal for a DA8x board to > > pass NULL platform data. In that case this line > > will crash. You should either check for pdata to > > be NULL or just let each board choose whether it > > needs recovery (I think the better option). > > > > I actually had a problem with using NULL for pdata with davinci I2C > and had submitted a patch here that sort of fell on the floor. The > problem was that the i2c_davinci_calc_clk_dividers is using > platform_data without a check as described above. So at the moment > using NULL doesn't really work, best as I can tell... > > https://lkml.org/lkml/2010/9/4/119 Looking at this mail chain, it looks like Ben was actually OK with your first submission and just wanted you to update the patch description saying that dev->platform_data is accessed later in the code and that is why it needs to be updated. I liked your first attempt better and may be you should try another submission with the patch description updated. Thanks, Sekhar From nsekhar at ti.com Fri Apr 15 06:19:15 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 15 Apr 2011 16:49:15 +0530 Subject: [PATCH] DA830: fix SPI1 base address In-Reply-To: <201104062117.22238.sshtylyov@ru.mvista.com> References: <201104062117.22238.sshtylyov@ru.mvista.com> Message-ID: On Wed, Apr 06, 2011 at 22:47:21, Sergei Shtylyov wrote: > Commit 54ce6883d29630ff334bee4256a25e3f8719a181 (davinci: da8xx: add spi > resources and registration routine) wrongly assumed that SPI1 is mapped at > the same address on DA830/OMAP-L137 and DA850/OMAP-L138; actually, the base > address was valid only for the latter SoC. Teach the code to pass the correct > SPI1 memory resource for both SoCs... > > Signed-off-by: Sergei Shtylyov > > --- > The patch is against the recent DaVinci tree. It's needed in 2.6.39. Thanks Sergei, will queue this for 2.6.39 Regards, Sekhar From linux at arm.linux.org.uk Fri Apr 15 07:32:40 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 13:32:40 +0100 Subject: [PATCH] ARM: Davinci: Fix I2C build errors Message-ID: <20110415123240.GK1611@n2100.arm.linux.org.uk> Several Davinci platforms select the I2C EEPROM support, but don't select I2C support. This causes I2C EEPROM support to be built into the kernel, but I2C support may not be configured to be built in. This leads to linker errors due to missing I2C symbols. Arrange for I2C to be selected whenever EEPROM_AT24 is selected. Signed-off-by: Russell King --- arch/arm/mach-davinci/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 32f1479..c0deaca 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -63,6 +63,7 @@ config MACH_DAVINCI_EVM depends on ARCH_DAVINCI_DM644x select MISC_DEVICES select EEPROM_AT24 + select I2C help Configure this option to specify the whether the board used for development is a DM644x EVM @@ -72,6 +73,7 @@ config MACH_SFFSDR depends on ARCH_DAVINCI_DM644x select MISC_DEVICES select EEPROM_AT24 + select I2C help Say Y here to select the Lyrtech Small Form Factor Software Defined Radio (SFFSDR) board. @@ -105,6 +107,7 @@ config MACH_DAVINCI_DM6467_EVM select MACH_DAVINCI_DM6467TEVM select MISC_DEVICES select EEPROM_AT24 + select I2C help Configure this option to specify the whether the board used for development is a DM6467 EVM @@ -118,6 +121,7 @@ config MACH_DAVINCI_DM365_EVM depends on ARCH_DAVINCI_DM365 select MISC_DEVICES select EEPROM_AT24 + select I2C help Configure this option to specify whether the board used for development is a DM365 EVM @@ -129,6 +133,7 @@ config MACH_DAVINCI_DA830_EVM select GPIO_PCF857X select MISC_DEVICES select EEPROM_AT24 + select I2C help Say Y here to select the TI DA830/OMAP-L137/AM17x Evaluation Module. @@ -205,6 +210,7 @@ config MACH_MITYOMAPL138 depends on ARCH_DAVINCI_DA850 select MISC_DEVICES select EEPROM_AT24 + select I2C help Say Y here to select the Critical Link MityDSP-L138/MityARM-1808 System on Module. Information on this SoM may be found at From linux at arm.linux.org.uk Fri Apr 15 07:39:24 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 13:39:24 +0100 Subject: [PATCH] ARM: Davinci: Fix I2C build errors In-Reply-To: <20110415123240.GK1611@n2100.arm.linux.org.uk> References: <20110415123240.GK1611@n2100.arm.linux.org.uk> Message-ID: <20110415123924.GL1611@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 01:32:40PM +0100, Russell King - ARM Linux wrote: > Several Davinci platforms select the I2C EEPROM support, but don't > select I2C support. This causes I2C EEPROM support to be built into > the kernel, but I2C support may not be configured to be built in. > This leads to linker errors due to missing I2C symbols. > > Arrange for I2C to be selected whenever EEPROM_AT24 is selected. Although this fixes I2C build errors, it leaves these errors: arch/arm/mach-davinci/built-in.o: In function `phy_read': include/linux/phy.h:454: undefined reference to `mdiobus_read' arch/arm/mach-davinci/built-in.o: In function `phy_write': include/linux/phy.h:469: undefined reference to `mdiobus_write' arch/arm/mach-davinci/built-in.o: In function `davinci_evm_init': arch/arm/mach-davinci/board-dm644x-evm.c:708: undefined reference to `phy_register_fixup_for_uid' which also look like a configuration problem. From nsekhar at ti.com Fri Apr 15 07:58:52 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 15 Apr 2011 18:28:52 +0530 Subject: [PATCH 1/3] DA8xx: kill duplicate #define DA8XX_GPIO_BASE In-Reply-To: <201104062124.31206.sshtylyov@ru.mvista.com> References: <201104062124.31206.sshtylyov@ru.mvista.com> Message-ID: On Wed, Apr 06, 2011 at 22:54:31, Sergei Shtylyov wrote: > DA8XX_GPIO_BASE is #define'd in both and devices-da8xx.c; > moreover, it's not even used in the latter file... > > Signed-off-by: Sergei Shtylyov Thanks Sergei for the clean-up. Will queue these three for the next merge window. Thanks, Sekhar From linux at arm.linux.org.uk Fri Apr 15 08:06:07 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 14:06:07 +0100 Subject: [RFC PATCH] Consolidate SRAM support Message-ID: <20110415130607.GM1611@n2100.arm.linux.org.uk> This is work in progress. We have two SoCs using SRAM, both with their own allocation systems, and both with their own ways of copying functions into the SRAM. Let's unify this before we have additional SoCs re-implementing this obviously common functionality themselves. Unfortunately, we end up with code growth through doing this, but that will become a win when we have another SoC using this (which I know there's at least one in the pipeline). One of the considerations here is that we can easily convert sram-pool.c to hook into device tree stuff, which can tell the sram allocator: - physical address of sram - size of sram - allocation granularity and then we just need to ensure that it is appropriately mapped. This uses the physical address, and unlike Davinci's dma address usage, it always wants to have the physical address, and will always return the corresponding physical address when passed that pointer. OMAP could probably do with some more work to make the omapfb and other allocations use the sram allocator, rather than hooking in before the sram allocator is initialized - and then further cleanups so that we have an initialization function which just does sram_create(phys, size) virt = map sram(phys, size) create sram pool(virt, phys, size, min_alloc_order) Another question is whether we should allow multiple SRAM pools or not - this code does allow multiple pools, but so far we only have one pool per SoC. Overdesign? Maybe, but it prevents SoCs wanting to duplicate it if they want to partition the SRAM, or have peripheral-local SRAMs. Lastly, uio_pruss should probably take the SRAM pool pointer via platform data so that it doesn't have to include Davinci specific includes. Signed-off-by: Russell King --- arch/arm/Kconfig | 2 + arch/arm/common/Kconfig | 4 ++ arch/arm/common/Makefile | 1 + arch/arm/common/sram-pool.c | 69 +++++++++++++++++++++++++++ arch/arm/include/asm/sram-pool.h | 20 ++++++++ arch/arm/mach-davinci/da850.c | 2 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 2 +- arch/arm/mach-davinci/dm644x.c | 2 +- arch/arm/mach-davinci/dm646x.c | 2 +- arch/arm/mach-davinci/include/mach/common.h | 2 +- arch/arm/mach-davinci/include/mach/sram.h | 13 +---- arch/arm/mach-davinci/pm.c | 12 +---- arch/arm/mach-davinci/sram.c | 42 +++-------------- arch/arm/plat-omap/include/plat/sram.h | 17 ++++--- arch/arm/plat-omap/sram.c | 34 +++++--------- drivers/uio/uio_pruss.c | 8 ++- 17 files changed, 141 insertions(+), 93 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5b9f78b..5c3401c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -850,6 +850,7 @@ config ARCH_DAVINCI bool "TI DaVinci" select GENERIC_CLOCKEVENTS select ARCH_REQUIRE_GPIOLIB + select ARM_SRAM_POOL select ZONE_DMA select HAVE_IDE select CLKDEV_LOOKUP @@ -863,6 +864,7 @@ config ARCH_OMAP select HAVE_CLK select ARCH_REQUIRE_GPIOLIB select ARCH_HAS_CPUFREQ + select ARM_SRAM_POOL select GENERIC_CLOCKEVENTS select HAVE_SCHED_CLOCK select ARCH_HAS_HOLES_MEMORYMODEL diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index ea5ee4d..ddbd20b 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -39,3 +39,7 @@ config SHARP_PARAM config SHARP_SCOOP bool + +config ARM_SRAM_POOL + bool + select GENERIC_ALLOCATOR diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index e7521bca..b79ad68 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_COMMON_CLKDEV) += clkdev.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_ARM_SRAM_POOL) += sram-pool.o diff --git a/arch/arm/common/sram-pool.c b/arch/arm/common/sram-pool.c new file mode 100644 index 0000000..9ff1466 --- /dev/null +++ b/arch/arm/common/sram-pool.c @@ -0,0 +1,69 @@ +/* + * Unified SRAM allocator, based on mach-davinci/sram.c, which was + * Copyright (C) 2009 David Brownell. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include + +#include + +struct sram_pool { + struct gen_pool *genpool; + void *cpu_base; + phys_addr_t phys_base; +}; + +void *sram_pool_alloc(struct sram_pool *pool, size_t len, phys_addr_t *phys) +{ + void *addr = (void *)gen_pool_alloc(pool->genpool, len); + + if (phys) + *phys = addr ? (pool->phys_base + (addr - pool->cpu_base)) : + (phys_addr_t)-1ULL; + + return addr; +} +EXPORT_SYMBOL_GPL(sram_pool_alloc); + +void sram_pool_free(struct sram_pool *pool, void *addr, size_t len) +{ + gen_pool_free(pool->genpool, (unsigned long)addr, len); +} +EXPORT_SYMBOL_GPL(sram_pool_free); + +struct sram_pool *sram_pool_create(void *addr, phys_addr_t phys, size_t len, + int min_alloc_order) +{ + struct sram_pool *pool = kzalloc(sizeof(struct sram_pool), GFP_KERNEL); + + if (pool) { + pool->cpu_base = addr; + pool->phys_base = phys; + pool->genpool = gen_pool_create(min_alloc_order, -1); + if (!pool->genpool) { + kfree(pool); + pool = NULL; + } else { + WARN_ON(gen_pool_add(pool->genpool, (unsigned long)addr, + len, -1) < 0); + } + } + + return pool; +} +EXPORT_SYMBOL_GPL(sram_pool_create); + +void sram_pool_destroy(struct sram_pool *pool) +{ + gen_pool_destroy(pool->genpool); + kfree(pool); +} +EXPORT_SYMBOL_GPL(sram_pool_destroy); diff --git a/arch/arm/include/asm/sram-pool.h b/arch/arm/include/asm/sram-pool.h new file mode 100644 index 0000000..b7ae871 --- /dev/null +++ b/arch/arm/include/asm/sram-pool.h @@ -0,0 +1,20 @@ +#ifndef __ASMARM_SRAM_POOL_H +#define __ASMARM_SRAM_POOL_H + +#include + +struct sram_pool; + +void *sram_pool_alloc(struct sram_pool *, size_t, phys_addr_t *); +void sram_pool_free(struct sram_pool *, void *, size_t); +struct sram_pool *sram_pool_create(void *, phys_addr_t, size_t, int); +void sram_pool_destroy(struct sram_pool *); + +/* Macro to copy a function into SRAM, using the fncpy API */ +#define sram_pool_fncpy(pool, funcp, size) ({ \ + size_t _sz = size; \ + void *_sram = sram_pool_alloc(pool, _sz, NULL); \ + (_sram ? fncpy(_sram, &(funcp), _sz) : NULL); \ +}) + +#endif diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 68fe4c2..5eca128 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1099,7 +1099,7 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .gpio_irq = IRQ_DA8XX_GPIO0, .serial_dev = &da8xx_serial_device, .emac_pdata = &da8xx_emac_pdata, - .sram_dma = DA8XX_ARM_RAM_BASE, + .sram_phys = DA8XX_ARM_RAM_BASE, .sram_len = SZ_8K, .reset_device = &da8xx_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 76364d1..3df8730 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -850,7 +850,7 @@ static struct davinci_soc_info davinci_soc_info_dm355 = { .gpio_num = 104, .gpio_irq = IRQ_DM355_GPIOBNK0, .serial_dev = &dm355_serial_device, - .sram_dma = 0x00010000, + .sram_phys = 0x00010000, .sram_len = SZ_32K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 4604e72..d306034 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -1082,7 +1082,7 @@ static struct davinci_soc_info davinci_soc_info_dm365 = { .gpio_unbanked = 8, /* really 16 ... skip muxed GPIOs */ .serial_dev = &dm365_serial_device, .emac_pdata = &dm365_emac_pdata, - .sram_dma = 0x00010000, + .sram_phys = 0x00010000, .sram_len = SZ_32K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 9a2376b..4ca7295 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -764,7 +764,7 @@ static struct davinci_soc_info davinci_soc_info_dm644x = { .gpio_irq = IRQ_GPIOBNK0, .serial_dev = &dm644x_serial_device, .emac_pdata = &dm644x_emac_pdata, - .sram_dma = 0x00008000, + .sram_phys = 0x00008000, .sram_len = SZ_16K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..a4365f7 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -848,7 +848,7 @@ static struct davinci_soc_info davinci_soc_info_dm646x = { .gpio_irq = IRQ_DM646X_GPIOBNK0, .serial_dev = &dm646x_serial_device, .emac_pdata = &dm646x_emac_pdata, - .sram_dma = 0x10010000, + .sram_phys = 0x10010000, .sram_len = SZ_32K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index a57cba2..665d049 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -75,7 +75,7 @@ struct davinci_soc_info { int gpio_ctlrs_num; struct platform_device *serial_dev; struct emac_platform_data *emac_pdata; - dma_addr_t sram_dma; + phys_addr_t sram_phys; unsigned sram_len; struct platform_device *reset_device; void (*reset)(struct platform_device *); diff --git a/arch/arm/mach-davinci/include/mach/sram.h b/arch/arm/mach-davinci/include/mach/sram.h index 111f7cc..7d77e85 100644 --- a/arch/arm/mach-davinci/include/mach/sram.h +++ b/arch/arm/mach-davinci/include/mach/sram.h @@ -10,18 +10,11 @@ #ifndef __MACH_SRAM_H #define __MACH_SRAM_H +#include + /* ARBITRARY: SRAM allocations are multiples of this 2^N size */ #define SRAM_GRANULARITY 512 -/* - * SRAM allocations return a CPU virtual address, or NULL on error. - * If a DMA address is requested and the SRAM supports DMA, its - * mapped address is also returned. - * - * Errors include SRAM memory not being available, and requesting - * DMA mapped SRAM on systems which don't allow that. - */ -extern void *sram_alloc(size_t len, dma_addr_t *dma); -extern void sram_free(void *addr, size_t len); +extern struct sram_pool *davinci_sram_pool; #endif /* __MACH_SRAM_H */ diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c index 1bd73a0..f69cd7b 100644 --- a/arch/arm/mach-davinci/pm.c +++ b/arch/arm/mach-davinci/pm.c @@ -29,12 +29,6 @@ static void (*davinci_sram_suspend) (struct davinci_pm_config *); static struct davinci_pm_config *pdata; -static void davinci_sram_push(void *dest, void *src, unsigned int size) -{ - memcpy(dest, src, size); - flush_icache_range((unsigned long)dest, (unsigned long)(dest + size)); -} - static void davinci_pm_suspend(void) { unsigned val; @@ -123,15 +117,13 @@ static int __init davinci_pm_probe(struct platform_device *pdev) return -ENOENT; } - davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); + davinci_sram_suspend = sram_pool_fncpy(davinci_sram_pool, + davinci_cpu_suspend, davinci_cpu_suspend_sz); if (!davinci_sram_suspend) { dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); return -ENOMEM; } - davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, - davinci_cpu_suspend_sz); - suspend_set_ops(&davinci_pm_ops); return 0; diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c index db0f778..ebd4d67 100644 --- a/arch/arm/mach-davinci/sram.c +++ b/arch/arm/mach-davinci/sram.c @@ -10,40 +10,13 @@ */ #include #include -#include +#include #include #include -static struct gen_pool *sram_pool; - -void *sram_alloc(size_t len, dma_addr_t *dma) -{ - unsigned long vaddr; - dma_addr_t dma_base = davinci_soc_info.sram_dma; - - if (dma) - *dma = 0; - if (!sram_pool || (dma && !dma_base)) - return NULL; - - vaddr = gen_pool_alloc(sram_pool, len); - if (!vaddr) - return NULL; - - if (dma) - *dma = dma_base + (vaddr - SRAM_VIRT); - return (void *)vaddr; - -} -EXPORT_SYMBOL(sram_alloc); - -void sram_free(void *addr, size_t len) -{ - gen_pool_free(sram_pool, (unsigned long) addr, len); -} -EXPORT_SYMBOL(sram_free); - +struct sram_pool *davinci_sram_pool; +EXPORT_SYMBOL_GPL(davinci_sram_pool); /* * REVISIT This supports CPU and DMA access to/from SRAM, but it @@ -58,13 +31,12 @@ static int __init sram_init(void) if (len) { len = min_t(unsigned, len, SRAM_SIZE); - sram_pool = gen_pool_create(ilog2(SRAM_GRANULARITY), -1); - if (!sram_pool) + davinci_sram_pool = sram_pool_create((void *)SRAM_VIRT, + davinci_soc_info.sram_phys, len, + ilog2(SRAM_GRANULARITY)); + if (!davinci_sram_pool) status = -ENOMEM; } - if (sram_pool) - status = gen_pool_add(sram_pool, SRAM_VIRT, len, -1); - WARN_ON(status < 0); return status; } core_initcall(sram_init); diff --git a/arch/arm/plat-omap/include/plat/sram.h b/arch/arm/plat-omap/include/plat/sram.h index f500fc3..9fc27d0 100644 --- a/arch/arm/plat-omap/include/plat/sram.h +++ b/arch/arm/plat-omap/include/plat/sram.h @@ -12,16 +12,19 @@ #define __ARCH_ARM_OMAP_SRAM_H #ifndef __ASSEMBLY__ -#include +#include -extern void *omap_sram_push_address(unsigned long size); +extern struct sram_pool *omap_sram_pool; -/* Macro to push a function to the internal SRAM, using the fncpy API */ +/* + * Note that fncpy requires the SRAM address to be aligned to an 8-byte + * boundary, so the min_alloc_order for the pool is set appropriately. + */ #define omap_sram_push(funcp, size) ({ \ - typeof(&(funcp)) _res = NULL; \ - void *_sram_address = omap_sram_push_address(size); \ - if (_sram_address) \ - _res = fncpy(_sram_address, &(funcp), size); \ + typeof(&(funcp)) _res; \ + _res = sram_pool_fncpy(omap_sram_pool, funcp, size); \ + if (!_res) \ + pr_err("Not enough space in SRAM\n"); \ _res; \ }) diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index a3f50b3..3588749 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -75,7 +75,6 @@ static unsigned long omap_sram_start; static unsigned long omap_sram_base; static unsigned long omap_sram_size; -static unsigned long omap_sram_ceil; /* * Depending on the target RAMFS firewall setup, the public usable amount of @@ -104,6 +103,8 @@ static int is_sram_locked(void) return 1; /* assume locked with no PPA or security driver */ } +struct sram_pool *omap_sram_pool; + /* * The amount of SRAM depends on the core type. * Note that we cannot try to test for SRAM here because writes @@ -182,7 +183,16 @@ static void __init omap_detect_sram(void) omap_sram_size - SRAM_BOOTLOADER_SZ); omap_sram_size -= reserved; - omap_sram_ceil = omap_sram_base + omap_sram_size; + { + /* The first SRAM_BOOTLOADER_SZ of SRAM are reserved */ + void *base = (void *)omap_sram_base + SRAM_BOOTLOADER_SZ; + phys_addr_t phys = omap_sram_start + SRAM_BOOTLOADER_SZ; + size_t len = omap_sram_size - SRAM_BOOTLOADER_SZ; + + omap_sram_pool = sram_pool_create(base, phys, len, + ilog2(FNCPY_ALIGN)); + WARN_ON(!omap_sram_pool); + } } static struct map_desc omap_sram_io_desc[] __initdata = { @@ -242,26 +252,6 @@ static void __init omap_map_sram(void) omap_sram_size - SRAM_BOOTLOADER_SZ); } -/* - * Memory allocator for SRAM: calculates the new ceiling address - * for pushing a function using the fncpy API. - * - * Note that fncpy requires the returned address to be aligned - * to an 8-byte boundary. - */ -void *omap_sram_push_address(unsigned long size) -{ - if (size > (omap_sram_ceil - (omap_sram_base + SRAM_BOOTLOADER_SZ))) { - printk(KERN_ERR "Not enough space in SRAM\n"); - return NULL; - } - - omap_sram_ceil -= size; - omap_sram_ceil = ROUND_DOWN(omap_sram_ceil, FNCPY_ALIGN); - - return (void *)omap_sram_ceil; -} - #ifdef CONFIG_ARCH_OMAP1 static void (*_omap_sram_reprogram_clock)(u32 dpllctl, u32 ckctl); diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index daf6e77..2be3155 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -62,7 +62,7 @@ MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate"); struct uio_pruss_dev { struct uio_info *info; struct clk *pruss_clk; - dma_addr_t sram_paddr; + phys_addr_t sram_paddr; dma_addr_t ddr_paddr; void __iomem *prussio_vaddr; void *sram_vaddr; @@ -106,7 +106,8 @@ static void pruss_cleanup(struct platform_device *dev, gdev->ddr_paddr); } if (gdev->sram_vaddr) - sram_free(gdev->sram_vaddr, sram_pool_sz); + sram_pool_free(davinci_sram_pool, gdev->sram_vaddr, + sram_pool_sz); kfree(gdev->info); clk_put(gdev->pruss_clk); kfree(gdev); @@ -152,7 +153,8 @@ static int __devinit pruss_probe(struct platform_device *dev) goto out_free; } - gdev->sram_vaddr = sram_alloc(sram_pool_sz, &(gdev->sram_paddr)); + gdev->sram_vaddr = sram_pool_alloc(davinci_sram_pool, sram_pool_sz, + &(gdev->sram_paddr)); if (!gdev->sram_vaddr) { dev_err(&dev->dev, "Could not allocate SRAM pool\n"); goto out_free; From nsekhar at ti.com Fri Apr 15 09:39:23 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 15 Apr 2011 20:09:23 +0530 Subject: [PATCH] ARM: Davinci: Fix I2C build errors In-Reply-To: <20110415123924.GL1611@n2100.arm.linux.org.uk> References: <20110415123240.GK1611@n2100.arm.linux.org.uk> <20110415123924.GL1611@n2100.arm.linux.org.uk> Message-ID: Hi Russell, On Fri, Apr 15, 2011 at 18:09:24, Russell King - ARM Linux wrote: > On Fri, Apr 15, 2011 at 01:32:40PM +0100, Russell King - ARM Linux wrote: > > Several Davinci platforms select the I2C EEPROM support, but don't > > select I2C support. This causes I2C EEPROM support to be built into > > the kernel, but I2C support may not be configured to be built in. > > This leads to linker errors due to missing I2C symbols. > > > > Arrange for I2C to be selected whenever EEPROM_AT24 is selected. > > Although this fixes I2C build errors, it leaves these errors: > > arch/arm/mach-davinci/built-in.o: In function `phy_read': > include/linux/phy.h:454: undefined reference to `mdiobus_read' > arch/arm/mach-davinci/built-in.o: In function `phy_write': > include/linux/phy.h:469: undefined reference to `mdiobus_write' > arch/arm/mach-davinci/built-in.o: In function `davinci_evm_init': > arch/arm/mach-davinci/board-dm644x-evm.c:708: undefined reference to `phy_register_fixup_for_uid' > > which also look like a configuration problem. Will send out a fix for this. Thanks, Sekhar From arnd at arndb.de Fri Apr 15 09:40:00 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Fri, 15 Apr 2011 16:40:00 +0200 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <4DA84AAB.60601@gmail.com> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> Message-ID: <201104151640.01040.arnd@arndb.de> On Friday 15 April 2011 15:39:55 Rob Herring wrote: > On 04/15/2011 08:06 AM, Russell King - ARM Linux wrote: > > This is work in progress. > > > > We have two SoCs using SRAM, both with their own allocation systems, > > and both with their own ways of copying functions into the SRAM. > > It's more than that. Several i.MX chips use plat-mxc/iram_alloc.c. There are also non-ARM systems doing something like that, arch/blackfin/mm/sram-alloc.c comes to mind. On powerpc, the ppc7410 also has this feature in hardware, but I believe it was never supported in mainline Linux. How about putting sram-pool.c into the top-level mm/ directory and sram-pool.h into include/linux? They seem to be completely generic to me, aside from the dependency on asm/fncpy.h which should probably remain arch specific. As long as CONFIG_ARM_SRAM_POOL (or perhaps just CONFIG_SRAM_POOL) is selected only by platforms that support it, the dependency should not hurt. Arnd From linux at arm.linux.org.uk Fri Apr 15 10:24:43 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 16:24:43 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <20110415135237.GA9513@zarco> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110415135237.GA9513@zarco> Message-ID: <20110415152443.GB4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 04:52:38PM +0300, Eduardo Valentin wrote: > Hi Russel, > > Just small comment, > > On Fri, Apr 15, 2011 at 08:06:07AM -0500, Russell King wrote: > > > diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c > > index a3f50b3..3588749 100644 > > --- a/arch/arm/plat-omap/sram.c > > +++ b/arch/arm/plat-omap/sram.c > > @@ -75,7 +75,6 @@ > > static unsigned long omap_sram_start; > > static unsigned long omap_sram_base; > > static unsigned long omap_sram_size; > > -static unsigned long omap_sram_ceil; > > I think you missed one occurrence of omap_sram_ceil at omap3_sram_restore_context. > Probably you have compile-tested w/o CONFIG_PM? I built OMAP2 only, which did have CONFIG_PM=y. Welcome to the problems of ifdef'ing code. From linux at arm.linux.org.uk Fri Apr 15 10:26:43 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 16:26:43 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <201104151640.01040.arnd@arndb.de> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> <201104151640.01040.arnd@arndb.de> Message-ID: <20110415152643.GC4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 04:40:00PM +0200, Arnd Bergmann wrote: > On Friday 15 April 2011 15:39:55 Rob Herring wrote: > > > On 04/15/2011 08:06 AM, Russell King - ARM Linux wrote: > > > This is work in progress. > > > > > > We have two SoCs using SRAM, both with their own allocation systems, > > > and both with their own ways of copying functions into the SRAM. > > > > It's more than that. Several i.MX chips use plat-mxc/iram_alloc.c. > > There are also non-ARM systems doing something like that, > arch/blackfin/mm/sram-alloc.c comes to mind. On powerpc, the > ppc7410 also has this feature in hardware, but I believe it was > never supported in mainline Linux. Yes, but there's some horrible bits out there. PPC is one of them which looks like it doesn't lend itself to consolidating with this kind of implementation. > How about putting sram-pool.c into the top-level mm/ directory > and sram-pool.h into include/linux? They seem to be completely > generic to me, aside from the dependency on asm/fncpy.h which > should probably remain arch specific. I've thought about lib - but first lets see whether other architectures want to use it first. I've asked the sh folk off-list about this and waiting for a reply. Blackfin and PPC look horrible to sort out though, so they can come at a later date if they so wish. From grant.likely at secretlab.ca Fri Apr 15 10:32:01 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Fri, 15 Apr 2011 09:32:01 -0600 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <20110415152643.GC4423@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> <201104151640.01040.arnd@arndb.de> <20110415152643.GC4423@n2100.arm.linux.org.uk> Message-ID: On Fri, Apr 15, 2011 at 9:26 AM, Russell King - ARM Linux wrote: > On Fri, Apr 15, 2011 at 04:40:00PM +0200, Arnd Bergmann wrote: >> On Friday 15 April 2011 15:39:55 Rob Herring wrote: >> >> > On 04/15/2011 08:06 AM, Russell King - ARM Linux wrote: >> > > This is work in progress. >> > > >> > > We have two SoCs using SRAM, both with their own allocation systems, >> > > and both with their own ways of copying functions into the SRAM. >> > >> > It's more than that. Several i.MX chips use plat-mxc/iram_alloc.c. >> >> There are also non-ARM systems doing something like that, >> arch/blackfin/mm/sram-alloc.c comes to mind. On powerpc, the >> ppc7410 also has this feature in hardware, but I believe it was >> never supported in mainline Linux. > > Yes, but there's some horrible bits out there. ?PPC is one of them > which looks like it doesn't lend itself to consolidating with this > kind of implementation. > >> How about putting sram-pool.c into the top-level mm/ directory >> and sram-pool.h into include/linux? They seem to be completely >> generic to me, aside from the dependency on asm/fncpy.h which >> should probably remain arch specific. > > I've thought about lib - but first lets see whether other architectures > want to use it first. ?I've asked the sh folk off-list about this and > waiting for a reply. > > Blackfin and PPC look horrible to sort out though, so they can come at > a later date if they so wish. Yes, once the infrastructure is in place, powerpc can do its own migration to the new code. I vote for putting it in lib at the outset. g. From linux at arm.linux.org.uk Fri Apr 15 10:37:23 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 16:37:23 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <4DA85B49.60209@vollmann.ch> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA85B49.60209@vollmann.ch> Message-ID: <20110415153723.GD4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 04:50:49PM +0200, Detlef Vollmann wrote: > I'd love to have the mapping inside the create pool, but that might > not be possible in general. No, think about it. What if you need to map the RAM area with some special attributes - eg, where ioremap() doesn't work. Eg, OMAP you need SRAM mapped as normal memory, except for OMAP34xx where it must be mapped normal memory non-cacheable. It's best to leave the mapping to the architecture. >> Another question is whether we should allow multiple SRAM pools or not - >> this code does allow multiple pools, but so far we only have one pool >> per SoC. Overdesign? Maybe, but it prevents SoCs wanting to duplicate >> it if they want to partition the SRAM, or have peripheral-local SRAMs. > Having the option to partition the SRAM is probably useful. > What I'm missing is sram_pool_add: on AT91SAM9G20 you have two banks > of SRAM, and you might want to combine them into a single pool. Do you already combine them into a single pool, or is this a wishlist? I'm really not interested in sorting out peoples wishlist items in the process of consolidation. Second point is that you'll notice that the code converts to a phys address using this: phys = phys_base + (virt - virt_base). As soon as we start allowing multiple regions of SRAM, it becomes impossible to provide the phys address without a lot more complexity. >> arch/arm/common/sram-pool.c | 69 +++++++++++++++++++++++++++ >> arch/arm/include/asm/sram-pool.h | 20 ++++++++ > Waht is ARM specific about this code? > Why not put it into lib/ and include/linux, respectively? Nothing, but this is the first stage of consolidation of this code. I'm not trying to consolidate the universe down to one implementation here - that's going to take _far_ too much effort in one go, and from my previous experiences interacting with other arch maintainers, that's the way to get precisely nothing done. In my experience, arch maintainers tend to object to each others patches, and the net result is no forward progress. See dma_mmap API as a brilliant example of that - the lack of which contines to cause problems for drivers many years after I initially tried (and gave up) trying to get agreement on that. So, let's do what we can to consolidate our stuff and if we get some agreement from other arch maintainers, look towards moving it out. Moving stuff out once its properly modularized is trivial. From linux at arm.linux.org.uk Fri Apr 15 10:41:13 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 16:41:13 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> <201104151640.01040.arnd@arndb.de> <20110415152643.GC4423@n2100.arm.linux.org.uk> Message-ID: <20110415154113.GE4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 09:32:01AM -0600, Grant Likely wrote: > Yes, once the infrastructure is in place, powerpc can do its own > migration to the new code. I vote for putting it in lib at the > outset. I don't agree with stuffing non-arch directories with code which people haven't already agreed should be shared. As I've already said, in my experience it's hard to get agreement in the first place and even when you can the API generally needs to be changed from what you first think would be reasonable. So, lets wait to see whether the SH folk reply. From linux at arm.linux.org.uk Fri Apr 15 11:03:39 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 17:03:39 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <4DA84AAB.60601@gmail.com> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> Message-ID: <20110415160339.GF4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 08:39:55AM -0500, Rob Herring wrote: > Russell, > > On 04/15/2011 08:06 AM, Russell King - ARM Linux wrote: >> This is work in progress. >> >> We have two SoCs using SRAM, both with their own allocation systems, >> and both with their own ways of copying functions into the SRAM. > > It's more than that. Several i.MX chips use plat-mxc/iram_alloc.c. Hmm, that's nice - except for one issue. According to my grep of arch/arm/ and drivers/, nothing uses iram_alloc(). So, does anything in the MX stuff use iram_alloc.c, or is it dead code left over from a previous experiment? The commit says: ARM: imx: Add iram allocator functions Add IRAM(Internal RAM) allocation functions using GENERIC_ALLOCATOR. The allocation size is 4KB multiples to guarantee alignment. The idea for these functions is for i.MX platforms to use them to dynamically allocate IRAM usage. Applies on 2.6.36-rc7 > lpc32xx and pnx4008 also use iram, but do not have an allocator (only 1 > user). Both are doing a copy the suspend code to IRAM and run it which > may also be a good thing to have generic code for. Several i.MX chips > also need to run from IRAM for suspend. We have support for copying functions to other bits of memory and getting the Thumb-ness right - see asm/fncpy.h. So that's a separate patch to convert them over. From linux at arm.linux.org.uk Fri Apr 15 11:20:45 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 17:20:45 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <56132A77AB93C141BF06E6B96CA6CFEA1F88D9@039-SN1MPN1-004.039d.mgd.msft.net> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> <20110415160339.GF4423@n2100.arm.linux.org.uk> <56132A77AB93C141BF06E6B96CA6CFEA1F88D9@039-SN1MPN1-004.039d.mgd.msft.net> Message-ID: <20110415162045.GG4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 04:18:23PM +0000, Nguyen Dinh-R00091 wrote: > >Hmm, that's nice - except for one issue. According to my grep of > >arch/arm/ and drivers/, nothing uses iram_alloc(). So, does anything in > >the MX stuff use iram_alloc.c, or is it dead code left over from a > >previous experiment? > > This function will be used for suspend code in the mx5x series. I just > got done submitting a series of patches to Sascha for a simple suspend > that does not need running code out of IRAM yet. The next set of suspend > patches will be using these iram functions. Okay, so does iram_alloc() need to return a dma_addr_t or a phys_addr_t ? Are you dealing with physical addresses for it or DMA addresses? From linux at arm.linux.org.uk Fri Apr 15 11:58:58 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 17:58:58 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <20110415162045.GG4423@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> <20110415160339.GF4423@n2100.arm.linux.org.uk> <56132A77AB93C141BF06E6B96CA6CFEA1F88D9@039-SN1MPN1-004.039d.mgd.msft.net> <20110415162045.GG4423@n2100.arm.linux.org.uk> Message-ID: <20110415165857.GH4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 05:20:45PM +0100, Russell King - ARM Linux wrote: > On Fri, Apr 15, 2011 at 04:18:23PM +0000, Nguyen Dinh-R00091 wrote: > > >Hmm, that's nice - except for one issue. According to my grep of > > >arch/arm/ and drivers/, nothing uses iram_alloc(). So, does anything in > > >the MX stuff use iram_alloc.c, or is it dead code left over from a > > >previous experiment? > > > > This function will be used for suspend code in the mx5x series. I just > > got done submitting a series of patches to Sascha for a simple suspend > > that does not need running code out of IRAM yet. The next set of suspend > > patches will be using these iram functions. > > Okay, so does iram_alloc() need to return a dma_addr_t or a phys_addr_t ? > Are you dealing with physical addresses for it or DMA addresses? And another question: why does iram_alloc() return a void __iomem * for the virtual address, and the physical address via a pointer argument. However, iram_free() take an unsigned long for the address. It seems rather inconsistent that the parameter for free is returned via a pointer argument. If I convert this to sram-pool, can we change this to: static inline void *iram_alloc(size_t size, phys_addr_t *phys_addr) { return sram_pool_alloc(iram_pool, size, phys_addr); } static void iram_free(void *addr, size_t size) { sram_pool_free(iram_pool, addr, size); } and: int __init iram_init(unsigned long base, unsigned long size) becomes: int __init iram_init(phys_addr_t base, size_t size) ? From linux at arm.linux.org.uk Fri Apr 15 13:21:44 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 19:21:44 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <4DA88A77.6050502@vollmann.ch> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA85B49.60209@vollmann.ch> <20110415153723.GD4423@n2100.arm.linux.org.uk> <4DA88A77.6050502@vollmann.ch> Message-ID: <20110415182144.GI4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 08:12:07PM +0200, Detlef Vollmann wrote: >> Second point is that you'll notice that the code converts to a phys >> address using this: phys = phys_base + (virt - virt_base). As soon as >> we start allowing multiple regions of SRAM, it becomes impossible to >> provide the phys address without a lot more complexity. > Yes, I understand that. This either requires some intrusive changes > to gen_pool code or quite some additional code in sram_pool_alloc. It would require sram_pool to track each individual buffer alongside the similar tracking that gen_pool does, and then look up the returned address to find out which buffer it corresponds with. As it looks though, this functionality isn't required on AT91. So lets not complicate the code unless we know we have to. While the alloc/free API is such that it'll be able to cope if we have to add it later - the initialization will have to become a little more complex. And then I start to wonder if gen_pool should just be extended for the phys address. From linux at arm.linux.org.uk Fri Apr 15 14:40:21 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 20:40:21 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <56132A77AB93C141BF06E6B96CA6CFEA1F8D18@039-SN1MPN1-004.039d.mgd.msft.net> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <4DA84AAB.60601@gmail.com> <20110415160339.GF4423@n2100.arm.linux.org.uk> <56132A77AB93C141BF06E6B96CA6CFEA1F88D9@039-SN1MPN1-004.039d.mgd.msft.net> <20110415162045.GG4423@n2100.arm.linux.org.uk> <20110415165857.GH4423@n2100.arm.linux.org.uk> <56132A77AB93C141BF06E6B96CA6CFEA1F8D18@039-SN1MPN1-004.039d.mgd.msft.net> Message-ID: <20110415194021.GJ4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 07:20:12PM +0000, Nguyen Dinh-R00091 wrote: > > >-----Original Message----- > >From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk] > >Sent: Friday, April 15, 2011 11:59 AM > >To: Nguyen Dinh-R00091 > >Cc: Kevin Hilman; davinci-linux-open-source at linux.davincidsp.com; Tony Lindgren; Sekhar Nori; linux- > >omap at vger.kernel.org; linux-arm-kernel at lists.infradead.org > >Subject: Re: [RFC PATCH] Consolidate SRAM support > > > >On Fri, Apr 15, 2011 at 05:20:45PM +0100, Russell King - ARM Linux wrote: > >> On Fri, Apr 15, 2011 at 04:18:23PM +0000, Nguyen Dinh-R00091 wrote: > >> > >Hmm, that's nice - except for one issue. According to my grep of > >> > >arch/arm/ and drivers/, nothing uses iram_alloc(). So, does anything in > >> > >the MX stuff use iram_alloc.c, or is it dead code left over from a > >> > >previous experiment? > >> > > >> > This function will be used for suspend code in the mx5x series. I just > >> > got done submitting a series of patches to Sascha for a simple suspend > >> > that does not need running code out of IRAM yet. The next set of suspend > >> > patches will be using these iram functions. > >> > >> Okay, so does iram_alloc() need to return a dma_addr_t or a phys_addr_t ? > >> Are you dealing with physical addresses for it or DMA addresses? > > > >And another question: why does iram_alloc() return a void __iomem * for > >the virtual address, and the physical address via a pointer argument. > >However, iram_free() take an unsigned long for the address. > > Yes, should just be a void *iram_alloc(). Thanks. As you didn't comment against the change below, I'm going to assume that you're happy with it. It means that the usage changes from: unsigned long dma; void __iomem *addr = iram_alloc(SZ_4K, &dma); ... iram_free(dma, SZ_4K); to: phys_addr_t phys; void *addr = iram_alloc(SZ_4K, &phys); ... iram_free(addr, SZ_4K); > >It seems rather inconsistent that the parameter for free is returned via > >a pointer argument. > > > >If I convert this to sram-pool, can we change this to: > > > >static inline void *iram_alloc(size_t size, phys_addr_t *phys_addr) > >{ > > return sram_pool_alloc(iram_pool, size, phys_addr); > >} > > > >static void iram_free(void *addr, size_t size) > >{ > > sram_pool_free(iram_pool, addr, size); > >} > > > >and: > > > >int __init iram_init(unsigned long base, unsigned long size) > > > >becomes: > > > >int __init iram_init(phys_addr_t base, size_t size) > > > >? > > From linux at arm.linux.org.uk Fri Apr 15 15:19:25 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 15 Apr 2011 21:19:25 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <20110415201107.GO31990@pengutronix.de> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110415201107.GO31990@pengutronix.de> Message-ID: <20110415201925.GK4423@n2100.arm.linux.org.uk> On Fri, Apr 15, 2011 at 10:11:07PM +0200, Uwe Kleine-K?nig wrote: > On Fri, Apr 15, 2011 at 02:06:07PM +0100, Russell King - ARM Linux wrote: > > This is work in progress. > > > > We have two SoCs using SRAM, both with their own allocation systems, > > and both with their own ways of copying functions into the SRAM. > I havn't checked the details, but maybe the code in > arch/arm/plat-mxc/iram_alloc.c could be migrated to your approach, too? Its already in there now that I have replies from Nguyen Dinh. It looks like this presently: arch/arm/plat-mxc/Kconfig | 2 +- arch/arm/plat-mxc/include/mach/iram.h | 24 +++++++-- arch/arm/plat-mxc/iram_alloc.c | 50 +++++--------------- and if we get rid of the iram_alloc/iram_free wrappers around the sram_pool (now pv_pool) alloc/free in iram.h, in the same way I've done for Davinci, then we get less new additions too. From linux at arm.linux.org.uk Sat Apr 16 08:09:29 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Sat, 16 Apr 2011 14:09:29 +0100 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: References: <20110415130607.GM1611@n2100.arm.linux.org.uk> Message-ID: <20110416130929.GL4423@n2100.arm.linux.org.uk> On Sat, Apr 16, 2011 at 09:01:26PM +0800, Haojian Zhuang wrote: > On Fri, Apr 15, 2011 at 9:06 PM, Russell King - ARM Linux > wrote: > > This is work in progress. > > > > We have two SoCs using SRAM, both with their own allocation systems, > > and both with their own ways of copying functions into the SRAM. > > > > Let's unify this before we have additional SoCs re-implementing this > > obviously common functionality themselves. > > > > Unfortunately, we end up with code growth through doing this, but that > > will become a win when we have another SoC using this (which I know > > there's at least one in the pipeline). > > > > One of the considerations here is that we can easily convert sram-pool.c > > to hook into device tree stuff, which can tell the sram allocator: > > ? ? ? ?- physical address of sram > > ? ? ? ?- size of sram > > ? ? ? ?- allocation granularity > > and then we just need to ensure that it is appropriately mapped. > > > > This uses the physical address, and unlike Davinci's dma address usage, > > it always wants to have the physical address, and will always return > > the corresponding physical address when passed that pointer. > > > > OMAP could probably do with some more work to make the omapfb and other > > allocations use the sram allocator, rather than hooking in before the > > sram allocator is initialized - and then further cleanups so that we > > have an initialization function which just does > > > > sram_create(phys, size) > > ? ? ? ?virt = map sram(phys, size) > > ? ? ? ?create sram pool(virt, phys, size, min_alloc_order) > > > > Another question is whether we should allow multiple SRAM pools or not - > > this code does allow multiple pools, but so far we only have one pool > > per SoC. ?Overdesign? ?Maybe, but it prevents SoCs wanting to duplicate > > it if they want to partition the SRAM, or have peripheral-local SRAMs. > > > > Lastly, uio_pruss should probably take the SRAM pool pointer via > > platform data so that it doesn't have to include Davinci specific > > includes. > > > > Signed-off-by: Russell King > > This common sram driver is good for us. It can benefit us on DMA usage. > I just have one question on SRAM for storing instruction. We still need to > copy code into SRAM and flush cache & TLB with this SRAM driver. There is the fncpy API for copying code to other regions of memory, including SRAM. The fncpy API is explicitly designed to cope with Thumb 2 and provide an appropriate function pointer for such code. Such code needs to be position independent to cope with multiple SRAM users, and also possible variability of SRAM mapping which may (eventually) exist when DT comes along. > TCM driver can allocate code into SRAM section in link stage. It needs to > update link file and virtual memory layout. Is it worth to make SRAM driver > support this behavior? The case of using SRAM as memory for instruction > is switching frequency or entering/exiting low power mode in PXA silicons. This is more or less the same scenario which OMAP is using its SRAM for, although that's explicitly SRAM and not TCM. I don't think we need position dependent code requiring fixup for these applications as such things tend to be fairly easily coded in a position independent way. Also note that C functions can't be copied because C produces position dependent code, and we have no way of extracting the relocation dependencies from such code. From arnd at arndb.de Sun Apr 17 12:47:37 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Sun, 17 Apr 2011 19:47:37 +0200 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <20110415154113.GE4423@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110415154113.GE4423@n2100.arm.linux.org.uk> Message-ID: <201104171947.38048.arnd@arndb.de> On Friday 15 April 2011, Russell King - ARM Linux wrote: > On Fri, Apr 15, 2011 at 09:32:01AM -0600, Grant Likely wrote: > > Yes, once the infrastructure is in place, powerpc can do its own > > migration to the new code. I vote for putting it in lib at the > > outset. > > I don't agree with stuffing non-arch directories with code which people > haven't already agreed should be shared. As I've already said, in my > experience it's hard to get agreement in the first place and even when > you can the API generally needs to be changed from what you first think > would be reasonable. I believe we should be much more aggressive about putting code into generic locations, especially when there is nothing hardware specific to it. As we can see from all the mess regarding interrupt controllers, PCI busses or dma mapping, most of the smaller architectures get it wrong when they have to deal with these things, so when we have a common implementation, it's much easier to make sure we only have the bugs once and don't get incompatible interfaces. Arnd From tony at atomide.com Mon Apr 18 01:48:38 2011 From: tony at atomide.com (Tony Lindgren) Date: Mon, 18 Apr 2011 09:48:38 +0300 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <20110416130929.GL4423@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110416130929.GL4423@n2100.arm.linux.org.uk> Message-ID: <20110418064838.GE12272@atomide.com> * Russell King - ARM Linux [110416 16:06]: > On Sat, Apr 16, 2011 at 09:01:26PM +0800, Haojian Zhuang wrote: > > On Fri, Apr 15, 2011 at 9:06 PM, Russell King - ARM Linux > > > > > > This uses the physical address, and unlike Davinci's dma address usage, > > > it always wants to have the physical address, and will always return > > > the corresponding physical address when passed that pointer. > > > > > > OMAP could probably do with some more work to make the omapfb and other > > > allocations use the sram allocator, rather than hooking in before the > > > sram allocator is initialized - and then further cleanups so that we > > > have an initialization function which just does I think we can just remove the omapfb SRAM support for now as it should be optional. That will then leave out that dependency and can be added back once we have a common framework in place. Tomi do you see any problems with that? > > TCM driver can allocate code into SRAM section in link stage. It needs to > > update link file and virtual memory layout. Is it worth to make SRAM driver > > support this behavior? The case of using SRAM as memory for instruction > > is switching frequency or entering/exiting low power mode in PXA silicons. > > This is more or less the same scenario which OMAP is using its SRAM > for, although that's explicitly SRAM and not TCM. > > I don't think we need position dependent code requiring fixup for > these applications as such things tend to be fairly easily coded in a > position independent way. There should not be any need code requiring fixup in most cases using parameters passed to the functions should be enough. Usually it's just register addresses that are different. > Also note that C functions can't be copied because C produces position > dependent code, and we have no way of extracting the relocation > dependencies from such code. C functions should not be needed. The SRAM size is small and typical usage is a loop waiting for some register to lock. For any larger SRAM areas NUMA can be used instead. Regards, Tony From tony at atomide.com Mon Apr 18 03:17:43 2011 From: tony at atomide.com (Tony Lindgren) Date: Mon, 18 Apr 2011 11:17:43 +0300 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <1303110011.2062.7.camel@deskari> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110416130929.GL4423@n2100.arm.linux.org.uk> <20110418064838.GE12272@atomide.com> <1303110011.2062.7.camel@deskari> Message-ID: <20110418081743.GH12272@atomide.com> * Tomi Valkeinen [110418 09:57]: > On Mon, 2011-04-18 at 09:48 +0300, Tony Lindgren wrote: > > * Russell King - ARM Linux [110416 16:06]: > > > On Sat, Apr 16, 2011 at 09:01:26PM +0800, Haojian Zhuang wrote: > > > > On Fri, Apr 15, 2011 at 9:06 PM, Russell King - ARM Linux > > > > > > > > > > This uses the physical address, and unlike Davinci's dma address usage, > > > > > it always wants to have the physical address, and will always return > > > > > the corresponding physical address when passed that pointer. > > > > > > > > > > OMAP could probably do with some more work to make the omapfb and other > > > > > allocations use the sram allocator, rather than hooking in before the > > > > > sram allocator is initialized - and then further cleanups so that we > > > > > have an initialization function which just does > > > > I think we can just remove the omapfb SRAM support for now as it should > > be optional. That will then leave out that dependency and can be added > > back once we have a common framework in place. > > > > Tomi do you see any problems with that? > > I agree. Only OMAP2 has enough SRAM to possibly have a framebuffer > there, and even on OMAP2 the SRAM size is so small that it works only > for rather small displays. I'm not aware of anyone using omapfb's SRAM > support. > > The SRAM support also makes our video ram allocator and omapfb more > complex, without much benefit, so I'm more than happy to remove it > totally. Additionally, I believe there is work going on with memory > allocators that omapfb could also use, and migrating to any new mem > allocator will no doubt be much easier if we just handle normal RAM. > > So, I can make a patch that removes the SRAM support from omapfb, and > queue it up for the next merge window. OK. That patch should probably go into Russell's tree along with other SRAM related patches to avoid conflicts. Regards, Tony From linux at arm.linux.org.uk Mon Apr 18 03:52:59 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Mon, 18 Apr 2011 09:52:59 +0100 Subject: [RFC PATCH v2] Consolidate SRAM support In-Reply-To: <20110415130607.GM1611@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> Message-ID: <20110418085259.GA26044@n2100.arm.linux.org.uk> This is the second revision of this patch. I've not moved it out of ARM yet as I haven't had a positive response from SH yet. It's now called pv_pool (for phys/virt pool) rather than sram_pool, and I've included MXC's iram support in this. Hopefully, if OMAP can remove the FB stuff from SRAM we can clean the OMAP bits up a little more. Neither have I sorted out the last reference to omap_sram_ceil. Some comments from OMAP people on what's going on there would be good. On Fri, Apr 15, 2011 at 02:06:07PM +0100, Russell King - ARM Linux wrote: > This is work in progress. > > We have two SoCs using SRAM, both with their own allocation systems, > and both with their own ways of copying functions into the SRAM. > > Let's unify this before we have additional SoCs re-implementing this > obviously common functionality themselves. > > Unfortunately, we end up with code growth through doing this, but that > will become a win when we have another SoC using this (which I know > there's at least one in the pipeline). > > One of the considerations here is that we can easily convert sram-pool.c > to hook into device tree stuff, which can tell the sram allocator: > - physical address of sram > - size of sram > - allocation granularity > and then we just need to ensure that it is appropriately mapped. > > This uses the physical address, and unlike Davinci's dma address usage, > it always wants to have the physical address, and will always return > the corresponding physical address when passed that pointer. > > OMAP could probably do with some more work to make the omapfb and other > allocations use the sram allocator, rather than hooking in before the > sram allocator is initialized - and then further cleanups so that we > have an initialization function which just does > > sram_create(phys, size) > virt = map sram(phys, size) > create sram pool(virt, phys, size, min_alloc_order) > > Another question is whether we should allow multiple SRAM pools or not - > this code does allow multiple pools, but so far we only have one pool > per SoC. Overdesign? Maybe, but it prevents SoCs wanting to duplicate > it if they want to partition the SRAM, or have peripheral-local SRAMs. > > Lastly, uio_pruss should probably take the SRAM pool pointer via > platform data so that it doesn't have to include Davinci specific > includes. arch/arm/Kconfig | 2 + arch/arm/common/Kconfig | 4 ++ arch/arm/common/Makefile | 1 + arch/arm/common/pv-pool.c | 69 +++++++++++++++++++++++++++ arch/arm/include/asm/pv-pool.h | 20 ++++++++ arch/arm/mach-davinci/da850.c | 2 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 2 +- arch/arm/mach-davinci/dm644x.c | 2 +- arch/arm/mach-davinci/dm646x.c | 2 +- arch/arm/mach-davinci/include/mach/common.h | 2 +- arch/arm/mach-davinci/include/mach/sram.h | 13 +---- arch/arm/mach-davinci/pm.c | 12 +---- arch/arm/mach-davinci/sram.c | 42 +++-------------- arch/arm/plat-mxc/Kconfig | 2 +- arch/arm/plat-mxc/include/mach/iram.h | 24 +++++++-- arch/arm/plat-mxc/iram_alloc.c | 50 +++++--------------- arch/arm/plat-omap/include/plat/sram.h | 17 ++++--- arch/arm/plat-omap/sram.c | 34 +++++--------- drivers/uio/uio_pruss.c | 7 ++- 20 files changed, 171 insertions(+), 138 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5b9f78b..5c3401c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -850,6 +850,7 @@ config ARCH_DAVINCI bool "TI DaVinci" select GENERIC_CLOCKEVENTS select ARCH_REQUIRE_GPIOLIB + select PV_POOL select ZONE_DMA select HAVE_IDE select CLKDEV_LOOKUP @@ -863,6 +864,7 @@ config ARCH_OMAP select HAVE_CLK select ARCH_REQUIRE_GPIOLIB select ARCH_HAS_CPUFREQ + select PV_POOL select GENERIC_CLOCKEVENTS select HAVE_SCHED_CLOCK select ARCH_HAS_HOLES_MEMORYMODEL diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index ea5ee4d..ddbd20b 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -39,3 +39,7 @@ config SHARP_PARAM config SHARP_SCOOP bool + +config PV_POOL + bool + select GENERIC_ALLOCATOR diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index e7521bca..b79ad68 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_PV_POOL) += pv-pool.o diff --git a/arch/arm/common/pv-pool.c b/arch/arm/common/pv-pool.c new file mode 100644 index 0000000..9ff1466 --- /dev/null +++ b/arch/arm/common/pv-pool.c @@ -0,0 +1,69 @@ +/* + * Unified Phys/Virt allocator, based on mach-davinci/sram.c, which was + * Copyright (C) 2009 David Brownell. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include + +#include + +struct pv_pool { + struct gen_pool *genpool; + void *cpu_base; + phys_addr_t phys_base; +}; + +void *pv_pool_alloc(struct pv_pool *pool, size_t len, phys_addr_t *phys) +{ + void *addr = (void *)gen_pool_alloc(pool->genpool, len); + + if (phys) + *phys = addr ? (pool->phys_base + (addr - pool->cpu_base)) : + (phys_addr_t)-1ULL; + + return addr; +} +EXPORT_SYMBOL_GPL(pv_pool_alloc); + +void pv_pool_free(struct pv_pool *pool, void *addr, size_t len) +{ + gen_pool_free(pool->genpool, (unsigned long)addr, len); +} +EXPORT_SYMBOL_GPL(pv_pool_free); + +struct pv_pool *pv_pool_create(void *addr, phys_addr_t phys, size_t len, + int min_alloc_order) +{ + struct pv_pool *pool = kzalloc(sizeof(struct pv_pool), GFP_KERNEL); + + if (pool) { + pool->cpu_base = addr; + pool->phys_base = phys; + pool->genpool = gen_pool_create(min_alloc_order, -1); + if (!pool->genpool) { + kfree(pool); + pool = NULL; + } else { + WARN_ON(gen_pool_add(pool->genpool, (unsigned long)addr, + len, -1) < 0); + } + } + + return pool; +} +EXPORT_SYMBOL_GPL(pv_pool_create); + +void pv_pool_destroy(struct pv_pool *pool) +{ + gen_pool_destroy(pool->genpool); + kfree(pool); +} +EXPORT_SYMBOL_GPL(pv_pool_destroy); diff --git a/arch/arm/include/asm/pv-pool.h b/arch/arm/include/asm/pv-pool.h new file mode 100644 index 0000000..b7ae871 --- /dev/null +++ b/arch/arm/include/asm/pv-pool.h @@ -0,0 +1,20 @@ +#ifndef __ASMARM_PV_POOL_H +#define __ASMARM_PV_POOL_H + +#include + +struct pv_pool; + +void *pv_pool_alloc(struct pv_pool *, size_t, phys_addr_t *); +void pv_pool_free(struct pv_pool *, void *, size_t); +struct pv_pool *pv_pool_create(void *, phys_addr_t, size_t, int); +void pv_pool_destroy(struct pv_pool *); + +/* Macro to copy a function into SRAM, using the fncpy API */ +#define pv_pool_fncpy(pool, funcp, size) ({ \ + size_t _sz = size; \ + void *_sram = pv_pool_alloc(pool, _sz, NULL); \ + (_sram ? fncpy(_sram, &(funcp), _sz) : NULL); \ +}) + +#endif diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 68fe4c2..5eca128 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1099,7 +1099,7 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .gpio_irq = IRQ_DA8XX_GPIO0, .serial_dev = &da8xx_serial_device, .emac_pdata = &da8xx_emac_pdata, - .sram_dma = DA8XX_ARM_RAM_BASE, + .sram_phys = DA8XX_ARM_RAM_BASE, .sram_len = SZ_8K, .reset_device = &da8xx_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 76364d1..3df8730 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -850,7 +850,7 @@ static struct davinci_soc_info davinci_soc_info_dm355 = { .gpio_num = 104, .gpio_irq = IRQ_DM355_GPIOBNK0, .serial_dev = &dm355_serial_device, - .sram_dma = 0x00010000, + .sram_phys = 0x00010000, .sram_len = SZ_32K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 4604e72..d306034 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -1082,7 +1082,7 @@ static struct davinci_soc_info davinci_soc_info_dm365 = { .gpio_unbanked = 8, /* really 16 ... skip muxed GPIOs */ .serial_dev = &dm365_serial_device, .emac_pdata = &dm365_emac_pdata, - .sram_dma = 0x00010000, + .sram_phys = 0x00010000, .sram_len = SZ_32K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 9a2376b..4ca7295 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -764,7 +764,7 @@ static struct davinci_soc_info davinci_soc_info_dm644x = { .gpio_irq = IRQ_GPIOBNK0, .serial_dev = &dm644x_serial_device, .emac_pdata = &dm644x_emac_pdata, - .sram_dma = 0x00008000, + .sram_phys = 0x00008000, .sram_len = SZ_16K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..a4365f7 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -848,7 +848,7 @@ static struct davinci_soc_info davinci_soc_info_dm646x = { .gpio_irq = IRQ_DM646X_GPIOBNK0, .serial_dev = &dm646x_serial_device, .emac_pdata = &dm646x_emac_pdata, - .sram_dma = 0x10010000, + .sram_phys = 0x10010000, .sram_len = SZ_32K, .reset_device = &davinci_wdt_device, }; diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index a57cba2..665d049 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -75,7 +75,7 @@ struct davinci_soc_info { int gpio_ctlrs_num; struct platform_device *serial_dev; struct emac_platform_data *emac_pdata; - dma_addr_t sram_dma; + phys_addr_t sram_phys; unsigned sram_len; struct platform_device *reset_device; void (*reset)(struct platform_device *); diff --git a/arch/arm/mach-davinci/include/mach/sram.h b/arch/arm/mach-davinci/include/mach/sram.h index 111f7cc..7d77e85 100644 --- a/arch/arm/mach-davinci/include/mach/sram.h +++ b/arch/arm/mach-davinci/include/mach/sram.h @@ -10,18 +10,11 @@ #ifndef __MACH_SRAM_H #define __MACH_SRAM_H +#include + /* ARBITRARY: SRAM allocations are multiples of this 2^N size */ #define SRAM_GRANULARITY 512 -/* - * SRAM allocations return a CPU virtual address, or NULL on error. - * If a DMA address is requested and the SRAM supports DMA, its - * mapped address is also returned. - * - * Errors include SRAM memory not being available, and requesting - * DMA mapped SRAM on systems which don't allow that. - */ -extern void *sram_alloc(size_t len, dma_addr_t *dma); -extern void sram_free(void *addr, size_t len); +extern struct pv_pool *davinci_pv_pool; #endif /* __MACH_SRAM_H */ diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c index 1bd73a0..f69cd7b 100644 --- a/arch/arm/mach-davinci/pm.c +++ b/arch/arm/mach-davinci/pm.c @@ -29,12 +29,6 @@ static void (*davinci_sram_suspend) (struct davinci_pm_config *); static struct davinci_pm_config *pdata; -static void davinci_sram_push(void *dest, void *src, unsigned int size) -{ - memcpy(dest, src, size); - flush_icache_range((unsigned long)dest, (unsigned long)(dest + size)); -} - static void davinci_pm_suspend(void) { unsigned val; @@ -123,15 +117,13 @@ static int __init davinci_pm_probe(struct platform_device *pdev) return -ENOENT; } - davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); + davinci_sram_suspend = pv_pool_fncpy(davinci_pv_pool, + davinci_cpu_suspend, davinci_cpu_suspend_sz); if (!davinci_sram_suspend) { dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); return -ENOMEM; } - davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, - davinci_cpu_suspend_sz); - suspend_set_ops(&davinci_pm_ops); return 0; diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c index db0f778..ebd4d67 100644 --- a/arch/arm/mach-davinci/sram.c +++ b/arch/arm/mach-davinci/sram.c @@ -10,40 +10,13 @@ */ #include #include -#include +#include #include #include -static struct gen_pool *sram_pool; - -void *sram_alloc(size_t len, dma_addr_t *dma) -{ - unsigned long vaddr; - dma_addr_t dma_base = davinci_soc_info.sram_dma; - - if (dma) - *dma = 0; - if (!sram_pool || (dma && !dma_base)) - return NULL; - - vaddr = gen_pool_alloc(sram_pool, len); - if (!vaddr) - return NULL; - - if (dma) - *dma = dma_base + (vaddr - SRAM_VIRT); - return (void *)vaddr; - -} -EXPORT_SYMBOL(sram_alloc); - -void sram_free(void *addr, size_t len) -{ - gen_pool_free(sram_pool, (unsigned long) addr, len); -} -EXPORT_SYMBOL(sram_free); - +struct pv_pool *davinci_pv_pool; +EXPORT_SYMBOL_GPL(davinci_pv_pool); /* * REVISIT This supports CPU and DMA access to/from SRAM, but it @@ -58,13 +31,12 @@ static int __init sram_init(void) if (len) { len = min_t(unsigned, len, SRAM_SIZE); - sram_pool = gen_pool_create(ilog2(SRAM_GRANULARITY), -1); - if (!sram_pool) + davinci_pv_pool = pv_pool_create((void *)SRAM_VIRT, + davinci_soc_info.sram_phys, len, + ilog2(SRAM_GRANULARITY)); + if (!davinci_pv_pool) status = -ENOMEM; } - if (sram_pool) - status = gen_pool_add(sram_pool, SRAM_VIRT, len, -1); - WARN_ON(status < 0); return status; } core_initcall(sram_init); diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig index b0cb425..f60d5ea 100644 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig @@ -118,6 +118,6 @@ config ARCH_MXC_AUDMUX_V2 config IRAM_ALLOC bool - select GENERIC_ALLOCATOR + select PV_POOL endif diff --git a/arch/arm/plat-mxc/include/mach/iram.h b/arch/arm/plat-mxc/include/mach/iram.h index 022690c..543c6df 100644 --- a/arch/arm/plat-mxc/include/mach/iram.h +++ b/arch/arm/plat-mxc/include/mach/iram.h @@ -17,25 +17,37 @@ * MA 02110-1301, USA. */ #include +#include #ifdef CONFIG_IRAM_ALLOC -int __init iram_init(unsigned long base, unsigned long size); -void __iomem *iram_alloc(unsigned int size, unsigned long *dma_addr); -void iram_free(unsigned long dma_addr, unsigned int size); +int __init iram_init(phys_addr_t base, size_t size); + +extern struct pv_pool *mxc_iram_pool; + +static inline void *iram_alloc(size_t size, phys_addr_t *phys) +{ + return pv_pool_alloc(iram_pool, size, phys); +} + +static inline void iram_free(void *addr, size_t size) +{ + pv_pool_free(iram_pool, addr, size); +} #else -static inline int __init iram_init(unsigned long base, unsigned long size) +static inline int __init iram_init(phys_addr_t base, size_t size) { return -ENOMEM; } -static inline void __iomem *iram_alloc(unsigned int size, unsigned long *dma_addr) +static inline void *iram_alloc(size_t size, phys_addr_t *phys) { + *phys = (phys_addr_t)-1ULL; return NULL; } -static inline void iram_free(unsigned long base, unsigned long size) {} +static inline void iram_free(void *addr, size_t size) {} #endif diff --git a/arch/arm/plat-mxc/iram_alloc.c b/arch/arm/plat-mxc/iram_alloc.c index 074c386..1e3d437 100644 --- a/arch/arm/plat-mxc/iram_alloc.c +++ b/arch/arm/plat-mxc/iram_alloc.c @@ -24,50 +24,24 @@ #include #include -static unsigned long iram_phys_base; -static void __iomem *iram_virt_base; -static struct gen_pool *iram_pool; +struct pv_pool *mxc_iram_pool; +EXPORT_SYMBOL(mxc_iram_pool); -static inline void __iomem *iram_phys_to_virt(unsigned long p) +int __init iram_init(phys_addr_t base, size_t size) { - return iram_virt_base + (p - iram_phys_base); -} - -void __iomem *iram_alloc(unsigned int size, unsigned long *dma_addr) -{ - if (!iram_pool) - return NULL; - - *dma_addr = gen_pool_alloc(iram_pool, size); - pr_debug("iram alloc - %dB at 0x%lX\n", size, *dma_addr); - if (!*dma_addr) - return NULL; - return iram_phys_to_virt(*dma_addr); -} -EXPORT_SYMBOL(iram_alloc); - -void iram_free(unsigned long addr, unsigned int size) -{ - if (!iram_pool) - return; - - gen_pool_free(iram_pool, addr, size); -} -EXPORT_SYMBOL(iram_free); + void *addr = /*FIXME*/ ioremap(base, size); -int __init iram_init(unsigned long base, unsigned long size) -{ - iram_phys_base = base; + if (!addr) + return -EIO; - iram_pool = gen_pool_create(PAGE_SHIFT, -1); - if (!iram_pool) + mxc_iram_pool = pv_pool_create(addr, base, size, PAGE_SHIFT); + if (!mxc_iram_pool) { + iounmap(addr); return -ENOMEM; + } - gen_pool_add(iram_pool, base, size, -1); - iram_virt_base = ioremap(iram_phys_base, size); - if (!iram_virt_base) - return -EIO; + pr_debug("i.MX IRAM pool: %lu KB at 0x%08llx\n", size / 1024, + (unsigned long long)base); - pr_debug("i.MX IRAM pool: %ld KB at 0x%p\n", size / 1024, iram_virt_base); return 0; } diff --git a/arch/arm/plat-omap/include/plat/sram.h b/arch/arm/plat-omap/include/plat/sram.h index f500fc3..9fc27d0 100644 --- a/arch/arm/plat-omap/include/plat/sram.h +++ b/arch/arm/plat-omap/include/plat/sram.h @@ -12,16 +12,19 @@ #define __ARCH_ARM_OMAP_SRAM_H #ifndef __ASSEMBLY__ -#include +#include -extern void *omap_sram_push_address(unsigned long size); +extern struct pv_pool *omap_pv_pool; -/* Macro to push a function to the internal SRAM, using the fncpy API */ +/* + * Note that fncpy requires the SRAM address to be aligned to an 8-byte + * boundary, so the min_alloc_order for the pool is set appropriately. + */ #define omap_sram_push(funcp, size) ({ \ - typeof(&(funcp)) _res = NULL; \ - void *_sram_address = omap_sram_push_address(size); \ - if (_sram_address) \ - _res = fncpy(_sram_address, &(funcp), size); \ + typeof(&(funcp)) _res; \ + _res = pv_pool_fncpy(omap_pv_pool, funcp, size); \ + if (!_res) \ + pr_err("Not enough space in SRAM\n"); \ _res; \ }) diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index a3f50b3..3588749 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -75,7 +75,6 @@ static unsigned long omap_sram_start; static unsigned long omap_sram_base; static unsigned long omap_sram_size; -static unsigned long omap_sram_ceil; /* * Depending on the target RAMFS firewall setup, the public usable amount of @@ -104,6 +103,8 @@ static int is_sram_locked(void) return 1; /* assume locked with no PPA or security driver */ } +struct pv_pool *omap_pv_pool; + /* * The amount of SRAM depends on the core type. * Note that we cannot try to test for SRAM here because writes @@ -182,7 +183,16 @@ static void __init omap_detect_sram(void) omap_sram_size - SRAM_BOOTLOADER_SZ); omap_sram_size -= reserved; - omap_sram_ceil = omap_sram_base + omap_sram_size; + { + /* The first SRAM_BOOTLOADER_SZ of SRAM are reserved */ + void *base = (void *)omap_sram_base + SRAM_BOOTLOADER_SZ; + phys_addr_t phys = omap_sram_start + SRAM_BOOTLOADER_SZ; + size_t len = omap_sram_size - SRAM_BOOTLOADER_SZ; + + omap_pv_pool = pv_pool_create(base, phys, len, + ilog2(FNCPY_ALIGN)); + WARN_ON(!omap_pv_pool); + } } static struct map_desc omap_sram_io_desc[] __initdata = { @@ -242,26 +252,6 @@ static void __init omap_map_sram(void) omap_sram_size - SRAM_BOOTLOADER_SZ); } -/* - * Memory allocator for SRAM: calculates the new ceiling address - * for pushing a function using the fncpy API. - * - * Note that fncpy requires the returned address to be aligned - * to an 8-byte boundary. - */ -void *omap_sram_push_address(unsigned long size) -{ - if (size > (omap_sram_ceil - (omap_sram_base + SRAM_BOOTLOADER_SZ))) { - printk(KERN_ERR "Not enough space in SRAM\n"); - return NULL; - } - - omap_sram_ceil -= size; - omap_sram_ceil = ROUND_DOWN(omap_sram_ceil, FNCPY_ALIGN); - - return (void *)omap_sram_ceil; -} - #ifdef CONFIG_ARCH_OMAP1 static void (*_omap_sram_reprogram_clock)(u32 dpllctl, u32 ckctl); diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index daf6e77..2be3155 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -62,7 +62,7 @@ MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate"); struct uio_pruss_dev { struct uio_info *info; struct clk *pruss_clk; - dma_addr_t sram_paddr; + phys_addr_t sram_paddr; dma_addr_t ddr_paddr; void __iomem *prussio_vaddr; void *sram_vaddr; @@ -106,7 +106,7 @@ static void pruss_cleanup(struct platform_device *dev, gdev->ddr_paddr); } if (gdev->sram_vaddr) - sram_free(gdev->sram_vaddr, sram_pool_sz); + pv_pool_free(davinci_pv_pool, gdev->sram_vaddr, sram_pool_sz); kfree(gdev->info); clk_put(gdev->pruss_clk); kfree(gdev); @@ -152,7 +152,8 @@ static int __devinit pruss_probe(struct platform_device *dev) goto out_free; } - gdev->sram_vaddr = sram_alloc(sram_pool_sz, &(gdev->sram_paddr)); + gdev->sram_vaddr = pv_pool_alloc(davinci_pv_pool, sram_pool_sz, + &(gdev->sram_paddr)); if (!gdev->sram_vaddr) { dev_err(&dev->dev, "Could not allocate SRAM pool\n"); goto out_free; From tony at atomide.com Mon Apr 18 06:33:36 2011 From: tony at atomide.com (Tony Lindgren) Date: Mon, 18 Apr 2011 14:33:36 +0300 Subject: [RFC PATCH v2] Consolidate SRAM support In-Reply-To: <20110418085259.GA26044@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110418085259.GA26044@n2100.arm.linux.org.uk> Message-ID: <20110418113335.GL12272@atomide.com> * Russell King - ARM Linux [110418 11:50]: > This is the second revision of this patch. I've not moved it out of > ARM yet as I haven't had a positive response from SH yet. > > It's now called pv_pool (for phys/virt pool) rather than sram_pool, > and I've included MXC's iram support in this. Hopefully, if OMAP can > remove the FB stuff from SRAM we can clean the OMAP bits up a little > more. Neither have I sorted out the last reference to omap_sram_ceil. > Some comments from OMAP people on what's going on there would be good. I believe the omap_sram_ceil should also disappear with the omapfb SRAM patch. Tony From nsekhar at ti.com Mon Apr 18 11:12:10 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 18 Apr 2011 21:42:10 +0530 Subject: [RFC PATCH v2] Consolidate SRAM support In-Reply-To: <20110418085259.GA26044@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110418085259.GA26044@n2100.arm.linux.org.uk> Message-ID: Hi Russell, On Mon, Apr 18, 2011 at 14:22:59, Russell King - ARM Linux wrote: > This is the second revision of this patch. I've not moved it out of > ARM yet as I haven't had a positive response from SH yet. I was able to test this on DaVinci (DA850 suspend-to-RAM) with the following additional changes: There was a sram_free call remaining in pm.c file. diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c index 06eb0ff..0068e41 100644 --- a/arch/arm/mach-davinci/pm.c +++ b/arch/arm/mach-davinci/pm.c @@ -131,7 +131,8 @@ static int __init davinci_pm_probe(struct platform_device *pdev) static int __exit davinci_pm_remove(struct platform_device *pdev) { - sram_free(davinci_sram_suspend, davinci_cpu_suspend_sz); + pv_pool_free(davinci_pv_pool, davinci_sram_suspend, + davinci_cpu_suspend_sz); return 0; } The cpu suspend code on DaVinci was not aligned 8 to bytes and so the fncpy function was throwing a bug. diff --git a/arch/arm/mach-davinci/sleep.S b/arch/arm/mach-davinci/sleep.S index fb5e72b..dcb8e9d 100644 --- a/arch/arm/mach-davinci/sleep.S +++ b/arch/arm/mach-davinci/sleep.S @@ -50,6 +50,8 @@ * r4: contains virtual address of the DEEPSLEEP register */ ENTRY(davinci_cpu_suspend) + .align 3 + stmfd sp!, {r0-r12, lr} @ save registers on stack ldr ip, CACHE_FLUSH > > Lastly, uio_pruss should probably take the SRAM pool pointer via > > platform data so that it doesn't have to include Davinci specific > > includes. > > arch/arm/Kconfig | 2 + > arch/arm/common/Kconfig | 4 ++ > arch/arm/common/Makefile | 1 + > arch/arm/common/pv-pool.c | 69 +++++++++++++++++++++++++++ > arch/arm/include/asm/pv-pool.h | 20 ++++++++ > arch/arm/mach-davinci/da850.c | 2 +- > arch/arm/mach-davinci/dm355.c | 2 +- > arch/arm/mach-davinci/dm365.c | 2 +- > arch/arm/mach-davinci/dm644x.c | 2 +- > arch/arm/mach-davinci/dm646x.c | 2 +- > arch/arm/mach-davinci/include/mach/common.h | 2 +- > arch/arm/mach-davinci/include/mach/sram.h | 13 +---- > arch/arm/mach-davinci/pm.c | 12 +---- > arch/arm/mach-davinci/sram.c | 42 +++-------------- > arch/arm/plat-mxc/Kconfig | 2 +- > arch/arm/plat-mxc/include/mach/iram.h | 24 +++++++-- > arch/arm/plat-mxc/iram_alloc.c | 50 +++++--------------- > arch/arm/plat-omap/include/plat/sram.h | 17 ++++--- > arch/arm/plat-omap/sram.c | 34 +++++--------- > drivers/uio/uio_pruss.c | 7 ++- > 20 files changed, 171 insertions(+), 138 deletions(-) The davinci audio driver in sound/soc/davinci/davinci-pcm.c uses the sram allocator too and would need to be converted to the new API. Thanks, Sekhar From linux at arm.linux.org.uk Mon Apr 18 11:18:17 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Mon, 18 Apr 2011 17:18:17 +0100 Subject: [RFC PATCH v2] Consolidate SRAM support In-Reply-To: References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110418085259.GA26044@n2100.arm.linux.org.uk> Message-ID: <20110418161817.GB26044@n2100.arm.linux.org.uk> On Mon, Apr 18, 2011 at 09:42:10PM +0530, Nori, Sekhar wrote: > I was able to test this on DaVinci (DA850 suspend-to-RAM) with > the following additional changes: > > There was a sram_free call remaining in pm.c file. Hmm, wonder why my build for DaVinci didn't pick that up. Thanks for that and I'll merge it in. > diff --git a/arch/arm/mach-davinci/sleep.S b/arch/arm/mach-davinci/sleep.S > index fb5e72b..dcb8e9d 100644 > --- a/arch/arm/mach-davinci/sleep.S > +++ b/arch/arm/mach-davinci/sleep.S > @@ -50,6 +50,8 @@ > * r4: contains virtual address of the DEEPSLEEP register > */ > ENTRY(davinci_cpu_suspend) > + .align 3 > + I think you want the .align 3 on the line before ENTRY(). > The davinci audio driver in sound/soc/davinci/davinci-pcm.c uses > the sram allocator too and would need to be converted to the > new API. Ah, right - I'll sort that too. Slightly annoying that sound stuff is outside the drivers/ subdir. From bengardiner at nanometrics.ca Mon Apr 18 16:36:38 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 18 Apr 2011 17:36:38 -0400 Subject: [PATCH 0/6] i2c-davinci gpio pulsed SCL recovery with ICPFUNC In-Reply-To: References: Message-ID: Hi Sekhar, Thank you for reviewing the series. On Wed, Apr 13, 2011 at 12:25 PM, Nori, Sekhar wrote: > Hi Ben, > > On Wed, Apr 06, 2011 at 03:08:03, Ben Gardiner wrote: >> This series for the i2c-davinci driver implements both a register dump and >> a pulsed-SCL recovery of the bus on those controllers that have ICPFUNC >> registers which is, so far, the da850 and da830 based platforms. >> >> I2C "controller timed out" messages were being observed by both myself on the >> da850evm and Bastian Ruppert on some custom da850 hardware [1]. Originally I >> thought it was the existing pulse-SCL routine but was quickly proven wrong; my >> apologies to John Povey and Philby John for jumping to conclusions. >> >> A discussion was spawned on the e2e forums [2] where Brad Griffis was >> instrumental in the development of the recovery routine proposed by this patch >> series. It was pointed out by him that the da850's i2c controller has the >> ability to control the SDA and SCL pins as GPIOS through its ICPFUNC registers. >> The recovery routine implemented by the patch series toggles the SCL pin and >> reads the SDA state using the ICPFUNC registers. > > [...] > >> >> When creating this series I noticed that there are obvious similarities between >> the existing recovery routine implemented by Philby John and John Povey and the >> recovery routine proposed in this series. Testing was performed using the >> ICPFUNC regsiters to toggle SCL as prescribed by the existing routine and it >> was found that the recovery did not work. The method described initially by Brad >> Griffis had the initial state of SCL high and delays of 5us with a maximum > of >> 8 pulses with a check of SDA each time as compared to the existing routine with >> undetermined SCL initial state, 20us delays and a maximum of 8 pulses. I tested > > I wonder how these values (5us or 20us) are derived. The document[1] > referred to in the existing code says that "the master should send > 9 clock pulses". I think 5us suggested by Brad assumes a 100KHz > bus. If that is so, this number should be derived out of the bus_freq > platform data. > > [1] http://www.nxp.com/documents/user_manual/UM10204.pdf I'm not sure how the 20us in the existing method was derived -- I wonder if Philby John or John Povey could comment? I agree that the 5us suggested by Brad assumes a 100KHz bus -- I could certainly derive this delay from the bus_freq platform data for the new recovery procedure since board-da850-evm.c sets bus_freq to 100 kHz. But I was apprehensive to do so for the existing recovery procedure since the two (in-tree) boards that are assigning the scl_pin member are dm355 and dm644x which assign bus_freq values of 400kHz and 20kHz, respectively. These would require delays of 1.25us and 25us, respectively; neither of which is the hard-coded 20us. To complicate matters further, dm644x (20kHz) sets bus_delay to 100us with a comment that "the msp430 uses a slow bitbanged i2c implementation [...] which requires 100us of idle after i2c writes [...]." If I derived the clock-pulse delays from bus_freq the dm644x would probably still work but if the clock-pulse width was at all important for dm355 then the recovery procedure could fail there. I don't want to introduce instabilities into other platforms. >> and found that if I changed the initial state of SCL or the number of pulses >> (Bastian had success with 16) that the recovery did not occur as expected; > > Was this testing done with the original PinMuxed GPIO based approach or with > the new internal GPIO based approach? This testing was performed using the 'new' ICPFUNC-based GPIO toggling. >> furthermore Brad pointed out that it was important to check the state of SDA >> after each pulse to see if the slave had released the line. Indeed, adding the >> check of SDA resulted in a quicker recovery. On my da850evm, at least, the >> recovery took only one SCL pulse every time. > > Yes, the document referred to above suggests this too. > > I guess the existing recovery mechanism and the new ICPFUNC > based approach shouldn't be functionally different - correct? At the outset I had hoped so too; but what we have in the result is a method proven to work on the problem hardware here that is now different than the existing method. > In that case, it would really be worthwhile to fix the existing > recovery method as well. > >> >> It would be nice to consolidate the two recovery routines and -- since they >> are gpio-based -- put the shared recovery routine in i2c-algo-bit so it can >> also be used by bitbanging i2c masters. I did not undertake the former because >> I don't have access to the hardware on which the existing recovery routine was >> tested. > > I think if you see issues with existing code, you should just > go ahead and fix. AFAICT, the existing approach should have > worked for you. You can test on your hardware and those with > other hardware can test your patches as well and send their > acks. Ok -- I did not want to break behaviour on other hardware with my changes --but I respect your expert opinion coming from the experience you have in merging patches as a kernel mach maintainer. I will re-spin (not immeadiately -- I don't expect to be able to return to development of this patch series for another couple weeks) another version, this time replacing the existing recovery procedure with the tested method here. Also, allowing the individual da8xx boards to specify whether they enable the recovery procedure as you requested in reviewing 6/6 of this series. I would still prefer to do the extraction to i2c-algo-bit in a separate series. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From Jon.Povey at racelogic.co.uk Mon Apr 18 20:47:53 2011 From: Jon.Povey at racelogic.co.uk (Jon Povey) Date: Tue, 19 Apr 2011 02:47:53 +0100 Subject: [PATCH 0/6] i2c-davinci gpio pulsed SCL recovery with ICPFUNC In-Reply-To: Message-ID: <70E876B0EA86DD4BAF101844BC814DFE093FB40998@Cloud.RL.local> Ben Gardiner wrote: >>> When creating this series I noticed that there are obvious >>> similarities between the existing recovery routine implemented by >>> Philby John and John Povey > I'm not sure how the 20us in the existing method was derived -- I > wonder if Philby John or John Povey could comment? I've been a little bemused about why I am getting credited for I2C bus recovery work. I don't remember doing any work on that. I did a couple of patches to fix a race when setting up a TX, but those are, afaik, unrelated. All I know about the bus recovery stuff is looking at it a while back and thinking hmm, that seems to wiggle gpio without changing the pinmuxing, so it can't possibly work. -- Jon Povey jon.povey at racelogic.co.uk Racelogic is a limited company registered in England. Registered number 2743719 . Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network From bengardiner at nanometrics.ca Tue Apr 19 07:20:09 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 19 Apr 2011 08:20:09 -0400 Subject: [PATCH 0/6] i2c-davinci gpio pulsed SCL recovery with ICPFUNC In-Reply-To: <70E876B0EA86DD4BAF101844BC814DFE093FB40998@Cloud.RL.local> References: <70E876B0EA86DD4BAF101844BC814DFE093FB40998@Cloud.RL.local> Message-ID: Hi Jon, On Mon, Apr 18, 2011 at 9:47 PM, Jon Povey wrote: > Ben Gardiner wrote: > >>>> When creating this series I noticed that there are obvious >>>> similarities between the existing recovery routine implemented by >>>> Philby John and John Povey > >> I'm not sure how the 20us in the existing method was derived -- I >> wonder if Philby John or John Povey could comment? > > I've been a little bemused about why I am getting credited for I2C > bus recovery work. I don't remember doing any work on that. My mistake -- sorry. Also sorry for getting your name wrong repeatedly. > I did a couple of patches to fix a race when setting up a TX, but > those are, afaik, unrelated. In "i2c-davinci: Fix race when setting up for TX" and "i2c-davinci: Fix TX setup for more SoCs" you mention testing on DM355 -- is there any you have hardware on which the recovery procedure is executed on occasion and that you would be available to test modifications to the current implementation? > All I know about the bus recovery stuff is looking at it a while back > and thinking hmm, that seems to wiggle gpio without changing the > pinmuxing, so it can't possibly work. :) Probably not then. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From linux at arm.linux.org.uk Tue Apr 19 11:18:46 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Tue, 19 Apr 2011 17:18:46 +0100 Subject: [RFC PATCH v2] Consolidate SRAM support In-Reply-To: <20110419160135.GA4204@game.jcrosoft.org> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110418085259.GA26044@n2100.arm.linux.org.uk> <20110419160135.GA4204@game.jcrosoft.org> Message-ID: <20110419161846.GB24972@n2100.arm.linux.org.uk> On Tue, Apr 19, 2011 at 06:01:35PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote: > Hi, > > I do post a patch to add the support to specify a virt and phys > address to the generic allocator so the pv-pool.c is not needed > we can just use the generic fucntion You've talked about this before in the thread, but the patch never appeared. From linux at arm.linux.org.uk Tue Apr 19 18:20:38 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Wed, 20 Apr 2011 00:20:38 +0100 Subject: [RFC PATCH v2] Consolidate SRAM support In-Reply-To: <20110419190557.GB4204@game.jcrosoft.org> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110418085259.GA26044@n2100.arm.linux.org.uk> <20110419160135.GA4204@game.jcrosoft.org> <20110419161846.GB24972@n2100.arm.linux.org.uk> <20110419190557.GB4204@game.jcrosoft.org> Message-ID: <20110419232038.GC24972@n2100.arm.linux.org.uk> On Tue, Apr 19, 2011 at 09:05:57PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote: > On 17:18 Tue 19 Apr , Russell King - ARM Linux wrote: > > On Tue, Apr 19, 2011 at 06:01:35PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote: > > > Hi, > > > > > > I do post a patch to add the support to specify a virt and phys > > > address to the generic allocator so the pv-pool.c is not needed > > > we can just use the generic fucntion > > > > You've talked about this before in the thread, but the patch never appeared. > I forget to re-send it sorry > it's the mm tree One obvious issue here is that you're using 'unsigned long' for phys addresses. With LPAE we could start seeing SRAM outside of the 4GB PA range, and so would need this to be phys_addr_t. From khilman at ti.com Tue Apr 19 19:06:23 2011 From: khilman at ti.com (Kevin Hilman) Date: Tue, 19 Apr 2011 17:06:23 -0700 Subject: [PATCH] davinci: fix DEBUG_LL code for p2v changes Message-ID: <1303257983-24546-1-git-send-email-khilman@ti.com> Fixup davinci UART low-level debug code for new ARM generic p2v changes. Based on OMAP changes by Tony Lindgren Cc: Tony Lindgren Signed-off-by: Kevin Hilman --- arch/arm/mach-davinci/include/mach/debug-macro.S | 13 ++++++++----- arch/arm/mach-davinci/include/mach/serial.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/debug-macro.S b/arch/arm/mach-davinci/include/mach/debug-macro.S index 9f1befc..f8b7ea4 100644 --- a/arch/arm/mach-davinci/include/mach/debug-macro.S +++ b/arch/arm/mach-davinci/include/mach/debug-macro.S @@ -24,6 +24,9 @@ #define UART_SHIFT 2 +#define davinci_uart_v2p(x) ((x) - PAGE_OFFSET + PLAT_PHYS_OFFSET) +#define davinci_uart_p2v(x) ((x) - PLAT_PHYS_OFFSET + PAGE_OFFSET) + .pushsection .data davinci_uart_phys: .word 0 davinci_uart_virt: .word 0 @@ -34,7 +37,7 @@ davinci_uart_virt: .word 0 /* Use davinci_uart_phys/virt if already configured */ 10: mrc p15, 0, \rp, c1, c0 tst \rp, #1 @ MMU enabled? - ldreq \rp, =__virt_to_phys(davinci_uart_phys) + ldreq \rp, =davinci_uart_v2p(davinci_uart_phys) ldrne \rp, =davinci_uart_phys add \rv, \rp, #4 @ davinci_uart_virt ldr \rp, [\rp, #0] @@ -48,18 +51,18 @@ davinci_uart_virt: .word 0 tst \rp, #1 @ MMU enabled? /* Copy uart phys address from decompressor uart info */ - ldreq \rv, =__virt_to_phys(davinci_uart_phys) + ldreq \rv, =davinci_uart_v2p(davinci_uart_phys) ldrne \rv, =davinci_uart_phys ldreq \rp, =DAVINCI_UART_INFO - ldrne \rp, =__phys_to_virt(DAVINCI_UART_INFO) + ldrne \rp, =davinci_uart_p2v(DAVINCI_UART_INFO) ldr \rp, [\rp, #0] str \rp, [\rv] /* Copy uart virt address from decompressor uart info */ - ldreq \rv, =__virt_to_phys(davinci_uart_virt) + ldreq \rv, =davinci_uart_v2p(davinci_uart_virt) ldrne \rv, =davinci_uart_virt ldreq \rp, =DAVINCI_UART_INFO - ldrne \rp, =__phys_to_virt(DAVINCI_UART_INFO) + ldrne \rp, =davinci_uart_p2v(DAVINCI_UART_INFO) ldr \rp, [\rp, #4] str \rp, [\rv] diff --git a/arch/arm/mach-davinci/include/mach/serial.h b/arch/arm/mach-davinci/include/mach/serial.h index 8051110..c9e6ce1 100644 --- a/arch/arm/mach-davinci/include/mach/serial.h +++ b/arch/arm/mach-davinci/include/mach/serial.h @@ -22,7 +22,7 @@ * * This area sits just below the page tables (see arch/arm/kernel/head.S). */ -#define DAVINCI_UART_INFO (PHYS_OFFSET + 0x3ff8) +#define DAVINCI_UART_INFO (PLAT_PHYS_OFFSET + 0x3ff8) #define DAVINCI_UART0_BASE (IO_PHYS + 0x20000) #define DAVINCI_UART1_BASE (IO_PHYS + 0x20400) -- 1.7.4 From Jon.Povey at racelogic.co.uk Tue Apr 19 20:34:47 2011 From: Jon.Povey at racelogic.co.uk (Jon Povey) Date: Wed, 20 Apr 2011 02:34:47 +0100 Subject: [PATCH 0/6] i2c-davinci gpio pulsed SCL recovery with ICPFUNC In-Reply-To: Message-ID: <70E876B0EA86DD4BAF101844BC814DFE093FB40BBD@Cloud.RL.local> Ben Gardiner wrote: > In "i2c-davinci: Fix race when setting up for TX" and "i2c-davinci: > Fix TX setup for more SoCs" you mention testing on DM355 -- is there > any you have hardware on which the recovery procedure is executed on > occasion and that you would be available to test modifications to the > current implementation? Nope, sorry. I've never seen the bus lock up. -- Jon Povey jon.povey at racelogic.co.uk Racelogic is a limited company registered in England. Registered number 2743719 . Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network From tony at atomide.com Wed Apr 20 00:27:21 2011 From: tony at atomide.com (Tony Lindgren) Date: Wed, 20 Apr 2011 08:27:21 +0300 Subject: [RFC PATCH] Consolidate SRAM support In-Reply-To: <1303222615.32281.91.camel@deskari> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110416130929.GL4423@n2100.arm.linux.org.uk> <20110418064838.GE12272@atomide.com> <1303110011.2062.7.camel@deskari> <20110418081743.GH12272@atomide.com> <1303222615.32281.91.camel@deskari> Message-ID: <20110420052718.GA5918@atomide.com> * Tomi Valkeinen [110419 17:13]: > Hi Tony and Russell, > > On Mon, 2011-04-18 at 11:17 +0300, Tony Lindgren wrote: > > * Tomi Valkeinen [110418 09:57]: > > > > So, I can make a patch that removes the SRAM support from omapfb, and > > > queue it up for the next merge window. > > > > OK. That patch should probably go into Russell's tree along with > > other SRAM related patches to avoid conflicts. > > Here's a simple patch to remove SRAM support from omapfb. Or more > precisely, this just disables it. > > I started to remove the SRAM support from the drivers, but the patches > got quite big and will probably cause conflicts if they reside in > somebody else's tree than mine. So I believe it's easiest to divide SRAM > FB removal into this patch, handled by Russell (?), and then the rest, > handled by me. OK sounds like a plan. > Is there something else related to OMAP FB than the code removed in this > patch that is hindering the SRAM work? I think we can now also remove sram_ceil and omap_sram_push_address? Regards, Tony From lamiaposta71 at gmail.com Wed Apr 20 00:48:51 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Wed, 20 Apr 2011 07:48:51 +0200 Subject: being a maintainer inside a company Message-ID: I'm being involved in u-boot and kernel responsability inside my company from many years. I write code and there are other developers writing code that I need to coordinate. By now I have managed all patches incoming using a separated devel branch, validating those patches and after applying on master branch. The reason to do that was given by my company e-mail account (or probably the mail router) that is not able to send cleanly the body. How can I specify the router/? specifications in order to work in a human way? It is 'mutt' a good advice to select some patches incoming and apply automatically to the devel branch? Anybody else has faced and solved this kind of issue? Hoping in some good ideas, Raffaele -- www.opensurf.it From manjunath.hadli at ti.com Wed Apr 20 08:53:17 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 20 Apr 2011 19:23:17 +0530 Subject: [PATCH v18 08/13] davinci: eliminate use of IO_ADDRESS() on sysmod In-Reply-To: Message-ID: On Tue, Apr 05, 2011 at 16:28:33, Nori, Sekhar wrote: > Hi Manju, > > On Sat, Apr 02, 2011 at 15:13:17, Hadli, Manjunath wrote: > > Current devices.c file has a number of instances where > > IO_ADDRESS() is used for system module register access. Eliminate this > > in favor of a ioremap() based access. > > > > Consequent to this, a new global pointer davinci_sysmodbase has been > > introduced which gets initialized during the initialization of each > > relevant SoC > > > > Signed-off-by: Manjunath Hadli > > Acked-by: Sekhar Nori > > --- > > > diff --git a/arch/arm/mach-davinci/include/mach/hardware.h > > b/arch/arm/mach-davinci/include/mach/hardware.h > > index 414e0b9..2a6b560 100644 > > --- a/arch/arm/mach-davinci/include/mach/hardware.h > > +++ b/arch/arm/mach-davinci/include/mach/hardware.h > > @@ -21,6 +21,12 @@ > > */ > > #define DAVINCI_SYSTEM_MODULE_BASE 0x01C40000 > > > > +#ifndef __ASSEMBLER__ > > +extern void __iomem *davinci_sysmodbase; > > +#define DAVINCI_SYSMODULE_VIRT(x) (davinci_sysmodbase + (x)) > > +void davinci_map_sysmod(void); > > +#endif > > Russell has posted[1] that the hardware.h file should not be polluted with platform private stuff like this. > > Your patch 7/13 actually helped towards that goal, but this one takes us back. This patch cannot be used in the current form. > > Currently there are separate header files for dm644x, dm355, dm646x and dm365. I would like to start by removing unnecessary code from these files and trying to consolidate them into a single file. Done. I have consolidated all the headers for DM6446, Dm6467, DM365 and DM355 into a single header as per your suggestion. > > Example, the EMAC base address definitions in dm365.h should be moved into dm365.c. Similarly, there is a lot of VPIF specific stuff in dm646x.h which is not really specific to dm646x.h and so should probably be moved to include/media/ or arch/arm/mach-davinci/include/mach/vpif.h Done. > > Once consolidated into a single file, davinci_sysmodbase can be moved into that file. Done. > > Also, Russell has said[2] that at least for this merge window only consolidation and bug fixes will go through his tree. This means that as far as mach-davinci is concerned, the clean-up part of this series can go to 2.6.40 - but not the stuff which adds new support. > > Thanks, > Sekhar > > [1] http://www.spinics.net/lists/arm-kernel/msg120410.html > [2] http://www.spinics.net/lists/arm-kernel/msg120606.html > > From manjunath.hadli at ti.com Wed Apr 20 10:30:08 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 20 Apr 2011 21:00:08 +0530 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <201104071358.21451.laurent.pinchart@ideasonboard.com> Message-ID: Hi Laurent, Thank you for you very valuable and detailed comments. I have fixed a lot of your suggestions and there are a few questions I need more explanation for. I will send the fixed and updated patches as a follow-up after your clarifications. -Manju On Thu, Apr 07, 2011 at 17:28:20, Laurent Pinchart wrote: > Hi Manjunath, > > On Saturday 02 April 2011 11:40:49 Manjunath Hadli wrote: > > This is the display driver for Texas Instruments's DM644X family > > SoC. This patch contains the main implementation of the driver with the > > V4L2 interface. The driver implements the streaming model with > > support for both kernel allocated buffers and user pointers. It also > > implements all of the necessary IOCTLs necessary and supported by the > > video display device. > > > > Signed-off-by: Manjunath Hadli > > Acked-by: Muralidharan Karicheri > > Acked-by: Hans Verkuil > > [snip] > > > diff --git a/drivers/media/video/davinci/vpbe_display.c > > b/drivers/media/video/davinci/vpbe_display.c new file mode 100644 > > index 0000000..dde5f8a > > --- /dev/null > > +++ b/drivers/media/video/davinci/vpbe_display.c > > @@ -0,0 +1,2085 @@ > > +/* > > + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License as > > + * published by the Free Software Foundation version 2. > > + * > > + * This program is distributed WITHOUT ANY WARRANTY of any > > + * kind, whether express or implied; without even the implied warranty > > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > +#include > > +#include > > +#include > > +#include > > +#include > > That looks suspicious, do you really need to include linux/fs.h ? Fixed. > > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include "vpbe_venc_regs.h" > > + > > +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" > > + > > +static int debug; > > +static u32 video2_numbuffers = 3; > > +static u32 video3_numbuffers = 3; > > Why is the number of buffers set by a module parameter ? It should be > negotiated dynamically with REQBUFS. This is the minimum number of buffers to be allocated by the system as there is no scatter-gather mechanism in Davinci. To make sure of availability of a minimum numbers of buffers for the system which may not be available otherwise due to fragmentation, these are used. > > > +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) > > +#define VPBE_DEFAULT_NUM_BUFS 3 > > + > > +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; > > +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; > > Those two variables are assigned but never read. You either forgot to read > them, or to remove them. > > > +module_param(video2_numbuffers, uint, S_IRUGO); > > +module_param(video3_numbuffers, uint, S_IRUGO); > > +module_param(video2_bufsize, uint, S_IRUGO); > > +module_param(video3_bufsize, uint, S_IRUGO); > > +module_param(debug, int, 0644); > > + > > +static struct buf_config_params display_buf_config_params = { > > + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, > > + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, > > + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, > > + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, > > + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, > > + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, > > + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, > > +}; > > This also looks like something that shouldn't be hardcoded. > > > +static struct vpbe_device *vpbe_dev; > > +static struct osd_state *osd_device; > > No such global variables please. Pass the pointers around between functions > instead. Fixed. > > > +static int vpbe_display_nr[] = { 2, 3 }; > > Is there a reason to register video devices with a specific number ? > video_register_device() doesn't guarantee, that the requested number will be > used, so it looks a bit pointless to me. Ok. We are now not using the numbers. > > > +static struct v4l2_capability vpbe_display_videocap = { > > + .driver = VPBE_DISPLAY_DRIVER, > > + .bus_info = "platform", > > + .version = VPBE_DISPLAY_VERSION_CODE, > > + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, > > +}; > > Please fill v4l2_capability dynamically in vpbe_display_querycap() instead of > using a global variable. Addressed. v4l2_capability structure is filled in vpbe_display_querycap() > > > +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; > > This belongs to your vpbe_display_obj object, not to a global variable. addressed- layer_first_int is made part of struct vpbe_layer instead of global variable. > > > +static int venc_is_second_field() > > +{ > > + int ret = 0; > > + int val; > > + ret = v4l2_subdev_call(vpbe_dev->venc, > > + core, > > + ioctl, > > + VENC_GET_FLD, > > + &val); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in getting Field ID 0\n"); > > + } > > + return val; > > +} > > + > > +/* > > + * vpbe_display_isr() > > + * ISR function. It changes status of the displayed buffer, takes next > > buffer + * from the queue and sets its address in VPBE registers > > + */ > > +static void vpbe_display_isr(unsigned int event, void *disp_obj) > > You can pass a vpbe_display object directly tp vpbe_display_isr() instead of > using void *. Addressed. > > > +{ > > + unsigned long jiffies_time = get_jiffies_64(); > > + struct timeval timevalue; > > + int i, fid; > > + unsigned long addr = 0; > > + struct vpbe_display_obj *layer = NULL; > > No need to initialize addr and layer to NULL. Fixed. > > > + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; > > + > > + /* Convert time represention from jiffies to timeval */ > > + jiffies_to_timeval(jiffies_time, &timevalue); > > Please use ktime_get_ts() or ktime_get_real_ts() to get the timestamp. Fixed. Used do_gettimeofday(). > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + layer = disp_dev->dev[i]; > > + /* If streaming is started in this layer */ > > + if (!layer->started) > > + continue; > > + /* Check the field format */ > > + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && > > + (event & OSD_END_OF_FRAME)) { > > + /* Progressive mode */ > > + if (layer_first_int[i]) { > > + layer_first_int[i] = 0; > > + continue; > > + } > > + /* > > + * Mark status of the cur_frm to > > + * done and unlock semaphore on it > > + */ > > + > > + if (layer->cur_frm != layer->next_frm) { > > + layer->cur_frm->ts = timevalue; > > + layer->cur_frm->state = VIDEOBUF_DONE; > > Please use videobuf2. I would like to get to videobuf2 as a next set of changes. I want to get the Dm6446 driver fisrt, add it with Dm365 and do the videobuf2 later. I hope it is okay. > > > + wake_up_interruptible( > > + &layer->cur_frm->done); > > + /* Make cur_frm pointing to next_frm */ > > + layer->cur_frm = layer->next_frm; > > + } > > + /* Get the next buffer from buffer queue */ > > + spin_lock(&disp_dev->dma_queue_lock); > > + if (!list_empty(&layer->dma_queue)) { > > + layer->next_frm = > > + list_entry(layer->dma_queue.next, > > + struct videobuf_buffer, queue); > > + /* Remove that buffer from the buffer queue */ > > + list_del(&layer->next_frm->queue); > > Do the next statements until the end of the if () {} block need to be > protected by the spinlock ? Addressed. > > > + /* Mark status of the buffer as active */ > > + layer->next_frm->state = VIDEOBUF_ACTIVE; > > + > > + addr = videobuf_to_dma_contig(layer->next_frm); > > + osd_device->ops.start_layer(osd_device, > > + layer->layer_info.id, > > + addr, disp_dev->cbcr_ofst); > > + } > > + spin_unlock(&disp_dev->dma_queue_lock); > > + } else { > > + /* > > + * Interlaced mode > > + * If it is first interrupt, ignore it > > + */ > > + if (layer_first_int[i]) { > > + layer_first_int[i] = 0; > > + return; > > + } > > + > > + layer->field_id ^= 1; > > + if (event & OSD_FIRST_FIELD) > > + fid = 0; > > + else if (event & OSD_SECOND_FIELD) > > + fid = 1; > > Did you mean VENC_FIRST_FIELD and VENC_SECOND_FIELD ? Yes. Addresed. > > > + else > > + return; > > I don't think this can happen. Addressed. > > > + > > + /* > > + * If field id does not match with stored > > + * field id > > + */ > > + if (fid != layer->field_id) { > > + /* Make them in sync */ > > + if (0 == fid) > > + layer->field_id = fid; > > + > > + return; > > + } > > + /* > > + * device field id and local field id are > > + * in sync. If this is even field > > + */ > > + if (0 == fid) { > > + if (layer->cur_frm == layer->next_frm) > > + continue; > > + /* > > + * one frame is displayed If next frame is > > + * available, release cur_frm and move on > > + * copy frame display time > > + */ > > + layer->cur_frm->ts = timevalue; > > + /* Change status of the cur_frm */ > > + layer->cur_frm->state = VIDEOBUF_DONE; > > + /* unlock semaphore on cur_frm */ > > + wake_up_interruptible(&layer->cur_frm->done); > > + /* Make cur_frm pointing to next_frm */ > > + layer->cur_frm = layer->next_frm; > > + } else if (1 == fid) { /* odd field */ > > + > > + if (list_empty(&layer->dma_queue) > > + || (layer->cur_frm != layer->next_frm)) > > + continue; > > Indentation is wrong here. Addressed. > > Doesn't the list_empty() check need to be protected by a spinlock ? Addressed. > > > + > > + /* > > + * one field is displayed configure > > + * the next frame if it is available > > + * otherwise hold on current frame > > + * Get next from the buffer queue > > + */ > > + spin_lock(&disp_dev->dma_queue_lock); > > + layer->next_frm = list_entry( > > + layer->dma_queue.next, > > + struct videobuf_buffer, > > + queue); > > + > > + /* Remove that from the buffer queue */ > > + list_del(&layer->next_frm->queue); > > + > > Do the next statements need to be protected by the spinlock ? Addressed. > > > + /* Mark state of the frame to active */ > > + layer->next_frm->state = VIDEOBUF_ACTIVE; > > + addr = videobuf_to_dma_contig(layer->next_frm); > > + osd_device->ops.start_layer(osd_device, > > + layer->layer_info.id, > > + addr, > > + disp_dev->cbcr_ofst); > > + spin_unlock(&disp_dev->dma_queue_lock); > > + } > > + } > > + } > > +} > > + > > +/* interrupt service routine */ > > +static irqreturn_t venc_isr(int irq, void *arg) > > +{ > > + static unsigned last_event; > > + unsigned event = 0; > > + > > + if (venc_is_second_field()) > > + event |= VENC_SECOND_FIELD; > > + else > > + event |= VENC_FIRST_FIELD; > > + > > + if (event == (last_event & ~VENC_END_OF_FRAME)) { > > + /* > > + * If the display is non-interlaced, then we need to flag the > > + * end-of-frame event at every interrupt regardless of the > > + * value of the FIDST bit. We can conclude that the display is > > + * non-interlaced if the value of the FIDST bit is unchanged > > + * from the previous interrupt. > > + */ > > What about checking pix_fmt.field instead ? Not sure what you mean here. We get the FIRST or second field event from the hardware so we need to check the register value rather than pix_fmt.field. > > > + event |= VENC_END_OF_FRAME; > > + } else if (event == VENC_SECOND_FIELD) { > > + /* end-of-frame for interlaced display */ > > + event |= VENC_END_OF_FRAME; > > + } > > + last_event = event; > > + > > + vpbe_display_isr(event, arg); > > + return IRQ_HANDLED; > > +} > > [snip] > > > +static int vpbe_set_video_display_params(struct vpbe_display *disp_dev, > > + struct vpbe_display_obj *layer) > > This function seems to enable OSD. Shouldn't it be renamed it make that clear Addressed. > ? > > > +{ > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > + unsigned long addr; > > + int ret = 0; > > + > > + addr = videobuf_to_dma_contig(layer->cur_frm); > > + /* Set address in the display registers */ > > + osd_device->ops.start_layer(osd_device, > > + layer->layer_info.id, > > + addr, > > + disp_dev->cbcr_ofst); > > + > > + ret = osd_device->ops.enable_layer(osd_device, > > + layer->layer_info.id, 0); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in enabling osd window layer 0\n"); > > + return -1; > > + } > > + > > + /* Enable the window */ > > Could you please explain how this work ? I fail to understand the relationship > between layers, windows, OSD, ... OSD here refers to both the IP block which hosts the 4 windows or layers and the 2 RGB based windows/layers as part of the same. "osd_device->ops" is the collection of all the functions which deal with all the 4 "layers" and in general everything that is in the OSD block. > > > + layer->layer_info.enable = 1; > > + if (cfg->pixfmt == PIXFMT_NV12) { > > + struct vpbe_display_obj *otherlayer = > > + _vpbe_display_get_other_win(disp_dev, layer); > > The _vpbe_display_get_other_win() function returns a vpbe_display_obj that you > call otherlayer. Is it a layer, a window or an 'obj' ? Please keep the code > consistent and rename functions, types and variables where needed. Fixed. The name of vpbe_display_obj is changed to vpbe_layer > > > + > > + ret = osd_device->ops.enable_layer(osd_device, > > + otherlayer->layer_info.id, 1); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in enabling osd window layer 1\n"); > > + return -1; > > + } > > + otherlayer->layer_info.enable = 1; > > + } > > + return 0; > > +} > > + > > +static void > > +vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, > > + struct vpbe_display_obj *layer, > > + int expected_xsize, int expected_ysize) > > +{ > > + struct display_layer_info *layer_info = &layer->layer_info; > > + struct v4l2_pix_format *pixfmt = &layer->pix_fmt; > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; > > Please split the variable declaration on multiple lines, this gets hard to > read. Fixed. > > No need to initialize h_scale and v_scale to 0. > Fixed. > temp is a bad name. Fixed. > > > + v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id; > > + > > + /* > > + * Application initially set the image format. Current display > > + * size is obtained from the vpbe display controller. expected_xsize > > + * and expected_ysize are set through S_CROP ioctl. Based on this, > > + * driver will calculate the scale factors for vertical and > > + * horizontal direction so that the image is displayed scaled > > + * and expanded. Application uses expansion to display the image > > + * in a square pixel. Otherwise it is displayed using displays > > + * pixel aspect ratio.It is expected that application chooses > > + * the crop coordinates for cropped or scaled display. if crop > > + * size is less than the image size, it is displayed cropped or > > + * it is displayed scaled and/or expanded. > > + * > > + * to begin with, set the crop window same as expected. Later we > > + * will override with scaled window size > > + */ > > + > > + cfg->xsize = pixfmt->width; > > + cfg->ysize = pixfmt->height; > > + layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */ > > + layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */ > > + layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */ > > + layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */ > > + > > + if (pixfmt->width < expected_xsize) { > > + h_scale = vpbe_dev->current_timings.xres / pixfmt->width; > > + if (h_scale < 2) > > + h_scale = 1; > > + else if (h_scale >= 4) > > + h_scale = 4; > > + else > > + h_scale = 2; > > + cfg->xsize *= h_scale; > > + if (cfg->xsize < expected_xsize) { > > + if ((standard_id & V4L2_STD_525_60) || > > + (standard_id & V4L2_STD_625_50)) { > > + temp = (cfg->xsize * > > + VPBE_DISPLAY_H_EXP_RATIO_N) / > > + VPBE_DISPLAY_H_EXP_RATIO_D; > > + if (temp <= expected_xsize) { > > + h_exp = 1; > > + cfg->xsize = temp; > > + } > > + } > > + } > > + if (h_scale == 2) > > + layer_info->h_zoom = ZOOM_X2; > > + else if (h_scale == 4) > > + layer_info->h_zoom = ZOOM_X4; > > + if (h_exp) > > + layer_info->h_exp = H_EXP_9_OVER_8; > > + } else { > > + /* no scaling, only cropping. Set display area to crop area */ > > + cfg->xsize = expected_xsize; > > + } > > + > > + if (pixfmt->height < expected_ysize) { > > + v_scale = expected_ysize / pixfmt->height; > > + if (v_scale < 2) > > + v_scale = 1; > > + else if (v_scale >= 4) > > + v_scale = 4; > > + else > > + v_scale = 2; > > + cfg->ysize *= v_scale; > > + if (cfg->ysize < expected_ysize) { > > + if ((standard_id & V4L2_STD_625_50)) { > > + temp = (cfg->ysize * > > + VPBE_DISPLAY_V_EXP_RATIO_N) / > > + VPBE_DISPLAY_V_EXP_RATIO_D; > > + if (temp <= expected_ysize) { > > + v_exp = 1; > > + cfg->ysize = temp; > > + } > > + } > > + } > > + if (v_scale == 2) > > + layer_info->v_zoom = ZOOM_X2; > > + else if (v_scale == 4) > > + layer_info->v_zoom = ZOOM_X4; > > + if (v_exp) > > + layer_info->h_exp = V_EXP_6_OVER_5; > > + } else { > > + /* no scaling, only cropping. Set display area to crop area */ > > + cfg->ysize = expected_ysize; > > + } > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "crop display xsize = %d, ysize = %d\n", > > + cfg->xsize, cfg->ysize); > > +} > > + > > +static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, > > + struct vpbe_display_obj *layer, > > + int top, int left) > > +{ > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > + > > + cfg->xpos = cfg->ypos = 0; > > Please split this on two lines. Fixed. > > > + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) > > + cfg->xpos = left; > > + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) > > + cfg->ypos = top; > > Shouldn't you set cfg->xpos to min(left, vpbe_dev->current_timings.xres - cfg- > >xsize) unconditionally instead ? Same for ypos. Fixed. min() used for both cfg->xpos and cfg->ypos. > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "new xpos = %d, ypos = %d\n", > > + cfg->xpos, cfg->ypos); > > +} > > + > > +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, > > + struct v4l2_rect *c) > > +{ > > + if ((c->width == 0) || > > + ((c->width + c->left) > vpbe_dev->current_timings.xres) || > > + (c->height == 0) || ((c->height + c->top) > > > + vpbe_dev->current_timings.yres)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); > > + return -1; > > + } > > + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "window height must be even for interlaced display\n"); > > + return -1; > > + } > > Instead of returning an error when crop setting are invalid, you should adjust > them are return the adjusted value to userspace. Fixed. > > > + return 0; > > +} > > + > > +/** > > + * vpbe_try_format() > > + * If user application provides width and height, and have bytesperline > > set > > + * to zero, driver calculates bytesperline and sizeimage based on hardware > > + * limits. If application likes to add pads at the end of each line and > > + * end of the buffer , it can set bytesperline to line size and sizeimage > > to > > + * bytesperline * height of the buffer. If driver fills zero for active > > + * video width and height, and has requested user bytesperline and > > sizeimage, > > + * width and height is adjusted to maximum display limit or buffer width > > + * height which ever is lower > > Filling width and height based on the bytesperline and sizeimage fields isn't > a standard V4L2 behaviour. Do you have a use case for that ? No. It will be removed. > > > + */ > > +static int vpbe_try_format(struct vpbe_display *disp_dev, > > + struct v4l2_pix_format *pixfmt, int check) > > +{ > > + int min_sizeimage, bpp, min_height = 1, min_width = 32, > > + max_width, max_height, user_info = 0; > > Split this please. > > > + > > + if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) && > > + (pixfmt->pixelformat != V4L2_PIX_FMT_NV12)) > > + /* choose default as V4L2_PIX_FMT_UYVY */ > > + pixfmt->pixelformat = V4L2_PIX_FMT_UYVY; > > + > > + /* Check the field format */ > > + if (pixfmt->field == V4L2_FIELD_ANY) { > > + if (vpbe_dev->current_timings.interlaced) > > + pixfmt->field = V4L2_FIELD_INTERLACED; > > + else > > + pixfmt->field = V4L2_FIELD_NONE; > > + } > > + > > + if (pixfmt->field == V4L2_FIELD_INTERLACED) > > + min_height = 2; > > + > > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) > > + bpp = 1; > > + else > > + bpp = 2; > > + > > + max_width = vpbe_dev->current_timings.xres; > > + max_height = vpbe_dev->current_timings.yres; > > + > > + min_width /= bpp; > > + > > + if (!pixfmt->width && !pixfmt->bytesperline) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" > > + " cannot be zero\n"); > > + return -EINVAL; > > Don't fail, just set sane default values. This comment applies to the rest of > the function as well. Fixed. > > > + } > > + > > + /* if user provided bytesperline, it must provide sizeimage as well */ > > + if (pixfmt->bytesperline && !pixfmt->sizeimage) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "sizeimage must be non zero, when user" > > + " provides bytesperline\n"); > > + return -EINVAL; > > If sizeimage is not provided, the driver should compute it. Fixed. > > > + } > > + > > + /* adjust bytesperline as per hardware - multiple of 32 */ > > + if (!pixfmt->width) > > + pixfmt->width = pixfmt->bytesperline / bpp; > > + > > + if (!pixfmt->bytesperline) > > + pixfmt->bytesperline = pixfmt->width * bpp; > > + else > > + user_info = 1; > > + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); > > + > > + if (pixfmt->width < min_width) { > > + if (check) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "height is less than minimum," > > + "input width = %d, min_width = %d\n", > > + pixfmt->width, min_width); > > + return -EINVAL; > > + } > > + pixfmt->width = min_width; > > + } > > + > > + if (pixfmt->width > max_width) { > > + if (check) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "width is more than maximum," > > + "input width = %d, max_width = %d\n", > > + pixfmt->width, max_width); > > + return -EINVAL; > > + } > > + pixfmt->width = max_width; > > + } > > + > > + /* > > + * If height is zero, then atleast we need to have sizeimage > > + * to calculate height > > + */ > > + if (!pixfmt->height) { > > + if (user_info) { > > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { > > + /* > > + * for NV12 format, sizeimage is y-plane size > > + * + CbCr plane which is half of y-plane > > + */ > > + pixfmt->height = pixfmt->sizeimage / > > + (pixfmt->bytesperline + > > + (pixfmt->bytesperline >> 1)); > > + } else > > + pixfmt->height = pixfmt->sizeimage/ > > + pixfmt->bytesperline; > > + } > > + } > > + > > + if (pixfmt->height > max_height) { > > + if (check && !user_info) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "height is more than maximum," > > + "input height = %d, max_height = %d\n", > > + pixfmt->height, max_height); > > + return -EINVAL; > > + } > > + pixfmt->height = max_height; > > + } > > + > > + if (pixfmt->height < min_height) { > > + if (check && !user_info) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "width is less than minimum," > > + "input height = %d, min_height = %d\n", > > + pixfmt->height, min_height); > > + return -EINVAL; > > + } > > + pixfmt->height = min_width; > > + } > > + > > + /* if user has not provided bytesperline calculate it based on width */ > > + if (!user_info) > > + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); > > + > > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) > > + min_sizeimage = pixfmt->bytesperline * pixfmt->height + > > + (pixfmt->bytesperline * pixfmt->height >> 1); > > + else > > + min_sizeimage = pixfmt->bytesperline * pixfmt->height; > > + > > + if (pixfmt->sizeimage < min_sizeimage) { > > + if (check && user_info) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", > > + min_sizeimage); > > + return -EINVAL; > > + } > > + pixfmt->sizeimage = min_sizeimage; > > + } > > + return 0; > > +} > > [snip] > > > +static int vpbe_display_querycap(struct file *file, void *priv, > > + struct v4l2_capability *cap) > > +{ > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); > > Do you really need a debugging call here ? I am Ok either ways. When debugging is enabled, it is just one of the data points since there are multiple windows. > > > + *cap = vpbe_display_videocap; > > + > > + return 0; > > +} > > + > > +static int vpbe_display_s_crop(struct file *file, void *priv, > > + struct v4l2_crop *crop) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); > > + > > + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { > > Do it the other way around. If crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT return > an error. That will save an indentation level for the rest of the function. > This comment appleis to the whole driver, you have several occurences of the > same issue (such as in vpbe_display_g_crop()). Addressed. > > > + struct v4l2_rect *rect = &crop->c; > > + > > + if (rect->top < 0 || rect->left < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); > > + return -EINVAL; > > + } > > Don't fail, adjust the values. Done. > > > + > > + if (vpbe_disp_check_window_params(disp_dev, rect)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); > > + return -EINVAL; > > + } > > + osd_device->ops.get_layer_config(osd_device, > > + layer->layer_info.id, cfg); > > + > > + vpbe_disp_calculate_scale_factor(disp_dev, layer, > > + rect->width, > > + rect->height); > > + vpbe_disp_adj_position(disp_dev, layer, rect->top, > > + rect->left); > > + ret = osd_device->ops.set_layer_config(osd_device, > > + layer->layer_info.id, cfg); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in set layer config:\n"); > > + return -EINVAL; > > + } > > + > > + /* apply zooming and h or v expansion */ > > + osd_device->ops.set_zoom(osd_device, > > + layer->layer_info.id, > > + layer->layer_info.h_zoom, > > + layer->layer_info.v_zoom); > > + ret = osd_device->ops.set_vid_expansion(osd_device, > > + layer->layer_info.h_exp, > > + layer->layer_info.v_exp); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in set vid expansion:\n"); > > + return -EINVAL; > > + } > > + > > + if ((layer->layer_info.h_zoom != ZOOM_X1) || > > + (layer->layer_info.v_zoom != ZOOM_X1) || > > + (layer->layer_info.h_exp != H_EXP_OFF) || > > + (layer->layer_info.v_exp != V_EXP_OFF)) > > + /* Enable expansion filter */ > > + osd_device->ops.set_interpolation_filter(osd_device, 1); > > + else > > + osd_device->ops.set_interpolation_filter(osd_device, 0); > > + > > + } else { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); > > + return -EINVAL; > > + } > > + > > + return ret; > > +} > > [snip] > > > +static int vpbe_display_g_fmt(struct file *file, void *priv, > > + struct v4l2_format *fmt) > > +{ > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_G_FMT, layer id = %d\n", > > + layer->device_id); > > + > > + /* If buffer type is video output */ > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > + /* Fill in the information about format */ > > + *pixfmt = layer->pix_fmt; > > I don't see anything wrong in doing > > fmt->fmt.pix = layer->pix_fmt; > > directly. Wel,. In the past patches we had a large amount of multiple indirections, and as part of a suggestion on open source I removed all of them with a general rule of not having more than 2 indirections. What is your take on this? How many indirection levels do you allow? > > > + } else { > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > +static int vpbe_display_enum_fmt(struct file *file, void *priv, > > + struct v4l2_fmtdesc *fmt) > > +{ > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + unsigned int index = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_ENUM_FMT, layer id = %d\n", > > + layer->device_id); > > + if (fmt->index > 0) { > > You support two formats below, should this be fmt->index > 1 ? Thanks. > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n"); > > + return -EINVAL; > > + } > > + > > + /* Fill in the information about format */ > > + index = fmt->index; > > + memset(fmt, 0, sizeof(*fmt)); > > + fmt->index = index; > > + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; > > + if (index == 0) { > > + strcpy(fmt->description, "YUV 4:2:2 - UYVY"); > > + fmt->pixelformat = V4L2_PIX_FMT_UYVY; > > + } else if (index == 1) { > > A simple else will be enough. OK. > > > + strcpy(fmt->description, "Y/CbCr 4:2:0"); > > + fmt->pixelformat = V4L2_PIX_FMT_NV12; > > + } > > + return 0; > > +} > > + > > +static int vpbe_display_s_fmt(struct file *file, void *priv, > > + struct v4l2_format *fmt) > > +{ > > + int ret = 0; > > No need to initialize ret to 0. Ok. > > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + struct vpbe_display_obj *layer = fh->layer; > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > Variables are often declared in longuest to shortest line order in kernel > drivers. It might not be a requirement though, but I find it to make code more > readable. cannot do as suggested since the structure variable assignments have dependency on previous structure variables. > > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_S_FMT, layer id = %d\n", > > + layer->device_id); > > + > > + /* If streaming is started, return error */ > > + if (layer->started) { > > I'm pretty sure there's a race condition here. Not sure about this. The entire driver is under V4L2 lock per layer handle and it would not allow another call to come here once in. Can you elaborate how is it a race condition? > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > + return -EBUSY; > > + } > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); > > + return -EINVAL; > > + } else { > > No need for an else as the first branch of the if returns. Ok. > > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > + /* Check for valid pixel format */ > > + ret = vpbe_try_format(disp_dev, pixfmt, 1); > > + if (ret) > > + return ret; > > + > > + /* YUV420 is requested, check availability of the > > + other video window */ > > + > > + layer->pix_fmt = *pixfmt; > > There's probably a race condition here as well if two applications (or > threads) call S_FMT at the same time. Same question as above. > > > + > > + /* Get osd layer config */ > > + osd_device->ops.get_layer_config(osd_device, > > + layer->layer_info.id, cfg); > > + /* Store the pixel format in the layer object */ > > + cfg->xsize = pixfmt->width; > > + cfg->ysize = pixfmt->height; > > + cfg->line_length = pixfmt->bytesperline; > > + cfg->ypos = 0; > > + cfg->xpos = 0; > > + cfg->interlaced = vpbe_dev->current_timings.interlaced; > > + > > + /* Change of the default pixel format for both video windows */ > > + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { > > + struct vpbe_display_obj *otherlayer; > > If the requested format isn't NV12, cfg->pixfmt won't be modified. If it has > been set to NV12 by a previous S_FMT call, it won't become YUYV. Is that > intentional ? It is reset to YUYV as part of Open. So it is changed only if it is NV12. > > > + cfg->pixfmt = PIXFMT_NV12; > > + otherlayer = _vpbe_display_get_other_win(disp_dev, > > + layer); > > + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; > > + } > > + > > + /* Set the layer config in the osd window */ > > + ret = osd_device->ops.set_layer_config(osd_device, > > + layer->layer_info.id, cfg); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in S_FMT params:\n"); > > + return -EINVAL; > > + } > > + > > + /* Readback and fill the local copy of current pix format */ > > + osd_device->ops.get_layer_config(osd_device, > > + layer->layer_info.id, cfg); > > + > > + /* verify if readback values are as expected */ > > + if (layer->pix_fmt.width != cfg->xsize || > > + layer->pix_fmt.height != cfg->ysize || > > + layer->pix_fmt.bytesperline != layer->layer_info. > > + config.line_length || (cfg->interlaced && > > + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || > > + (!cfg->interlaced && layer->pix_fmt.field != > > + V4L2_FIELD_NONE)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "mismatch:layer conf params:\n"); > > + return -EINVAL; > > Can this happen ? If so, why ? It was a safety check. Removed as there is no need for it. > > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int vpbe_display_try_fmt(struct file *file, void *priv, > > + struct v4l2_format *fmt) > > +{ > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); > > + > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > + /* Check for valid field format */ > > + return vpbe_try_format(disp_dev, pixfmt, 0); > > Other way around here as well please. Return an error if the type is not > correct, and continue otherwise. Addressed. > > > + } > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > + return -EINVAL; > > +} > > + > > +/** > > + * vpbe_display_s_std - Set the given standard in the encoder > > + * > > + * Sets the standard if supported by the current encoder. Return the > > status. + * 0 - success & -EINVAL on error > > + */ > > +static int vpbe_display_s_std(struct file *file, void *priv, > > + v4l2_std_id *std_id) > > +{ > > + struct vpbe_fh *fh = priv; > > + struct vpbe_display_obj *layer = fh->layer; > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); > > + > > + /* If streaming is started, return error */ > > + if (layer->started) { > > Race condition here too. How? > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > + return -EBUSY; > > + } > > + if (NULL != vpbe_dev->ops.s_std) { > > + ret = vpbe_dev->ops.s_std(vpbe_dev, std_id); > > + if (ret) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Failed to set standard for sub devices\n"); > > + return -EINVAL; > > + } > > + } > > + return 0; > > +} > > + > > +/** > > + * vpbe_display_g_std - Get the standard in the current encoder > > + * > > + * Get the standard in the current encoder. Return the status. 0 - success > > + * -EINVAL on error > > + */ > > +static int vpbe_display_g_std(struct file *file, void *priv, > > + v4l2_std_id *std_id) > > +{ > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); > > + > > + /* Get the standard from the current encoder */ > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > > + *std_id = vpbe_dev->current_timings.timings.std_id; > > + return 0; > > + } > > + return -EINVAL; > > Where do you set timings_type ? When can this return an error ? It can return an error if the driver is set to a DV_PRESET mode. timings_type would tell whether is is an SD standard or a DV_PRESET. > > > +} > > + > > +/** > > + * vpbe_display_enum_output - enumerate outputs > > + * > > + * Enumerates the outputs available at the vpbe display > > + * returns the status, -EINVAL if end of output list > > + */ > > +static int vpbe_display_enum_output(struct file *file, void *priv, > > + struct v4l2_output *output) > > +{ > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); > > + > > + /* Enumerate outputs */ > > + > > + if (NULL != vpbe_dev->ops.enum_outputs) { > > + ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); > > + if (ret) { > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "Failed to enumerate outputs\n"); > > + return -EINVAL; > > + } > > + } > > + return 0; > > Shouldn't this return an error if there's no enum_outputs operation ? Addressed. > > > +} > > + > > +/** > > + * vpbe_display_s_output - Set output to > > + * the output specified by the index > > + */ > > +static int vpbe_display_s_output(struct file *file, void *priv, > > + unsigned int i) > > +{ > > + struct vpbe_fh *fh = priv; > > + struct vpbe_display_obj *layer = fh->layer; > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n"); > > + /* If streaming is started, return error */ > > + if (layer->started) { > > Race condition. > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > + return -EBUSY; > > + } > > + if (NULL != vpbe_dev->ops.set_output) { > > + ret = vpbe_dev->ops.set_output(vpbe_dev, i); > > + if (ret) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Failed to set output for sub devices\n"); > > + return -EINVAL; > > + } > > + } > > + return ret; > > Shouldn't this return an error if there's no set_output operation ? Addressed. > > > +} > > [snip] > > > +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, > > + struct vpbe_display *disp_dev) > > +{ > > + int err = 0; > > No need to initialize err to 0. OK. > > > + struct osd_layer_config *layer_config; > > + struct vpbe_display_obj *layer = disp_dev->dev[id]; > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > + > > + /* First claim the layer for this device */ > > + err = osd_device->ops.request_layer(osd_device, > > + layer->layer_info.id); > > + if (err < 0) { > > + /* Couldn't get layer */ > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Display Manager failed to allocate layer\n"); > > + return -EBUSY; > > + } > > + > > + layer_config = cfg; > > + /* Set the default image and crop values */ > > + layer_config->pixfmt = PIXFMT_YCbCrI; > > + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; > > + layer->pix_fmt.bytesperline = layer_config->line_length = > > + vpbe_dev->current_timings.xres * 2; > > + > > + layer->pix_fmt.width = layer_config->xsize = > > + vpbe_dev->current_timings.xres; > > + layer->pix_fmt.height = layer_config->ysize = > > + vpbe_dev->current_timings.yres; > > + layer->pix_fmt.sizeimage = > > + layer->pix_fmt.bytesperline * layer->pix_fmt.height; > > + layer_config->xpos = 0; > > + layer_config->ypos = 0; > > + layer_config->interlaced = vpbe_dev->current_timings.interlaced; > > You shouldn't reinitialized the format every time the device is opened. The > previously set format should be kept. This strictly followed across drivers? I am Ok if that is the expectation. > > > + > > + /* > > + * turn off ping-pong buffer and field inversion to fix > > + * the image shaking problem in 1080I mode > > + */ > > + > > + if (cfg->interlaced) > > + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; > > + else > > + layer->pix_fmt.field = V4L2_FIELD_NONE; > > + > > + err = osd_device->ops.set_layer_config(osd_device, > > + layer->layer_info.id, > > + layer_config); > > + if (err < 0) { > > + /* Couldn't set layer */ > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Display Manager failed to set osd layer\n"); > > + return -EBUSY; > > + } > > + > > + return 0; > > +} > > + > > +/* > > + * vpbe_display_open() > > + * It creates object of file handle structure and stores it in > > private_data + * member of filepointer > > + */ > > +static int vpbe_display_open(struct file *file) > > +{ > > + int minor = iminor(file->f_path.dentry->d_inode); > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + struct vpbe_display_obj *layer; > > + struct vpbe_fh *fh = NULL; > > + int found = -1; > > + int i = 0; > > + > > + /* Check for valid minor number */ > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + /* Get the pointer to the layer object */ > > + layer = disp_dev->dev[i]; > > + if (minor == layer->video_dev->minor) { > > + found = i; > > + break; > > + } > > + } > > Don't rely on minors, store the vpbe_display_obj object into the video device > private data using video_set_drvdata() at registration time, and retrieve it > here with video_drvdata(). Addressed. > > > + > > + /* If not found, return error no device */ > > + if (0 > found) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); > > + return -ENODEV; > > + } > > + > > + /* Allocate memory for the file handle object */ > > + fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL); > > + if (fh == NULL) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "unable to allocate memory for file handle object\n"); > > + return -ENOMEM; > > + } > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "vpbe display open plane = %d\n", > > + layer->device_id); > > + > > + /* store pointer to fh in private_data member of filep */ > > + file->private_data = fh; > > + fh->layer = layer; > > + fh->disp_dev = disp_dev; > > + > > + if (!layer->usrs) { > > + /* Configure the default values for the layer */ > > + if (vpbe_display_cfg_layer_default(layer->device_id, > > + disp_dev)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Unable to configure video layer" > > + " for id = %d\n", layer->device_id); > > + return -EINVAL; > > Memory leak, you need to free fh. Fixed. > > > + } > > + } > > + /* Increment layer usrs counter */ > > + layer->usrs++; > > Race condition. > > > + /* Set io_allowed member to false */ > > + fh->io_allowed = 0; > > + /* Initialize priority of this instance to default priority */ > > + fh->prio = V4L2_PRIORITY_UNSET; > > + v4l2_prio_open(&layer->prio, &fh->prio); > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "vpbe display device opened successfully\n"); > > + return 0; > > +} > > + > > +/* > > + * vpbe_display_release() > > + * This function deletes buffer queue, frees the buffers and the davinci > > + * display file * handle > > + */ > > +static int vpbe_display_release(struct file *file) > > +{ > > + /* Get the layer object and file handle object */ > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); > > + /* If this is doing IO and other layer are not closed */ > > + if ((layer->usrs != 1) && fh->io_allowed) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); > > + return -EAGAIN; > > Don't do that. You can't deny a close() call because other file handles are > still present. You need to handle that inside the driver. Addressed. > > > + } > > + > > + /* if this instance is doing IO */ > > + if (fh->io_allowed) { > > + /* Reset io_usrs member of layer object */ > > + layer->io_usrs = 0; > > + > > + osd_device->ops.disable_layer(osd_device, > > + layer->layer_info.id); > > + layer->started = 0; > > + /* Free buffers allocated */ > > + videobuf_queue_cancel(&layer->buffer_queue); > > + videobuf_mmap_free(&layer->buffer_queue); > > + } > > + > > + /* Decrement layer usrs counter */ > > + layer->usrs--; > > + /* If this file handle has initialize encoder device, reset it */ > > + if (!layer->usrs) { > > + if (cfg->pixfmt == PIXFMT_NV12) { > > + struct vpbe_display_obj *otherlayer; > > + otherlayer = > > + _vpbe_display_get_other_win(disp_dev, layer); > > + osd_device->ops.disable_layer(osd_device, > > + otherlayer->layer_info.id); > > + osd_device->ops.release_layer(osd_device, > > + otherlayer->layer_info.id); > > + } > > + osd_device->ops.disable_layer(osd_device, > > + layer->layer_info.id); > > + osd_device->ops.release_layer(osd_device, > > + layer->layer_info.id); > > + } > > + /* Close the priority */ > > + v4l2_prio_close(&layer->prio, fh->prio); > > + file->private_data = NULL; > > + > > + /* Free memory allocated to file handle object */ > > + kfree(fh); > > + > > + disp_dev->cbcr_ofst = 0; > > + > > + return 0; > > +} > > [snip] > > > +/* > > + * vpbe_display_probe() > > + * This function creates device entries by register itself to the V4L2 > > driver + * and initializes fields of each layer objects > > + */ > > +static __devinit int vpbe_display_probe(struct platform_device *pdev) > > +{ > > + int i, j = 0, k, err = 0; > > + struct vpbe_display *disp_dev; > > + struct video_device *vbd = NULL; > > + struct vpbe_display_obj *vpbe_display_layer = NULL; > > + struct resource *res; > > + int irq; > > + > > + printk(KERN_DEBUG "vpbe_display_probe\n"); > > + > > + /* Allocate memory for vpbe_display */ > > + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); > > + if (!disp_dev) { > > + printk(KERN_ERR "ran out of memory\n"); > > + return -ENOMEM; > > + } > > + > > + /* Allocate memory for four plane display objects */ > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + disp_dev->dev[i] = > > + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); > > + /* If memory allocation fails, return error */ > > + if (!disp_dev->dev[i]) { > > + printk(KERN_ERR "ran out of memory\n"); > > + err = -ENOMEM; > > + goto probe_out; > > + } > > + spin_lock_init(&disp_dev->dev[i]->irqlock); > > + mutex_init(&disp_dev->dev[i]->opslock); > > + } > > + spin_lock_init(&disp_dev->dma_queue_lock); > > + > > + err = init_vpbe_layer_objects(i); > > + if (err) { > > + printk(KERN_ERR "Error initializing vpbe display\n"); > > + return err; > > + } > > + > > + /* > > + * Scan all the platform devices to find the vpbe > > + * controller device and get the vpbe_dev object > > + */ > > + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, > > + vpbe_device_get); > > + if (err < 0) > > + return err; > > + > > + /* Initialize the vpbe display controller */ > > + if (NULL != vpbe_dev->ops.initialize) { > > + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); > > + if (err) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); > > + err = -ENOMEM; > > + goto probe_out; > > + } > > + } > > + > > + /* check the name of davinci device */ > > + if (vpbe_dev->cfg->module_name != NULL) > > + strcpy(vpbe_display_videocap.card, > > + vpbe_dev->cfg->module_name); > > + > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > Please move the content of this loop to a separate function (but not the loop > itself), it will make the code more readable. Done. > > > + /* Get the pointer to the layer object */ > > + vpbe_display_layer = disp_dev->dev[i]; > > + /* Allocate memory for video device */ > > + vbd = video_device_alloc(); > > + if (vbd == NULL) { > > + for (j = 0; j < i; j++) { > > + video_device_release( > > + disp_dev->dev[j]->video_dev); > > + } > > + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); > > + err = -ENOMEM; > > + goto probe_out; > > + } > > + /* Initialize field of video device */ > > + vbd->release = video_device_release; > > + vbd->fops = &vpbe_fops; > > + vbd->ioctl_ops = &vpbe_ioctl_ops; > > + vbd->minor = -1; > > + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; > > + vbd->lock = &vpbe_display_layer->opslock; > > + > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > > + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); > > + vbd->current_norm = > > + vpbe_dev->current_timings.timings.std_id; > > + } else > > + vbd->current_norm = 0; > > + > > + snprintf(vbd->name, sizeof(vbd->name), > > + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", > > + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, > > + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, > > + (VPBE_DISPLAY_VERSION_CODE) & 0xff); > > + > > + /* Set video_dev to the video device */ > > + vpbe_display_layer->video_dev = vbd; > > + vpbe_display_layer->device_id = i; > > + > > + vpbe_display_layer->layer_info.id = > > + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); > > + if (display_buf_config_params.numbuffers[i] == 0) > > + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; > > + else > > + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; > > + > > + /* Initialize field of the display layer objects */ > > + vpbe_display_layer->usrs = 0; > > + vpbe_display_layer->io_usrs = 0; > > + vpbe_display_layer->started = 0; > > + > > + /* Initialize prio member of layer object */ > > + v4l2_prio_init(&vpbe_display_layer->prio); > > + > > + /* Register video device */ > > + v4l2_info(&vpbe_dev->v4l2_dev, > > + "Trying to register VPBE display device.\n"); > > + v4l2_info(&vpbe_dev->v4l2_dev, > > + "layer=%x,layer->video_dev=%x\n", > > + (int)vpbe_display_layer, > > + (int)&vpbe_display_layer->video_dev); > > + > > + err = video_register_device(vpbe_display_layer-> > > + video_dev, > > + VFL_TYPE_GRABBER, > > + vpbe_display_nr[i]); > > + if (err) > > + goto probe_out; > > + /* set the driver data in platform device */ > > + platform_set_drvdata(pdev, disp_dev); > > + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); > > + } > > + > > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > > + if (!res) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Unable to get VENC interrupt resource\n"); > > + err = -ENODEV; > > + goto probe_out; > > + } > > + irq = res->start; > > + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, > > + disp_dev)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); > > + err = -ENODEV; > > + goto probe_out; > > + } > > You probably want to get the resources and register the interrupt handler > before registering the V4L2 devices, otherwise userspace will be able to open > devices before you're done with the initialization. I do not think anyone would attempt to open the device so soon as the boot process is not complete yet. Also, if the irq is registered before, the interrupts start and the driver crashes for lack of initialized structure variables. > > > + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 > > device\n"); + return 0; > > +probe_out: > > + kfree(disp_dev); > > + > > + for (k = 0; k < j; k++) { > > + /* Get the pointer to the layer object */ > > + vpbe_display_layer = disp_dev->dev[k]; > > + /* Unregister video device */ > > + video_unregister_device(vpbe_display_layer->video_dev); > > + /* Release video device */ > > + video_device_release(vpbe_display_layer->video_dev); > > + vpbe_display_layer->video_dev = NULL; > > + } > > + return err; > > +} > > [snip] > > > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); > > Should this be "TI DM644x" instead ? This is a common IP for DM644x, Dm355 and Dm365 for which the patches will follow after this set. So I think it is OK. > > > +MODULE_LICENSE("GPL"); > > +MODULE_AUTHOR("Texas Instruments"); > > > > diff --git a/include/media/davinci/vpbe_display.h > > b/include/media/davinci/vpbe_display.h new file mode 100644 > > index 0000000..d5cce40 > > --- /dev/null > > +++ b/include/media/davinci/vpbe_display.h > > @@ -0,0 +1,146 @@ > > +/* > > + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License as > > + * published by the Free Software Foundation version 2. > > + * > > + * This program is distributed WITHOUT ANY WARRANTY of any > > + * kind, whether express or implied; without even the implied warranty > > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > +#ifndef VPBE_DISPLAY_H > > +#define VPBE_DISPLAY_H > > + > > +#ifdef __KERNEL__ > > This is a kernel header, you don't need to guard it with #ifdef __KERNEL__ Addressed. > > > + > > +/* Header files */ > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#define VPBE_DISPLAY_MAX_DEVICES 2 > > + > > +enum vpbe_display_device_id { > > + VPBE_DISPLAY_DEVICE_0, > > + VPBE_DISPLAY_DEVICE_1 > > +}; > > + > > +#define VPBE_DISPLAY_DRV_NAME "vpbe-display" > > + > > +#define VPBE_DISPLAY_MAJOR_RELEASE 1 > > +#define VPBE_DISPLAY_MINOR_RELEASE 0 > > +#define VPBE_DISPLAY_BUILD 1 > > +#define VPBE_DISPLAY_VERSION_CODE ((VPBE_DISPLAY_MAJOR_RELEASE << 16) | \ > > + (VPBE_DISPLAY_MINOR_RELEASE << 8) | \ > > + VPBE_DISPLAY_BUILD) > > + > > +#define VPBE_DISPLAY_VALID_FIELD(field) ((V4L2_FIELD_NONE == field) || \ > > + (V4L2_FIELD_ANY == field) || (V4L2_FIELD_INTERLACED == field)) > > + > > +/* Exp ratio numerator and denominator constants */ > > +#define VPBE_DISPLAY_H_EXP_RATIO_N (9) > > +#define VPBE_DISPLAY_H_EXP_RATIO_D (8) > > +#define VPBE_DISPLAY_V_EXP_RATIO_N (6) > > +#define VPBE_DISPLAY_V_EXP_RATIO_D (5) > > + > > +/* Zoom multiplication factor */ > > +#define VPBE_DISPLAY_ZOOM_4X (4) > > +#define VPBE_DISPLAY_ZOOM_2X (2) > > No need for () around the constants. Ok. > > > + > > +/* Structures */ > > +struct display_layer_info { > > + int enable; > > + /* Layer ID used by Display Manager */ > > + enum osd_layer id; > > + struct osd_layer_config config; > > + enum osd_zoom_factor h_zoom; > > + enum osd_zoom_factor v_zoom; > > + enum osd_h_exp_ratio h_exp; > > + enum osd_v_exp_ratio v_exp; > > +}; > > + > > +/* vpbe display object structure */ > > +struct vpbe_display_obj { > > + /* number of buffers in fbuffers */ > > + unsigned int numbuffers; > > + /* Pointer pointing to current v4l2_buffer */ > > + struct videobuf_buffer *cur_frm; > > + /* Pointer pointing to next v4l2_buffer */ > > + struct videobuf_buffer *next_frm; > > + /* videobuf specific parameters > > + * Buffer queue used in video-buf > > + */ > > + struct videobuf_queue buffer_queue; > > + /* Queue of filled frames */ > > + struct list_head dma_queue; > > + /* Used in video-buf */ > > + spinlock_t irqlock; > > + /* V4l2 specific parameters */ > > + /* Identifies video device for this layer */ > > + struct video_device *video_dev; > > + /* This field keeps track of type of buffer exchange mechanism user > > + * has selected > > + */ > > + enum v4l2_memory memory; > > + /* Used to keep track of state of the priority */ > > + struct v4l2_prio_state prio; > > + /* Used to store pixel format */ > > + struct v4l2_pix_format pix_fmt; > > + enum v4l2_field buf_field; > > + /* Video layer configuration params */ > > + struct display_layer_info layer_info; > > + /* vpbe specific parameters > > + * enable window for display > > + */ > > + unsigned char window_enable; > > + /* number of open instances of the layer */ > > + unsigned int usrs; > > + /* number of users performing IO */ > > + unsigned int io_usrs; > > + /* Indicates id of the field which is being displayed */ > > + unsigned int field_id; > > + /* Indicates whether streaming started */ > > + unsigned char started; > > + /* Identifies device object */ > > + enum vpbe_display_device_id device_id; > > + /* facilitation of ioctl ops lock by v4l2*/ > > + struct mutex opslock; > > +}; > > + > > +/* vpbe device structure */ > > +struct vpbe_display { > > + /* layer specific parameters */ > > + /* lock for isr updates to buf layers*/ > > + spinlock_t dma_queue_lock; > > + /* C-Plane offset from start of y-plane */ > > + unsigned int cbcr_ofst; > > + struct vpbe_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; > > +}; > > + > > +/* File handle structure */ > > +struct vpbe_fh { > > + /* vpbe device structure */ > > + struct vpbe_display *disp_dev; > > + /* pointer to layer object for opened device */ > > + struct vpbe_display_obj *layer; > > + /* Indicates whether this file handle is doing IO */ > > + unsigned char io_allowed; > > + /* Used to keep track priority of this instance */ > > + enum v4l2_priority prio; > > +}; > > + > > +struct buf_config_params { > > + unsigned char min_numbuffers; > > + unsigned char numbuffers[VPBE_DISPLAY_MAX_DEVICES]; > > + unsigned int min_bufsize[VPBE_DISPLAY_MAX_DEVICES]; > > + unsigned int layer_bufsize[VPBE_DISPLAY_MAX_DEVICES]; > > +}; > > + > > +static int venc_is_second_field(void); > > If the function is static, there's no need to add it to the header. venc_is_second_field fn declaration is removed. > > > +#endif /* end of __KERNEL__ */ > > +#endif /* VPBE_DISPLAY_H */ > > diff --git a/include/media/davinci/vpbe_types.h > > b/include/media/davinci/vpbe_types.h new file mode 100644 > > index 0000000..24a358b > > --- /dev/null > > +++ b/include/media/davinci/vpbe_types.h > > @@ -0,0 +1,91 @@ > > +/* > > + * Copyright (C) 2010 Texas Instruments Inc > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License as published by > > + * the Free Software Foundation version 2. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public License > > + * along with this program; if not, write to the Free Software > > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > > + */ > > +#ifndef _VPBE_TYPES_H > > +#define _VPBE_TYPES_H > > + > > +enum vpbe_types { > > + VPBE_VERSION_1 = 1, > > + VPBE_VERSION_2, > > + VPBE_VERSION_3, > > +}; > > Should it be renamed to enum vpbe_version ? OK. > > > + > > +/* vpbe_timing_type - Timing types used in vpbe device */ > > +enum vpbe_enc_timings_type { > > + VPBE_ENC_STD = 0x1, > > + VPBE_ENC_DV_PRESET = 0x2, > > + VPBE_ENC_CUSTOM_TIMINGS = 0x4, > > + /* Used when set timings through FB device interface */ > > + VPBE_ENC_TIMINGS_INVALID = 0x8, > > +}; > > + > > +union vpbe_timings { > > + v4l2_std_id std_id; > > + unsigned int dv_preset; > > +}; > > + > > +/* > > + * struct vpbe_enc_mode_info > > + * @name: ptr to name string of the standard, "NTSC", "PAL" etc > > + * @std: standard or non-standard mode. 1 - standard, 0 - nonstandard > > + * @interlaced: 1 - interlaced, 0 - non interlaced/progressive > > + * @xres: x or horizontal resolution of the display > > + * @yres: y or vertical resolution of the display > > + * @fps: frame per second > > + * @left_margin: left margin of the display > > + * @right_margin: right margin of the display > > + * @upper_margin: upper margin of the display > > + * @lower_margin: lower margin of the display > > + * @hsync_len: h-sync length > > + * @vsync_len: v-sync length > > + * @flags: bit field: bit usage is documented below > > + * > > + * Description: > > + * Structure holding timing and resolution information of a standard. > > + * Used by vpbe_device to set required non-standard timing in the > > + * venc when lcd controller output is connected to a external encoder. > > + * A table of timings is maintained in vpbe device to set this in > > + * venc when external encoder is connected to lcd controller output. > > + * Encoder may provide a g_dv_timings() API to override these values > > + * as needed. > > + * > > + * Notes > > + * ------ > > + * if_type should be used only by encoder manager and encoder. > > + * flags usage > > + * b0 (LSB) - hsync polarity, 0 - negative, 1 - positive > > + * b1 - vsync polarity, 0 - negative, 1 - positive > > + * b2 - field id polarity, 0 - negative, 1 - positive > > + */ > > +struct vpbe_enc_mode_info { > > + unsigned char *name; > > + enum vpbe_enc_timings_type timings_type; > > + union vpbe_timings timings; > > + unsigned int interlaced; > > + unsigned int xres; > > + unsigned int yres; > > + struct v4l2_fract aspect; > > + struct v4l2_fract fps; > > + unsigned int left_margin; > > + unsigned int right_margin; > > + unsigned int upper_margin; > > + unsigned int lower_margin; > > + unsigned int hsync_len; > > + unsigned int vsync_len; > > + unsigned int flags; > > +}; > > + > > +#endif > > -- > Regards, > > Laurent Pinchart > From khilman at ti.com Wed Apr 20 12:27:15 2011 From: khilman at ti.com (Kevin Hilman) Date: Wed, 20 Apr 2011 10:27:15 -0700 Subject: [linux-pm] [RFC PATCH V3 1/4] cpuidle: Move dev->last_residency update to driver enter routine; remove dev->last_state In-Reply-To: <20110420065523.332.58678.stgit@tringupt.in.ibm.com> (Trinabh Gupta's message of "Wed, 20 Apr 2011 12:25:34 +0530") References: <20110420065445.332.13688.stgit@tringupt.in.ibm.com> <20110420065523.332.58678.stgit@tringupt.in.ibm.com> Message-ID: <87pqog6dfw.fsf@ti.com> Trinabh Gupta writes: > Cpuidle subsystem only suggests the state to enter and does not > guarantee if the suggested state is entered. The actual entered state > may be different because of software or hardware demotion. Software > demotion is done by the back-end cpuidle driver and can be accounted > correctly. Current cpuidle code uses last_state field to capture the > actual state entered and based on that updates the statistics for the > state entered. > > Ideally the driver enter routine should update the counters, > and it should return the state actually entered rather than the time > spent there. OK, the return type was changed to return the state index instead of the time, but since the governors are still relying on dev->last_residency, drivers are required to update it. Because of that, why not keep the update of the time/usage counters in common code rather than duplicating it (9 times in this patch) into all the drivers? Something like the patch below should suffice. Kevin diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 845d3ef..875d241 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -91,6 +91,11 @@ static void cpuidle_idle_call(void) entered_state = target_state->enter(dev, drv, next_state); + /* Update cpuidle counters */ + dev->states_usage[entered_state].time += + (unsigned long long)dev->last_residency; + dev->states_usage[entered_state].usage++; + trace_power_end(dev->cpu); trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu); From khilman at ti.com Wed Apr 20 12:33:25 2011 From: khilman at ti.com (Kevin Hilman) Date: Wed, 20 Apr 2011 10:33:25 -0700 Subject: [linux-pm] [RFC PATCH V3 4/4] cpuidle: Single/Global registration of idle states In-Reply-To: <20110420065608.332.30043.stgit@tringupt.in.ibm.com> (Trinabh Gupta's message of "Wed, 20 Apr 2011 12:26:34 +0530") References: <20110420065445.332.13688.stgit@tringupt.in.ibm.com> <20110420065608.332.30043.stgit@tringupt.in.ibm.com> Message-ID: <87k4eo6d5m.fsf@ti.com> Trinabh Gupta writes: > With this patch there is single copy of cpuidle_states structure > instead of per-cpu. The statistics needed on per-cpu basis > by the governor are kept per-cpu. This simplifies the cpuidle > subsystem as state registration is done by single cpu only. > Having single copy of cpuidle_states saves memory. Rare case > of asymmetric C-states can be handled within the cpuidle driverand > architectures such as POWER do not have asymmetric C-states. I haven't actually tested this series on OMAP yet, but it currently doesn't compile. The patch below (on top of your series) is required to compile on OMAP, I think it's doing what you intended, but please confirm. Kevin diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index 6641574..ab77ba3 100644 --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -512,6 +512,7 @@ static int omap3_cpuidle_driver_init(void) int i, retval, count = 0; struct omap3_processor_cx *cx; struct cpuidle_state *state; + struct cpuidle_driver *drv = &omap3_idle_driver; mpu_pd = pwrdm_lookup("mpu_pwrdm"); core_pd = pwrdm_lookup("core_pwrdm"); @@ -532,7 +533,7 @@ static int omap3_cpuidle_driver_init(void) state->enter = (state->flags & CPUIDLE_FLAG_CHECK_BM) ? omap3_enter_idle_bm : omap3_enter_idle; if (cx->type == OMAP3_STATE_C1) - dev->safe_state_index = count; + drv->safe_state_index = count; sprintf(state->name, "C%d", count+1); strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); count++; From laurent.pinchart at ideasonboard.com Thu Apr 21 05:18:05 2011 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Thu, 21 Apr 2011 12:18:05 +0200 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: References: Message-ID: <201104211218.06340.laurent.pinchart@ideasonboard.com> Hi Manjunath, On Wednesday 20 April 2011 17:30:08 Hadli, Manjunath wrote: > Hi Laurent, > Thank you for you very valuable and detailed comments. I have fixed a lot > of your suggestions and there are a few questions I need more explanation > for. I will send the fixed and updated patches as a follow-up after your > clarifications. OK. Please see below for answsers. > On Thu, Apr 07, 2011 at 17:28:20, Laurent Pinchart wrote: > > On Saturday 02 April 2011 11:40:49 Manjunath Hadli wrote: [snip] > > > +static u32 video2_numbuffers = 3; > > > +static u32 video3_numbuffers = 3; > > > > Why is the number of buffers set by a module parameter ? It should be > > negotiated dynamically with REQBUFS. > > This is the minimum number of buffers to be allocated by the system as > there is no scatter-gather mechanism in Davinci. To make sure of > availability of a minimum numbers of buffers for the system which may not > be available otherwise due to fragmentation, these are used. But you don't reserve the memory when the driver is probed, so how does this help ? [snip] > > > + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; > > > + > > > + /* Convert time represention from jiffies to timeval */ > > > + jiffies_to_timeval(jiffies_time, &timevalue); > > > > Please use ktime_get_ts() or ktime_get_real_ts() to get the timestamp. > > Fixed. Used do_gettimeofday(). Please use ktime_get_ts() instead. It will return a monotonic clock timestamp. Otherwise the buffer timestamp will vary when the system clock is modified (as a result of an NTP time update for instance). > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > + layer = disp_dev->dev[i]; > > > + /* If streaming is started in this layer */ > > > + if (!layer->started) > > > + continue; > > > + /* Check the field format */ > > > + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && > > > + (event & OSD_END_OF_FRAME)) { > > > + /* Progressive mode */ > > > + if (layer_first_int[i]) { > > > + layer_first_int[i] = 0; > > > + continue; > > > + } > > > + /* > > > + * Mark status of the cur_frm to > > > + * done and unlock semaphore on it > > > + */ > > > + > > > + if (layer->cur_frm != layer->next_frm) { > > > + layer->cur_frm->ts = timevalue; > > > + layer->cur_frm->state = VIDEOBUF_DONE; > > > > Please use videobuf2. > > I would like to get to videobuf2 as a next set of changes. I want to get > the Dm6446 driver fisrt, add it with Dm365 and do the videobuf2 later. I > hope it is okay. We're trying to get rid of videobuf1, so accepting new drivers that make use of videobuf1 is a bit problematic. Have you had a look at videobuf2 ? How much time do you think it would take to convert this driver to videobuf2 ? Let's first address all the other issues, and then tackle that one. [snip] > > > +/* interrupt service routine */ > > > +static irqreturn_t venc_isr(int irq, void *arg) > > > +{ > > > + static unsigned last_event; > > > + unsigned event = 0; > > > + > > > + if (venc_is_second_field()) > > > + event |= VENC_SECOND_FIELD; > > > + else > > > + event |= VENC_FIRST_FIELD; > > > + > > > + if (event == (last_event & ~VENC_END_OF_FRAME)) { > > > + /* > > > + * If the display is non-interlaced, then we need to flag > > > the + * end-of-frame event at every interrupt regardless of > > > the + * value of the FIDST bit. We can conclude that the > > > display is + * non-interlaced if the value of the FIDST bit > > > is unchanged + * from the previous interrupt. > > > + */ > > > > What about checking pix_fmt.field instead ? > > Not sure what you mean here. We get the FIRST or second field event from > the hardware so we need to check the register value rather than > pix_fmt.field. Interlacing is configured by userspace. When configured in interlaced mode, I expect the device to alternate fields. When configured in progressive mode, I expect it to always return the same field. If that's correct, the FIDST bit is only needed to identify the active field when configured in interlaced mode. pix_fmt.field can be use to check whether we're in interlaced or progressive mode. [snip] > > > +static int vpbe_display_querycap(struct file *file, void *priv, > > > + struct v4l2_capability *cap) > > > +{ > > > + struct vpbe_fh *fh = file->private_data; > > > + struct vpbe_display_obj *layer = fh->layer; > > > + > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); > > > > Do you really need a debugging call here ? > > I am Ok either ways. When debugging is enabled, it is just one of the data > points since there are multiple windows. You could leave it, but a debug call in the querycap handler doesn't look very useful to me. > > > + *cap = vpbe_display_videocap; > > > + > > > + return 0; > > > +} [snip] > > > +static int vpbe_display_g_fmt(struct file *file, void *priv, > > > + struct v4l2_format *fmt) > > > +{ > > > + struct vpbe_fh *fh = file->private_data; > > > + struct vpbe_display_obj *layer = fh->layer; > > > + > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > + "VIDIOC_G_FMT, layer id = %d\n", > > > + layer->device_id); > > > + > > > + /* If buffer type is video output */ > > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > > + /* Fill in the information about format */ > > > + *pixfmt = layer->pix_fmt; > > > > I don't see anything wrong in doing > > > > fmt->fmt.pix = layer->pix_fmt; > > > > directly. > > Wel,. In the past patches we had a large amount of multiple indirections, > and as part of a suggestion on open source I removed all of them with a > general rule of not having more than 2 indirections. What is your take on > this? How many indirection levels do you allow? There's no hard rule for that. I usually use intermediate pointers when the number of indirection levels grow too high, except when I only need to perform the indirection once or twice, like in this case. > > > + } else { > > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > > + return -EINVAL; > > > + } > > > + > > > + return 0; > > > +} [snip] > > > +static int vpbe_display_s_fmt(struct file *file, void *priv, > > > + struct v4l2_format *fmt) > > > +{ > > > + int ret = 0; > > > + struct vpbe_fh *fh = file->private_data; > > > + struct vpbe_display *disp_dev = video_drvdata(file); > > > + struct vpbe_display_obj *layer = fh->layer; > > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > > > Variables are often declared in longuest to shortest line order in kernel > > drivers. It might not be a requirement though, but I find it to make code > > more readable. > > cannot do as suggested since the structure variable assignments have > dependency on previous structure variables. Oops, my bad. > > > + > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > + "VIDIOC_S_FMT, layer id = %d\n", > > > + layer->device_id); > > > + > > > + /* If streaming is started, return error */ > > > + if (layer->started) { > > > > I'm pretty sure there's a race condition here. > > Not sure about this. The entire driver is under V4L2 lock per layer handle > and it would not allow another call to come here once in. I missed that. Probably because I dislike that lock :-) > Can you elaborate how is it a race condition? > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > > + return -EBUSY; > > > + } > > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); > > > + return -EINVAL; > > > + } else { > > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > > + /* Check for valid pixel format */ > > > + ret = vpbe_try_format(disp_dev, pixfmt, 1); > > > + if (ret) > > > + return ret; > > > + > > > + /* YUV420 is requested, check availability of the > > > + other video window */ > > > + > > > + layer->pix_fmt = *pixfmt; > > > + > > > + /* Get osd layer config */ > > > + osd_device->ops.get_layer_config(osd_device, > > > + layer->layer_info.id, cfg); > > > + /* Store the pixel format in the layer object */ > > > + cfg->xsize = pixfmt->width; > > > + cfg->ysize = pixfmt->height; > > > + cfg->line_length = pixfmt->bytesperline; > > > + cfg->ypos = 0; > > > + cfg->xpos = 0; > > > + cfg->interlaced = vpbe_dev->current_timings.interlaced; > > > + > > > + /* Change of the default pixel format for both video > > > windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { > > > + struct vpbe_display_obj *otherlayer; > > > > If the requested format isn't NV12, cfg->pixfmt won't be modified. If it > > has been set to NV12 by a previous S_FMT call, it won't become YUYV. Is > > that intentional ? > > It is reset to YUYV as part of Open. So it is changed only if it is NV12. As stated below, you shouldn't reset formats in open(). > > > + cfg->pixfmt = PIXFMT_NV12; > > > + otherlayer = _vpbe_display_get_other_win(disp_dev, > > > + layer); > > > + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; > > > + } > > > + > > > + /* Set the layer config in the osd window */ > > > + ret = osd_device->ops.set_layer_config(osd_device, > > > + layer->layer_info.id, cfg); > > > + if (ret < 0) { > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > + "Error in S_FMT params:\n"); > > > + return -EINVAL; > > > + } > > > + > > > + /* Readback and fill the local copy of current pix format > > > */ + osd_device->ops.get_layer_config(osd_device, > > > + layer->layer_info.id, cfg); > > > + > > > + /* verify if readback values are as expected */ > > > + if (layer->pix_fmt.width != cfg->xsize || > > > + layer->pix_fmt.height != cfg->ysize || > > > + layer->pix_fmt.bytesperline != layer->layer_info. > > > + config.line_length || (cfg->interlaced && > > > + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || > > > + (!cfg->interlaced && layer->pix_fmt.field != > > > + V4L2_FIELD_NONE)) { > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > + "mismatch:layer conf params:\n"); > > > + return -EINVAL; > > > + } > > > + } > > > + > > > + return 0; > > > +} [snip] > > > +/** > > > + * vpbe_display_g_std - Get the standard in the current encoder > > > + * > > > + * Get the standard in the current encoder. Return the status. 0 - > > > success + * -EINVAL on error > > > + */ > > > +static int vpbe_display_g_std(struct file *file, void *priv, > > > + v4l2_std_id *std_id) > > > +{ > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); > > > + > > > + /* Get the standard from the current encoder */ > > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > > > + *std_id = vpbe_dev->current_timings.timings.std_id; > > > + return 0; > > > + } > > > + return -EINVAL; > > > > Where do you set timings_type ? When can this return an error ? > > It can return an error if the driver is set to a DV_PRESET mode. > timings_type would tell whether is is an SD standard or a DV_PRESET. But you don't seem to set it anywhere. > > > +} [snip] > > > +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id > > > id, + struct vpbe_display *disp_dev) > > > +{ > > > + int err = 0; > > > + struct osd_layer_config *layer_config; > > > + struct vpbe_display_obj *layer = disp_dev->dev[id]; > > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > > + > > > + /* First claim the layer for this device */ > > > + err = osd_device->ops.request_layer(osd_device, > > > + layer->layer_info.id); > > > + if (err < 0) { > > > + /* Couldn't get layer */ > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > + "Display Manager failed to allocate layer\n"); > > > + return -EBUSY; > > > + } > > > + > > > + layer_config = cfg; > > > + /* Set the default image and crop values */ > > > + layer_config->pixfmt = PIXFMT_YCbCrI; > > > + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; > > > + layer->pix_fmt.bytesperline = layer_config->line_length = > > > + vpbe_dev->current_timings.xres * 2; > > > + > > > + layer->pix_fmt.width = layer_config->xsize = > > > + vpbe_dev->current_timings.xres; > > > + layer->pix_fmt.height = layer_config->ysize = > > > + vpbe_dev->current_timings.yres; > > > + layer->pix_fmt.sizeimage = > > > + layer->pix_fmt.bytesperline * layer->pix_fmt.height; > > > + layer_config->xpos = 0; > > > + layer_config->ypos = 0; > > > + layer_config->interlaced = vpbe_dev->current_timings.interlaced; > > > > You shouldn't reinitialized the format every time the device is opened. > > The previously set format should be kept. > > This strictly followed across drivers? I am Ok if that is the expectation. It's how V4L2 drivers should behave. Some drivers might not follow that rule, but that would be a bug :-) > > > + > > > + /* > > > + * turn off ping-pong buffer and field inversion to fix > > > + * the image shaking problem in 1080I mode > > > + */ > > > + > > > + if (cfg->interlaced) > > > + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; > > > + else > > > + layer->pix_fmt.field = V4L2_FIELD_NONE; > > > + > > > + err = osd_device->ops.set_layer_config(osd_device, > > > + layer->layer_info.id, > > > + layer_config); > > > + if (err < 0) { > > > + /* Couldn't set layer */ > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > + "Display Manager failed to set osd layer\n"); > > > + return -EBUSY; > > > + } > > > + > > > + return 0; > > > +} [snip] > > > +/* > > > + * vpbe_display_probe() > > > + * This function creates device entries by register itself to the V4L2 > > > driver + * and initializes fields of each layer objects > > > + */ > > > +static __devinit int vpbe_display_probe(struct platform_device *pdev) > > > +{ > > > + int i, j = 0, k, err = 0; > > > + struct vpbe_display *disp_dev; > > > + struct video_device *vbd = NULL; > > > + struct vpbe_display_obj *vpbe_display_layer = NULL; > > > + struct resource *res; > > > + int irq; > > > + > > > + printk(KERN_DEBUG "vpbe_display_probe\n"); > > > + > > > + /* Allocate memory for vpbe_display */ > > > + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); > > > + if (!disp_dev) { > > > + printk(KERN_ERR "ran out of memory\n"); > > > + return -ENOMEM; > > > + } > > > + > > > + /* Allocate memory for four plane display objects */ > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > + disp_dev->dev[i] = > > > + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); > > > + /* If memory allocation fails, return error */ > > > + if (!disp_dev->dev[i]) { > > > + printk(KERN_ERR "ran out of memory\n"); > > > + err = -ENOMEM; > > > + goto probe_out; > > > + } > > > + spin_lock_init(&disp_dev->dev[i]->irqlock); > > > + mutex_init(&disp_dev->dev[i]->opslock); > > > + } > > > + spin_lock_init(&disp_dev->dma_queue_lock); > > > + > > > + err = init_vpbe_layer_objects(i); > > > + if (err) { > > > + printk(KERN_ERR "Error initializing vpbe display\n"); > > > + return err; > > > + } > > > + > > > + /* > > > + * Scan all the platform devices to find the vpbe > > > + * controller device and get the vpbe_dev object > > > + */ > > > + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, > > > + vpbe_device_get); > > > + if (err < 0) > > > + return err; > > > + > > > + /* Initialize the vpbe display controller */ > > > + if (NULL != vpbe_dev->ops.initialize) { > > > + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); > > > + if (err) { > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing > > > vpbe\n"); + err = -ENOMEM; > > > + goto probe_out; > > > + } > > > + } > > > + > > > + /* check the name of davinci device */ > > > + if (vpbe_dev->cfg->module_name != NULL) > > > + strcpy(vpbe_display_videocap.card, > > > + vpbe_dev->cfg->module_name); > > > + > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > + /* Get the pointer to the layer object */ > > > + vpbe_display_layer = disp_dev->dev[i]; > > > + /* Allocate memory for video device */ > > > + vbd = video_device_alloc(); > > > + if (vbd == NULL) { > > > + for (j = 0; j < i; j++) { > > > + video_device_release( > > > + disp_dev->dev[j]->video_dev); > > > + } > > > + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of > > > memory\n"); + err = -ENOMEM; > > > + goto probe_out; > > > + } > > > + /* Initialize field of video device */ > > > + vbd->release = video_device_release; > > > + vbd->fops = &vpbe_fops; > > > + vbd->ioctl_ops = &vpbe_ioctl_ops; > > > + vbd->minor = -1; > > > + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; > > > + vbd->lock = &vpbe_display_layer->opslock; > > > + > > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) > > > { + vbd->tvnorms = (V4L2_STD_525_60 | > > > V4L2_STD_625_50); + vbd->current_norm = > > > + vpbe_dev->current_timings.timings.std_id; > > > + } else > > > + vbd->current_norm = 0; > > > + > > > + snprintf(vbd->name, sizeof(vbd->name), > > > + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", > > > + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, > > > + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, > > > + (VPBE_DISPLAY_VERSION_CODE) & 0xff); > > > + > > > + /* Set video_dev to the video device */ > > > + vpbe_display_layer->video_dev = vbd; > > > + vpbe_display_layer->device_id = i; > > > + > > > + vpbe_display_layer->layer_info.id = > > > + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); > > > + if (display_buf_config_params.numbuffers[i] == 0) > > > + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; > > > + else > > > + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; > > > + > > > + /* Initialize field of the display layer objects */ > > > + vpbe_display_layer->usrs = 0; > > > + vpbe_display_layer->io_usrs = 0; > > > + vpbe_display_layer->started = 0; > > > + > > > + /* Initialize prio member of layer object */ > > > + v4l2_prio_init(&vpbe_display_layer->prio); > > > + > > > + /* Register video device */ > > > + v4l2_info(&vpbe_dev->v4l2_dev, > > > + "Trying to register VPBE display device.\n"); > > > + v4l2_info(&vpbe_dev->v4l2_dev, > > > + "layer=%x,layer->video_dev=%x\n", > > > + (int)vpbe_display_layer, > > > + (int)&vpbe_display_layer->video_dev); > > > + > > > + err = video_register_device(vpbe_display_layer-> > > > + video_dev, > > > + VFL_TYPE_GRABBER, > > > + vpbe_display_nr[i]); > > > + if (err) > > > + goto probe_out; > > > + /* set the driver data in platform device */ > > > + platform_set_drvdata(pdev, disp_dev); > > > + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); > > > + } > > > + > > > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > > > + if (!res) { > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > + "Unable to get VENC interrupt resource\n"); > > > + err = -ENODEV; > > > + goto probe_out; > > > + } > > > + irq = res->start; > > > + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, > > > + disp_dev)) { > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request > > > interrupt\n"); + err = -ENODEV; > > > + goto probe_out; > > > + } > > > > You probably want to get the resources and register the interrupt handler > > before registering the V4L2 devices, otherwise userspace will be able to > > open devices before you're done with the initialization. > > I do not think anyone would attempt to open the device so soon as the boot > process is not complete yet. Unless you compile the driver as a module. In that case an application could open the device as soon as it gets registered. hal is known to do that. > Also, if the irq is registered before, the interrupts start and the driver > crashes for lack of initialized structure variables. Why would an interrupt occur before the device gets opened ? > > > + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 > > > device\n"); + return 0; > > > +probe_out: > > > + kfree(disp_dev); > > > + > > > + for (k = 0; k < j; k++) { > > > + /* Get the pointer to the layer object */ > > > + vpbe_display_layer = disp_dev->dev[k]; > > > + /* Unregister video device */ > > > + video_unregister_device(vpbe_display_layer->video_dev); > > > + /* Release video device */ > > > + video_device_release(vpbe_display_layer->video_dev); > > > + vpbe_display_layer->video_dev = NULL; > > > + } > > > + return err; > > > +} > > > > [snip] > > > > > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); > > > > Should this be "TI DM644x" instead ? > > This is a common IP for DM644x, Dm355 and Dm365 for which the patches will > follow after this set. So I think it is OK. What about "TI DM644x/DM355/DM365" then ? DMXXX makes it look like it supports all DaVinci chips. -- Regards, Laurent Pinchart From bengardiner at nanometrics.ca Thu Apr 21 12:30:32 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 21 Apr 2011 13:30:32 -0400 Subject: [PATCH] [RFC] davinci_emac: don't WARN_ON cpdma_chan_submit -ENOMEM Message-ID: <1303407032-7526-1-git-send-email-bengardiner@nanometrics.ca> The current implementation of emac_rx_handler, when the host is flooded, will result in a great deal of WARNs on the console; due to the return value of cpdma_chan_submit. This function can error with EINVAL and ENOMEM; the former when the channel is in an invalid state, in which case the caller is in error. The latter when a cpdma descriptor cannot be allocated. When flooded, cpdma_chan_submit will return -ENOMEM; treat the inability to allocate a cpdma descriptor as an rx error similar in behaviour to when emac_rx_alloc returns NULL. No Signed-off-by yet; not complete fix (see below). CC: Sriramakrishnan A G --- I'm new to network drivers -- and kernel development, really. I'd be happy to receive feedback on this approach of resolving the -ENOMEM when flooded. Is there a more conventional approach? Shoud these frames be recorded as 'dropped'? Testing was performed on da850evm both with and without "net: davinci_emac: fix spinlock bug with dma channel cleanup" from Sriramakrishnan A G applied. The behaviour was the same: the emac is not able to receive any frames after being flooded -- but it can still send. I would appreciate any insight into the potential causes of the lockup. Best Regards, Ben Gardiner Nanometrics Inc. http://www.nanometrics.ca --- drivers/net/davinci_emac.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 7018bfe..17c48d6 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -1037,8 +1037,12 @@ static void emac_rx_handler(void *token, int len, int status) recycle: ret = cpdma_chan_submit(priv->rxchan, skb, skb->data, skb_tailroom(skb), GFP_KERNEL); - if (WARN_ON(ret < 0)) + WARN_ON(ret == -EINVAL); + if (ret < 0) { + if (netif_msg_rx_err(priv) && net_ratelimit()) + dev_err(emac_dev, "failed cpdma submit\n"); dev_kfree_skb_any(skb); + } } static void emac_tx_handler(void *token, int len, int status) -- 1.7.1 From bengardiner at nanometrics.ca Thu Apr 21 13:19:01 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 21 Apr 2011 14:19:01 -0400 Subject: [PATCH 1/4] davinci-mcasp: correct tdm_slots limit In-Reply-To: References: Message-ID: The current check for the number of tdm-slots specified by platform data is always true (x >= 2 || x <= 32); therefore the else branch that warns of an incorrect number of slots can never be taken. Check that the number of tdm slots specified by platform data is between 2 and 32, inclusive. Signed-off-by: Ben Gardiner Reviewed-by: James Nuss --- sound/soc/davinci/davinci-mcasp.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index fb55d2c..e595756 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -644,7 +644,7 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream) mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask); mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD); - if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) + if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXMOD(dev->tdm_slots), FSXMOD(0x1FF)); else @@ -660,7 +660,7 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream) AHCLKRE); mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask); - if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) + if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRMOD(dev->tdm_slots), FSRMOD(0x1FF)); else -- 1.7.1 From bengardiner at nanometrics.ca Thu Apr 21 13:19:00 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 21 Apr 2011 14:19:00 -0400 Subject: [PATCH 0/4] davinci-mcasp: fix tdm_slots and CBM/CFS Message-ID: This patch series is comprised of three bugfixes and one cleanup that were performed during prototyping of McASP operation in codec clock- master frame-slave mode. First we noticed that the check of the number of tdm slots requested by platform data was always returning true -- unrelated to CMB/CFS mode. Then a cleanup: the PDIR values set are currently based on magic numbers and there are available bitfield definitions. Not strictly needed but it makes the changes introduced in the last patch simpler to read. It was found that the hardware parameters assigned when codec clock-master frame-slave is requested were incorrect. This change is required for correct CBM/CFS operation. Finally, the direction of the pins is corrected to reflect the implications of codec clock-master frame-slave -- i.e. mcasp clock- input frame-output. This change is also required for correct operation. The combination was tested with a logic analyzer and the hrtimer pwm device from Bill Gatliff's PWM framework [1] on a da850evm with hardware modifications to access the McASP lines. [1] http://article.gmane.org/gmane.linux.kernel.embedded/3486/ Ben Gardiner (4): davinci-mcasp: correct tdm_slots limit davinci-mcasp: use bitfield definitions for PDIR davinci-mcasp: fix _CBM_CFS hw_params davinci-mcasp: fix _CBM_CFS pin directions sound/soc/davinci/davinci-mcasp.c | 19 ++++++++++++------- 1 files changed, 12 insertions(+), 7 deletions(-) From bengardiner at nanometrics.ca Thu Apr 21 13:19:04 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 21 Apr 2011 14:19:04 -0400 Subject: [PATCH 4/4] davinci-mcasp: fix _CBM_CFS pin directions In-Reply-To: References: Message-ID: The current davinci_mcasp_set_dai_fmt() sets bits ACLKX and ACLKR in the PDIR register for the codec clock-master/frame-slave mode; however, this results in the ACLKX and ACLKR pins being outputs according to SPRUFM1 [1] which conflicts with "codec is clock master." Similarly to the previous patch in this series, "fix _CBM_CFS hw_params" -- For codec clock-master/frame-slave mode (_CMB_CFS), clear bits ACLKX and ACLKR in the PDIR register to set the pins as inputs and hence allow externally sourced bit-clocks. [1] http://www.ti.com/litv/pdf/sprufm1 Signed-off-by: Ben Gardiner Reviewed-by: James Nuss --- sound/soc/davinci/davinci-mcasp.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 8004643..0a3d891 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -445,8 +445,10 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, + ACLKX | ACLKR); mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, - ACLKX | AFSX | ACLKR | AFSR); + AFSX | AFSR); break; case SND_SOC_DAIFMT_CBM_CFM: /* codec is clock and frame master */ -- 1.7.1 From bengardiner at nanometrics.ca Thu Apr 21 13:19:03 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 21 Apr 2011 14:19:03 -0400 Subject: [PATCH 3/4] davinci-mcasp: fix _CBM_CFS hw_params In-Reply-To: References: Message-ID: <831ad13442c7bca120d24d8679da614b20a0090d.1303408522.git.bengardiner@nanometrics.ca> The current davinci_mcasp_set_dai_fmt() sets bits ACLKXE and ACLKRE (CLKXM and CLKRM as they are reffered to in SPRUFM1 [1]) for codec clock-slave/ frame-slave mode (_CBS_CFS) which selects internally generated bit-clock and frame-sync signals; however, it does the same thing again for codec clock-master/frame-slave mode (_CBM_CFS) in the very next case statement which is incorrectly selecting internally generated bit-clocks in this mode. For codec clock-master/frame-slave mode (_CBM_CFS), clear bits ACLKXE and ACLKRE to select externally-generated bit-clocks. [1] http://www.ti.com/litv/pdf/sprufm1 Signed-off-by: Ben Gardiner Reviewed-by: James Nuss --- sound/soc/davinci/davinci-mcasp.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 1aa24a1..8004643 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -439,10 +439,10 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, break; case SND_SOC_DAIFMT_CBM_CFS: /* codec is clock master and frame slave */ - mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, -- 1.7.1 From bengardiner at nanometrics.ca Thu Apr 21 13:19:02 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 21 Apr 2011 14:19:02 -0400 Subject: [PATCH 2/4] davinci-mcasp: use bitfield definitions for PDIR In-Reply-To: References: Message-ID: <2541e7e4e115b2d54a1f785fc0f603b6a6e9a526.1303408522.git.bengardiner@nanometrics.ca> The current driver creates value for set/clr of PDIR using (x<<26) instead of the #defines that are convieniently made available. Update the driver to use the bitfield definitions of PDIR. There is no functional change introduced by this patch. Signed-off-by: Ben Gardiner Reviewed-by: James Nuss --- sound/soc/davinci/davinci-mcasp.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index e595756..1aa24a1 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -434,7 +434,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26)); + mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, + ACLKX | AHCLKX | AFSX); break; case SND_SOC_DAIFMT_CBM_CFS: /* codec is clock master and frame slave */ @@ -444,7 +445,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26)); + mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, + ACLKX | AFSX | ACLKR | AFSR); break; case SND_SOC_DAIFMT_CBM_CFM: /* codec is clock and frame master */ @@ -454,7 +456,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26)); + mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, + ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR); break; default: -- 1.7.1 From subhasish at mistralsolutions.com Fri Apr 22 07:08:18 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:18 +0530 Subject: [PATCH v4 00/11] pruss mfd drivers. Message-ID: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> PRUSS Functional Block Diagram /-------------------------------------------------------\ | | | |------| | 32GPO<------->PRU CORE-0 <----->| |<----> DRAM 0 | 30GPI<------->(4KB IRAM) | S | (512 Bytes) | | | | | 32GPO<------->PRU CORE-1 <----->| C |<----> DRAM 1 | 30GPI<------->(4KB IRAM) | | (512 Bytes) | | | R | | | | | | Ints to ARM/ | Interrupt <---->| |-------------------------> Master I/F (to SCR2) DSP INTC <------------->Controller | |<------------------------- Slave I/F (from SCR2) Events from | (INTC) |------| | Periph + PRUs | | \-------------------------------------------------------/ Programmable Realtime Unit (PRU) is basically a 32-bit RISC processor available within TI's DA8XX SOCs. It consists of local instruction and data RAM and also has access to SOC resources via a Switched Central Resource (SCR). There are two PRU's available within DA8XX SOC's PRUSS, hence providing two execution cores. Devices/Protocols can be emulated on these utilizing either both or only one of the PRUs independently. The rational behind the MFD driver being the fact that multiple devices can be implemented on the cores independently. It's also possible, as in our case, to implement a single device on both the PRU's resulting in improved load sharing. A detailed description is also available here: http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem version 4: ========= MFD: * added mfd_cells in the board file * exported seperate multiwrite/read functions * removed clk_get_pruss api * improved coding style * removed code duplications * renamed files to pruss based * used make C=1 * added __iomem cookie for io addresses * used iowrite/read instead of __raw variants SUART: * changes for the new mfd_cell implementation * code cleanup * used iowrite/read instead of __raw variants * used make C=1 NOTE: * as requested by the owner the CAN driver patch is submitted seperately. version 3: ========= * added locking to mfd driver. * removed typedefs. * resource allocation through mfd. * renamed da8xx_register_pruss to da8xx_register_pruss_mfd. * removed da8xx_pruss directory for suart. * combined all suart headers. * modified register structure for pruss. * added function clk_add_alias_mcasp. * suart api layer cleanup. * removed semaphore usage. * updated to latest internal code base. * removed __suart_err/dbg. version 2: ========== * added pruss TTY Soft-UART driver. * added pruss Soft-UART board and platform changes. * fixed previous review comments. * reordered patch sequence. version 1: ========== * added pruss mfd driver. Subhasish Ghosh (11): mfd: add pruss mfd driver. da850: add pruss clock. da850: pruss platform specific additions. da850: pruss board specific additions. mfd: pruss SUART private data. da850: pruss SUART board specific additions. da850: pruss SUART platform specific additions. tty: add pruss SUART driver mfd: pruss CAN private data. da850: pruss CAN platform specific additions. da850: pruss CAN board specific additions. arch/arm/mach-davinci/board-da850-evm.c | 104 ++ arch/arm/mach-davinci/da850.c | 12 + arch/arm/mach-davinci/devices-da8xx.c | 81 ++ arch/arm/mach-davinci/include/mach/da8xx.h | 4 + arch/arm/mach-davinci/include/mach/mux.h | 5 + drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/pruss.c | 513 +++++++++ drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 6 + drivers/tty/serial/pruss_suart.c | 1061 +++++++++++++++++ drivers/tty/serial/pruss_suart.h | 1038 +++++++++++++++++ drivers/tty/serial/pruss_suart_api.c | 1710 ++++++++++++++++++++++++++++ drivers/tty/serial/pruss_suart_utils.c | 393 +++++++ include/linux/mfd/pruss.h | 140 +++ include/linux/mfd/pruss_core.h | 128 +++ include/linux/serial_core.h | 2 + 17 files changed, 5226 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/pruss.c create mode 100644 drivers/tty/serial/pruss_suart.c create mode 100644 drivers/tty/serial/pruss_suart.h create mode 100644 drivers/tty/serial/pruss_suart_api.c create mode 100644 drivers/tty/serial/pruss_suart_utils.c create mode 100644 include/linux/mfd/pruss.h create mode 100644 include/linux/mfd/pruss_core.h -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:19 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:19 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss MFD driver and associated include files. For details regarding the PRUSS please refer the folowing link: http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem The rational behind the MFD driver being the fact that multiple devices can be implemented on the cores independently. This is determined by the nature of the program which is loaded into the PRU's instruction memory. A device may be de-initialized and another loaded or two different devices can be run simultaneously on the two cores. It's also possible, as in our case, to implement a single device on both the PRU's resulting in improved load sharing. Signed-off-by: Subhasish Ghosh --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/pruss.c | 513 ++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/pruss.h | 130 ++++++++++ include/linux/mfd/pruss_core.h | 128 ++++++++++ 5 files changed, 782 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/pruss.c create mode 100644 include/linux/mfd/pruss.h create mode 100644 include/linux/mfd/pruss_core.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0284c53..41479e4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -92,6 +92,16 @@ config MFD_TI_SSP To compile this driver as a module, choose M here: the module will be called ti-ssp. +config MFD_DA8XX_PRUSS + tristate "Texas Instruments DA8XX PRUSS support" + depends on ARCH_DAVINCI_DA850 + select MFD_CORE + help + This driver provides support API for the programmable + realtime unit (PRU) present on TI's da8xx processors. It + provides basic read, write, config, enable, disable + routines to facilitate devices emulated on it. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c56b6c7..8015dea 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o +obj-$(CONFIG_MFD_DA8XX_PRUSS) += pruss.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o diff --git a/drivers/mfd/pruss.c b/drivers/mfd/pruss.c new file mode 100644 index 0000000..6836d5a --- /dev/null +++ b/drivers/mfd/pruss.c @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pruss_priv { + struct device *dev; + spinlock_t lock; + struct resource *res; + struct clk *clk; + void __iomem *ioaddr; +}; + +s32 pruss_disable(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + spin_lock(&pruss->lock); + + /* pruss deinit */ + iowrite32(0xFFFFFFFF, &pruss_mmap->intc.statclrint[pruss_num]); + + /* Disable PRU */ + h_pruss = &pruss_mmap->core[pruss_num]; + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_COUNTENABLE_MASK) | + ((PRUCORE_CONTROL_COUNTENABLE_DISABLE << + PRUCORE_CONTROL_COUNTENABLE_SHIFT) & + PRUCORE_CONTROL_COUNTENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_ENABLE_MASK) | + ((PRUCORE_CONTROL_ENABLE_DISABLE << + PRUCORE_CONTROL_ENABLE_SHIFT) & + PRUCORE_CONTROL_ENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + /* Reset PRU */ + iowrite32(PRUCORE_CONTROL_RESETVAL, + &h_pruss->control); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_disable); + +s32 pruss_enable(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 i; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + /* Reset PRU */ + spin_lock(&pruss->lock); + iowrite32(PRUCORE_CONTROL_RESETVAL, &h_pruss->control); + spin_unlock(&pruss->lock); + + /* Reset any garbage in the ram */ + if (pruss_num == PRUCORE_0) + for (i = 0; i < PRUSS_PRU0_RAM_SZ; i++) + iowrite32(0x0, &pruss_mmap->dram0[i]); + else if (pruss_num == PRUCORE_1) + for (i = 0; i < PRUSS_PRU1_RAM_SZ; i++) + iowrite32(0x0, &pruss_mmap->dram1[i]); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_enable); + +/* Load the specified PRU with code */ +s32 pruss_load(struct device *dev, u8 pruss_num, + u32 *pruss_code, u32 code_size_in_words) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 __iomem *pruss_iram; + u32 i; + + if (pruss_num == PRUCORE_0) + pruss_iram = (u32 __iomem *)&pruss_mmap->iram0; + else if (pruss_num == PRUCORE_1) + pruss_iram = (u32 __iomem *)&pruss_mmap->iram1; + else + return -EINVAL; + + pruss_enable(dev, pruss_num); + + spin_lock(&pruss->lock); + /* Copy dMAX code to its instruction RAM */ + for (i = 0; i < code_size_in_words; i++) + iowrite32(pruss_code[i], (pruss_iram + i)); + + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_load); + +s32 pruss_run(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + /* Enable dMAX, let it execute the code we just copied */ + spin_lock(&pruss->lock); + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_COUNTENABLE_MASK) | + ((PRUCORE_CONTROL_COUNTENABLE_ENABLE << + PRUCORE_CONTROL_COUNTENABLE_SHIFT) & + PRUCORE_CONTROL_COUNTENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_ENABLE_MASK) | + ((PRUCORE_CONTROL_ENABLE_ENABLE << + PRUCORE_CONTROL_ENABLE_SHIFT) & + PRUCORE_CONTROL_ENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_run); + +s32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + u32 cnt = timeout; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + while (cnt--) { + temp_reg = ioread32(&h_pruss->control); + if (((temp_reg & PRUCORE_CONTROL_RUNSTATE_MASK) >> + PRUCORE_CONTROL_RUNSTATE_SHIFT) == + PRUCORE_CONTROL_RUNSTATE_HALT) + break; + } + if (!cnt) + return -EBUSY; + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_wait_for_halt); + +s32 pruss_writeb(struct device *dev, u32 offset, u8 pdatatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite8(pdatatowrite, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writeb); + +s32 pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddress; + u32 preg_data; + + paddress = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread8(paddress); + preg_data &= ~mask; + preg_data |= val; + iowrite8(preg_data, paddress); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_rmwb); + +s32 pruss_readb(struct device *dev, u32 offset, u8 *pdatatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstoread; + + paddresstoread = pruss->ioaddr + offset ; + *pdatatoread = ioread8(paddresstoread); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readb); + +s32 pruss_readb_multi(struct device *dev, u32 offset, + u8 *pdatatoread, u16 bytestoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u8 __iomem *paddresstoread; + u16 i; + + paddresstoread = pruss->ioaddr + offset; + + for (i = 0; i < bytestoread; i++) + *pdatatoread++ = ioread8(paddresstoread++); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readb_multi); + +s32 pruss_writel(struct device *dev, u32 offset, + u32 pdatatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite32(pdatatowrite, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writel); + +s32 pruss_writel_multi(struct device *dev, u32 offset, + u32 *pdatatowrite, u16 wordstowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *paddresstowrite; + u16 i; + + paddresstowrite = pruss->ioaddr + offset; + + for (i = 0; i < wordstowrite; i++) + iowrite32(*pdatatowrite++, paddresstowrite++); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writel_multi); + +s32 pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddress; + u32 preg_data; + + paddress = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread32(paddress); + preg_data &= ~mask; + preg_data |= val; + iowrite32(preg_data, paddress); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_rmwl); + +s32 pruss_readl(struct device *dev, u32 offset, u32 *pdatatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstoread; + + paddresstoread = pruss->ioaddr + offset; + *pdatatoread = ioread32(paddresstoread); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readl); + +s32 pruss_readl_multi(struct device *dev, u32 offset, + u32 *pdatatoread, u16 wordstoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *paddresstoread; + u16 i; + + paddresstoread = pruss->ioaddr + offset; + for (i = 0; i < wordstoread; i++) + *pdatatoread++ = ioread32(paddresstoread++); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readl_multi); + +s32 pruss_writew(struct device *dev, u32 offset, u16 pdatatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite16(pdatatowrite, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writew); + +s32 pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddress; + u32 preg_data; + + paddress = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread16(paddress); + preg_data &= ~mask; + preg_data |= val; + iowrite16(preg_data, paddress); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_rmww); + +s32 pruss_readw(struct device *dev, u32 offset, u16 *pdatatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstoread; + + paddresstoread = pruss->ioaddr + offset; + *pdatatoread = ioread16(paddresstoread); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readw); + +s32 pruss_idx_writel(struct device *dev, u32 offset, u32 value) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite32(value, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_idx_writel); + +static int pruss_mfd_add_devices(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mfd_cell *cell = pdev->dev.platform_data; + s32 err, i, num_devices = 0; + + for (i = 0; cell[i].name; i++) { + err = mfd_add_devices(dev, 0, &cell[i], 1, NULL, 0); + if (err) { + dev_err(dev, "cannot add mfd cell: %s\n", + cell[i].name); + continue; + } + num_devices++; + dev_info(dev, "mfd: added %s device\n", cell[i].name); + } + + return num_devices; +} + +static int __devinit pruss_probe(struct platform_device *pdev) +{ + struct pruss_priv *pruss_dev = NULL; + s32 err; + + pruss_dev = kzalloc(sizeof(struct pruss_priv), GFP_KERNEL); + if (!pruss_dev) + return -ENOMEM; + + pruss_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pruss_dev->res) { + dev_err(&pdev->dev, + "unable to get pruss memory resources!\n"); + err = -ENODEV; + goto probe_exit_kfree; + } + + if (!request_mem_region(pruss_dev->res->start, + resource_size(pruss_dev->res), dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "pruss memory region already claimed!\n"); + err = -EBUSY; + goto probe_exit_kfree; + } + + pruss_dev->ioaddr = ioremap(pruss_dev->res->start, + resource_size(pruss_dev->res)); + if (!pruss_dev->ioaddr) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -ENOMEM; + goto probe_exit_free_region; + } + + pruss_dev->clk = clk_get(NULL, "pruss"); + if (IS_ERR(pruss_dev->clk)) { + dev_err(&pdev->dev, "no clock available: pruss\n"); + err = -ENODEV; + pruss_dev->clk = NULL; + goto probe_exit_iounmap; + } + spin_lock_init(&pruss_dev->lock); + + clk_enable(pruss_dev->clk); + + err = pruss_mfd_add_devices(pdev); + if (!err) + goto probe_exit_clock; + + platform_set_drvdata(pdev, pruss_dev); + pruss_dev->dev = &pdev->dev; + return 0; + +probe_exit_clock: + clk_put(pruss_dev->clk); + clk_disable(pruss_dev->clk); +probe_exit_iounmap: + iounmap(pruss_dev->ioaddr); +probe_exit_free_region: + release_mem_region(pruss_dev->res->start, + resource_size(pruss_dev->res)); +probe_exit_kfree: + kfree(pruss_dev); + return err; +} + +static int __devexit pruss_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pruss_priv *pruss = dev_get_drvdata(dev); + + mfd_remove_devices(dev); + pruss_disable(dev, PRUCORE_0); + pruss_disable(dev, PRUCORE_1); + clk_disable(pruss->clk); + clk_put(pruss->clk); + iounmap(pruss->ioaddr); + release_mem_region(pruss->res->start, resource_size(pruss->res)); + kfree(pruss); + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct platform_driver pruss_driver = { + .probe = pruss_probe, + .remove = __devexit_p(pruss_remove), + .driver = { + .name = "pruss_mfd", + .owner = THIS_MODULE, + } +}; + +static int __init pruss_init(void) +{ + return platform_driver_register(&pruss_driver); +} +module_init(pruss_init); + +static void __exit pruss_exit(void) +{ + platform_driver_unregister(&pruss_driver); +} +module_exit(pruss_exit); + +MODULE_DESCRIPTION("Programmable Realtime Unit (PRU) Driver"); +MODULE_AUTHOR("Subhasish Ghosh"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/pruss.h b/include/linux/mfd/pruss.h new file mode 100644 index 0000000..8ef25b3 --- /dev/null +++ b/include/linux/mfd/pruss.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _PRUSS_H_ +#define _PRUSS_H_ + +#include +#include +#include "pruss_core.h" + +#define PRUSS_NUM0 PRUCORE_0 +#define PRUSS_NUM1 PRUCORE_1 + +#define PRUSS_PRU0_RAM_SZ 512 +#define PRUSS_PRU1_RAM_SZ 512 +#define PRUSS_PRU0_BASE_ADDRESS 0 +#define PRUSS_PRU1_BASE_ADDRESS 0x2000 +#define PRUSS_INTC_BASE_ADDRESS (PRUSS_PRU0_BASE_ADDRESS + 0x4000) +#define PRUSS_INTC_GLBLEN (PRUSS_INTC_BASE_ADDRESS + 0x10) +#define PRUSS_INTC_GLBLNSTLVL (PRUSS_INTC_BASE_ADDRESS + 0x1C) +#define PRUSS_INTC_STATIDXSET (PRUSS_INTC_BASE_ADDRESS + 0x20) +#define PRUSS_INTC_STATIDXCLR (PRUSS_INTC_BASE_ADDRESS + 0x24) +#define PRUSS_INTC_ENIDXSET (PRUSS_INTC_BASE_ADDRESS + 0x28) +#define PRUSS_INTC_ENIDXCLR (PRUSS_INTC_BASE_ADDRESS + 0x2C) +#define PRUSS_INTC_HSTINTENIDXSET (PRUSS_INTC_BASE_ADDRESS + 0x34) +#define PRUSS_INTC_HSTINTENIDXCLR (PRUSS_INTC_BASE_ADDRESS + 0x38) +#define PRUSS_INTC_GLBLPRIIDX (PRUSS_INTC_BASE_ADDRESS + 0x80) +#define PRUSS_INTC_STATSETINT0 (PRUSS_INTC_BASE_ADDRESS + 0x200) +#define PRUSS_INTC_STATSETINT1 (PRUSS_INTC_BASE_ADDRESS + 0x204) +#define PRUSS_INTC_STATCLRINT0 (PRUSS_INTC_BASE_ADDRESS + 0x280) +#define PRUSS_INTC_STATCLRINT1 (PRUSS_INTC_BASE_ADDRESS + 0x284) +#define PRUSS_INTC_ENABLESET0 (PRUSS_INTC_BASE_ADDRESS + 0x300) +#define PRUSS_INTC_ENABLESET1 (PRUSS_INTC_BASE_ADDRESS + 0x304) +#define PRUSS_INTC_ENABLECLR0 (PRUSS_INTC_BASE_ADDRESS + 0x380) +#define PRUSS_INTC_ENABLECLR1 (PRUSS_INTC_BASE_ADDRESS + 0x384) +#define PRUSS_INTC_CHANMAP0 (PRUSS_INTC_BASE_ADDRESS + 0x400) +#define PRUSS_INTC_CHANMAP1 (PRUSS_INTC_BASE_ADDRESS + 0x404) +#define PRUSS_INTC_CHANMAP2 (PRUSS_INTC_BASE_ADDRESS + 0x408) +#define PRUSS_INTC_CHANMAP3 (PRUSS_INTC_BASE_ADDRESS + 0x40C) +#define PRUSS_INTC_CHANMAP4 (PRUSS_INTC_BASE_ADDRESS + 0x410) +#define PRUSS_INTC_CHANMAP5 (PRUSS_INTC_BASE_ADDRESS + 0x414) +#define PRUSS_INTC_CHANMAP6 (PRUSS_INTC_BASE_ADDRESS + 0x418) +#define PRUSS_INTC_CHANMAP7 (PRUSS_INTC_BASE_ADDRESS + 0x41C) +#define PRUSS_INTC_CHANMAP8 (PRUSS_INTC_BASE_ADDRESS + 0x420) +#define PRUSS_INTC_CHANMAP9 (PRUSS_INTC_BASE_ADDRESS + 0x424) +#define PRUSS_INTC_CHANMAP10 (PRUSS_INTC_BASE_ADDRESS + 0x428) +#define PRUSS_INTC_CHANMAP11 (PRUSS_INTC_BASE_ADDRESS + 0x42C) +#define PRUSS_INTC_CHANMAP12 (PRUSS_INTC_BASE_ADDRESS + 0x430) +#define PRUSS_INTC_CHANMAP13 (PRUSS_INTC_BASE_ADDRESS + 0x434) +#define PRUSS_INTC_CHANMAP14 (PRUSS_INTC_BASE_ADDRESS + 0x438) +#define PRUSS_INTC_CHANMAP15 (PRUSS_INTC_BASE_ADDRESS + 0x43C) +#define PRUSS_INTC_HOSTMAP0 (PRUSS_INTC_BASE_ADDRESS + 0x800) +#define PRUSS_INTC_HOSTMAP1 (PRUSS_INTC_BASE_ADDRESS + 0x804) +#define PRUSS_INTC_HOSTMAP2 (PRUSS_INTC_BASE_ADDRESS + 0x808) +#define PRUSS_INTC_POLARITY0 (PRUSS_INTC_BASE_ADDRESS + 0xD00) +#define PRUSS_INTC_POLARITY1 (PRUSS_INTC_BASE_ADDRESS + 0xD04) +#define PRUSS_INTC_TYPE0 (PRUSS_INTC_BASE_ADDRESS + 0xD80) +#define PRUSS_INTC_TYPE1 (PRUSS_INTC_BASE_ADDRESS + 0xD84) +#define PRUSS_INTC_HOSTINTEN (PRUSS_INTC_BASE_ADDRESS + 0x1500) +#define PRUSS_INTC_HOSTINTLVL_MAX 9 + +#define PRU_INTC_HOSTMAP0_CHAN (0x03020100) +#define PRU_INTC_HOSTMAP1_CHAN (0x07060504) +#define PRU_INTC_HOSTMAP2_CHAN (0x00000908) + +#define PRU_INTC_CHANMAP7_SYS_EVT31 (0x00000000) +#define PRU_INTC_CHANMAP8_FULL (0x02020100) +#define PRU_INTC_CHANMAP9_FULL (0x04040303) +#define PRU_INTC_CHANMAP10_FULL (0x06060505) +#define PRU_INTC_CHANMAP11_FULL (0x08080707) +#define PRU_INTC_CHANMAP12_FULL (0x00010909) +#define PRU_INTC_CHANMAP8_HALF (0x03020100) +#define PRU_INTC_CHANMAP9_HALF (0x07060504) +#define PRU_INTC_CHANMAP10_HALF (0x03020908) +#define PRU_INTC_CHANMAP11_HALF (0x07060504) +#define PRU_INTC_CHANMAP12_HALF (0x00010908) +#define PRU_INTC_REGMAP_MASK (0xFFFFFFFF) + +s32 pruss_enable(struct device *dev, u8 pruss_num); + +s32 pruss_load(struct device *dev, u8 pruss_num, + u32 *pruss_code, u32 code_size_in_words); + +s32 pruss_run(struct device *dev, u8 pruss_num); + +s32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout); + +s32 pruss_disable(struct device *dev, u8 pruss_num); + +s32 pruss_writeb(struct device *dev, u32 offset, u8 pdatatowrite); + +s32 pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val); + +s32 pruss_readb(struct device *dev, u32 offset, u8 *pdatatoread); + +s32 pruss_readb_multi(struct device *dev, u32 offset, + u8 *pdatatoread, u16 bytestoread); + +s32 pruss_readl(struct device *dev, u32 offset, u32 *pdatatoread); + +s32 pruss_readl_multi(struct device *dev, u32 offset, + u32 *pdatatoread, u16 wordstoread); + +s32 pruss_writel(struct device *dev, u32 offset, u32 pdatatowrite); + +s32 pruss_writel_multi(struct device *dev, u32 offset, + u32 *pdatatowrite, u16 wordstowrite); + +s32 pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val); + +s32 pruss_idx_writel(struct device *dev, u32 offset, u32 value); + +s32 pruss_writew(struct device *dev, u32 offset, u16 datatowrite); + +s32 pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val); + +s32 pruss_readw(struct device *dev, u32 offset, u16 *pdatatoread); + +#endif /* End _PRUSS_H_ */ diff --git a/include/linux/mfd/pruss_core.h b/include/linux/mfd/pruss_core.h new file mode 100644 index 0000000..48e2b99 --- /dev/null +++ b/include/linux/mfd/pruss_core.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _PRUSS_CORE_H_ +#define _PRUSS_CORE_H_ + +#include + +#define PRUCORE_0 (0) +#define PRUCORE_1 (1) + +#define PRUCORE_CONTROL_PCRESETVAL_MASK (0xFFFF0000u) +#define PRUCORE_CONTROL_PCRESETVAL_SHIFT (0x00000010u) +#define PRUCORE_CONTROL_PCRESETVAL_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_MASK (0x00008000u) +#define PRUCORE_CONTROL_RUNSTATE_SHIFT (0x0000000Fu) +#define PRUCORE_CONTROL_RUNSTATE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_HALT (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_RUN (0x00000001u) +#define PRUCORE_CONTROL_SINGLESTEP_MASK (0x00000100u) +#define PRUCORE_CONTROL_SINGLESTEP_SHIFT (0x00000008u) +#define PRUCORE_CONTROL_SINGLESTEP_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SINGLESTEP_FREERUN (0x00000000u) +#define PRUCORE_CONTROL_SINGLESTEP_SINGLE (0x00000001u) +#define PRUCORE_CONTROL_COUNTENABLE_MASK (0x00000008u) +#define PRUCORE_CONTROL_COUNTENABLE_SHIFT (0x00000003u) +#define PRUCORE_CONTROL_COUNTENABLE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_COUNTENABLE_DISABLE (0x00000000u) +#define PRUCORE_CONTROL_COUNTENABLE_ENABLE (0x00000001u) +#define PRUCORE_CONTROL_SLEEPING_MASK (0x00000004u) +#define PRUCORE_CONTROL_SLEEPING_SHIFT (0x00000002u) +#define PRUCORE_CONTROL_SLEEPING_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SLEEPING_NOTASLEEP (0x00000000u) +#define PRUCORE_CONTROL_SLEEPING_ASLEEP (0x00000001u) +#define PRUCORE_CONTROL_ENABLE_MASK (0x00000002u) +#define PRUCORE_CONTROL_ENABLE_SHIFT (0x00000001u) +#define PRUCORE_CONTROL_ENABLE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_ENABLE_DISABLE (0x00000000u) +#define PRUCORE_CONTROL_ENABLE_ENABLE (0x00000001u) +#define PRUCORE_CONTROL_SOFTRESET_MASK (0x00000001u) +#define PRUCORE_CONTROL_SOFTRESET_SHIFT (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_RESET (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_OUT_OF_RESET (0x00000001u) +#define PRUCORE_CONTROL_RESETVAL (0x00000000u) + +struct prusscore_regs { + u32 control; + u32 status; + u32 wakeup; + u32 cyclecnt; + u32 stallcnt; + u8 rsvd0[12]; + u32 contabblkidx0; + u32 contabblkidx1; + u32 contabproptr0; + u32 contabproptr1; + u8 rsvd1[976]; + u32 intgpr[32]; + u32 intcter[32]; + u8 rsvd2[768]; +}; + +struct pruss_intc_regs { + u32 revid; + u32 control; + u8 res1[8]; + u32 glblen; + u8 res2[8]; + u32 glblnstlvl; + u32 statidxset; + u32 statidxclr; + u32 enidxset; + u32 enidxclr; + u8 res3[4]; + u32 hostintenidxset; + u32 hostintenidxclr; + u8 res4[68]; + u32 glblpriidx; + u8 res5[380]; + u32 statsetint[2]; + u8 res6[120]; + u32 statclrint[2]; + u8 res7[120]; + u32 enableset[2]; + u8 res8[120]; + u32 enableclr[2]; + u8 res9[120]; + u32 chanmap[16]; + u8 res10[960]; + u32 hostmap[2]; + u8 res11[248]; + u32 hostintpriidx[10]; + u8 res12[984]; + u32 polarity[2]; + u8 res13[120]; + u32 type[2]; + u8 res14[888]; + u32 hostintnstlvl[10]; + u8 res15[984]; + u32 hostinten; + u8 res16[6907]; +}; + +struct pruss_map { + u8 dram0[512]; + u8 res1[7680]; + u8 dram1[512]; + u8 res2[7680]; + struct pruss_intc_regs intc; + struct prusscore_regs core[2]; + u8 iram0[4096]; + u8 res3[12288]; + u8 iram1[4096]; + u8 res4[12288]; +}; +#endif -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:20 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:20 +0530 Subject: [PATCH v4 02/11] da850: add pruss clock. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-3-git-send-email-subhasish@mistralsolutions.com> This patch adds the PRUSS clock. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/da850.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 2d50885..a7cf2d0 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { .flags = ALWAYS_ENABLED, }; +static struct clk pruss_clk = { + .name = "pruss", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC0_PRUSS, +}; + static struct clk uart0_clk = { .name = "uart0", .parent = &pll0_sysclk2, @@ -401,6 +407,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "tpcc1", &tpcc1_clk), CLK(NULL, "tptc2", &tptc2_clk), CLK(NULL, "uart0", &uart0_clk), + CLK(NULL, "pruss", &pruss_clk), CLK(NULL, "uart1", &uart1_clk), CLK(NULL, "uart2", &uart2_clk), CLK(NULL, "aintc", &aintc_clk), -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:21 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:21 +0530 Subject: [PATCH v4 03/11] da850: pruss platform specific additions. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-4-git-send-email-subhasish@mistralsolutions.com> This patch adds the platform device and assignes the platform resources for the PRUSS mfd driver. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/devices-da8xx.c | 63 ++++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 4 ++ 2 files changed, 67 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 625d4b6..f51c9ad 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "clock.h" @@ -510,6 +511,68 @@ void __init da8xx_register_mcasp(int id, struct snd_platform_data *pdata) } } +static struct resource da8xx_pruss_resources[] = { + { + .name = "da8xx_pruss", + .start = DA8XX_PRUSS_MEM_BASE, + .end = DA8XX_PRUSS_MEM_BASE + 0xFFFF, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_DA8XX_EVTOUT0, + .end = IRQ_DA8XX_EVTOUT0, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_DA8XX_EVTOUT1, + .end = IRQ_DA8XX_EVTOUT1, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_DA8XX_EVTOUT2, + .end = IRQ_DA8XX_EVTOUT2, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_DA8XX_EVTOUT3, + .end = IRQ_DA8XX_EVTOUT3, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_DA8XX_EVTOUT4, + .end = IRQ_DA8XX_EVTOUT4, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_DA8XX_EVTOUT5, + .end = IRQ_DA8XX_EVTOUT5, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_DA8XX_EVTOUT6, + .end = IRQ_DA8XX_EVTOUT6, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_DA8XX_EVTOUT7, + .end = IRQ_DA8XX_EVTOUT7, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device da8xx_pruss_mfddev = { + .name = "pruss_mfd", + .id = -1, + .num_resources = ARRAY_SIZE(da8xx_pruss_resources), + .resource = da8xx_pruss_resources, +}; + +int __init da8xx_register_pruss_mfd(struct mfd_cell *cell) +{ + da8xx_pruss_mfddev.dev.platform_data = cell; + return platform_device_register(&da8xx_pruss_mfddev); +} + static const struct display_panel disp_panel = { QVGA, 16, diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 09b8ddb..0c23035 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -73,6 +75,7 @@ extern unsigned int da850_max_speed; #define DA8XX_DDR2_CTL_BASE 0xb0000000 #define DA8XX_ARM_RAM_BASE 0xffff0000 #define DA8XX_SHARED_RAM_BASE 0x80000000 +#define DA8XX_PRUSS_MEM_BASE 0x01C30000 void __init da830_init(void); void __init da850_init(void); @@ -85,6 +88,7 @@ int da8xx_register_watchdog(void); int da8xx_register_usb20(unsigned mA, unsigned potpgt); int da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata); int da8xx_register_emac(void); +int da8xx_register_pruss_mfd(struct mfd_cell *); int da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata); int da8xx_register_mmcsd0(struct davinci_mmc_config *config); int da850_register_mmcsd1(struct davinci_mmc_config *config); -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:22 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:22 +0530 Subject: [PATCH v4 04/11] da850: pruss board specific additions. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-5-git-send-email-subhasish@mistralsolutions.com> This patch adds board specific initializations and setup routines. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/board-da850-evm.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..0b6b948 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1117,6 +1117,15 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +static struct mfd_cell cell[] = { + {.name = NULL,}, +}; + +static int __init da8xx_evm_setup_pruss(void) +{ + return da8xx_register_pruss_mfd(cell); +} + static __init void da850_evm_init(void) { int ret; @@ -1191,6 +1200,11 @@ static __init void da850_evm_init(void) da8xx_register_mcasp(0, &da850_evm_snd_data); + ret = da8xx_evm_setup_pruss(); + if (ret) + pr_warning("%s: pruss initialization failed: %d\n", + __func__, ret); + ret = davinci_cfg_reg_list(da850_lcdcntl_pins); if (ret) pr_warning("da850_evm_init: lcdcntl mux setup failed: %d\n", -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:23 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:23 +0530 Subject: [PATCH v4 05/11] mfd: pruss SUART private data. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-6-git-send-email-subhasish@mistralsolutions.com> This patch adds the PRUSS SUART data. Signed-off-by: Subhasish Ghosh --- include/linux/mfd/pruss.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/include/linux/mfd/pruss.h b/include/linux/mfd/pruss.h index 8ef25b3..c5e2af2 100644 --- a/include/linux/mfd/pruss.h +++ b/include/linux/mfd/pruss.h @@ -87,6 +87,11 @@ #define PRU_INTC_CHANMAP12_HALF (0x00010908) #define PRU_INTC_REGMAP_MASK (0xFFFFFFFF) +struct da850_evm_pruss_suart_data { + u32 version; + int (*setup)(void); +}; + s32 pruss_enable(struct device *dev, u8 pruss_num); s32 pruss_load(struct device *dev, u8 pruss_num, -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:24 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:24 +0530 Subject: [PATCH v4 06/11] da850: pruss SUART board specific additions. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-7-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss SUART pin mux and registers the device with the pruss mfd driver. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/board-da850-evm.c | 46 ++++++++++++++++++++++++++++++- 1 files changed, 45 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 0b6b948..e7fdf31 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1117,8 +1117,52 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +static const short da850_evm_pruss_suart_pins[] = { + DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, + DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, + DA850_AXR_13, DA850_AXR_9, DA850_AXR_7, + DA850_AXR_14, DA850_AXR_10, DA850_AXR_8, + -1 +}; + +static int __init da850_evm_pruss_suart_setup(void) +{ + int ret; + + ret = davinci_cfg_reg_list(da850_evm_pruss_suart_pins); + if (ret) + pr_warning("%s: da850_evm_pruss_suart_pins " + "mux setup failed: %d\n", __func__, ret); + return ret; +} + +static struct da850_evm_pruss_suart_data suart_data = { + .version = 1, + .setup = da850_evm_pruss_suart_setup, +}; + +static struct resource da850_evm_pruss_suart_resource[] = { + { + .name = "da8xx_mcasp0_iomem", + .start = DAVINCI_DA8XX_MCASP0_REG_BASE, + .end = DAVINCI_DA8XX_MCASP0_REG_BASE + + (SZ_1K * 12) - 1, + .flags = IORESOURCE_MEM, + }, +}; + static struct mfd_cell cell[] = { - {.name = NULL,}, + { + .id = 0, + .name = "da8xx_pruss_uart", + .platform_data = &suart_data, + .data_size = sizeof(suart_data), + .num_resources = ARRAY_SIZE(da850_evm_pruss_suart_resource), + .resources = da850_evm_pruss_suart_resource, + }, + { + .name = NULL, + }, }; static int __init da8xx_evm_setup_pruss(void) -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:25 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:25 +0530 Subject: [PATCH v4 07/11] da850: pruss SUART platform specific additions. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-8-git-send-email-subhasish@mistralsolutions.com> This patch adds the McASP clock alias. The alias is used by the pruss suart driver for enabling the McASP PSC. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/devices-da8xx.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index f51c9ad..8f41ae4 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -567,9 +567,27 @@ static struct platform_device da8xx_pruss_mfddev = { .resource = da8xx_pruss_resources, }; +#ifdef CONFIG_SERIAL_PRUSS_SUART_MODULE +static int da8xx_mcasp_clk_add_alias(void) +{ + return clk_add_alias(NULL, "da8xx_pruss_uart.0", + NULL, &da850_mcasp_device.dev); +} +#else +static int da8xx_mcasp_clk_add_alias(void) +{ + return 0; +} +#endif int __init da8xx_register_pruss_mfd(struct mfd_cell *cell) { + int ret; + da8xx_pruss_mfddev.dev.platform_data = cell; + + ret = da8xx_mcasp_clk_add_alias(); + if (ret < 0) + return ret; return platform_device_register(&da8xx_pruss_mfddev); } -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:26 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:26 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the TTY compliant Soft-UART device emulated on PRUSS. This patch depends on: davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. https://patchwork.kernel.org/patch/615681/ davinci: changed SRAM allocator to shared ram. https://patchwork.kernel.org/patch/549351/ Signed-off-by: Subhasish Ghosh --- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 6 + drivers/tty/serial/pruss_suart.c | 1061 ++++++++++++++++++++ drivers/tty/serial/pruss_suart.h | 1038 +++++++++++++++++++ drivers/tty/serial/pruss_suart_api.c | 1710 ++++++++++++++++++++++++++++++++ drivers/tty/serial/pruss_suart_utils.c | 393 ++++++++ include/linux/serial_core.h | 2 + 7 files changed, 4228 insertions(+), 0 deletions(-) create mode 100644 drivers/tty/serial/pruss_suart.c create mode 100644 drivers/tty/serial/pruss_suart.h create mode 100644 drivers/tty/serial/pruss_suart_api.c create mode 100644 drivers/tty/serial/pruss_suart_utils.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2b83346..6c26ebf 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1596,4 +1596,22 @@ config SERIAL_PCH_UART This driver is for PCH(Platform controller Hub) UART of Intel EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. + +config SERIAL_PRUSS_SUART + depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850 + select SERIAL_CORE + tristate "PRUSS based SoftUART emulation on DA8XX" + ---help--- + This driver emulates up to eight different UARTs on the PRUSS. + You may modify the NR_SUARTS macro in the driver to emulate + less number of UARTS as per your requirement. + If not sure, mark No + +config PRUSS_SUART_MCASP + depends on ARCH_DAVINCI_DA830 && SERIAL_PRUSS_SUART + default "0" + int "McASP number" + ---help--- + Enter the McASP number to use with SUART (0, 1 or 2). + You will need to recompile the kernel if this is changed. endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 8ea92e9..e1eaaf3 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,3 +92,9 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o + +pruss_uart-objs := pruss_suart.o \ + pruss_suart_api.o \ + pruss_suart_utils.o + +obj-$(CONFIG_SERIAL_PRUSS_SUART) += pruss_uart.o diff --git a/drivers/tty/serial/pruss_suart.c b/drivers/tty/serial/pruss_suart.c new file mode 100644 index 0000000..37c3c21 --- /dev/null +++ b/drivers/tty/serial/pruss_suart.c @@ -0,0 +1,1061 @@ +/* + * PRUSS SUART Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU SUART Emulation and the + * specs for the same is available at + * + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed as is WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pruss_suart.h" + +#define NR_SUART 8 +#define DRV_NAME "da8xx_pruss_uart" +#define DRV_DESC "PRUSS SUART Driver v1.0" +#define MAX_SUART_RETRIES 100 +#define SUART_CNTX_SZ 512 +#define SUART_FIFO_TIMEOUT_DFLT 5 +#define SUART_FIFO_TIMEOUT_MIN 4 +#define SUART_FIFO_TIMEOUT_MAX 500 + +/* Default timeout set to 5ms */ +static s16 suart_timeout = SUART_FIFO_TIMEOUT_DFLT; +module_param(suart_timeout, short, S_IRUGO); +MODULE_PARM_DESC(suart_timeout, + "fifo timeout in milli seconds (min: 4; max: 500)"); + +struct suart_fifo { + void *fifo_vaddr_buff_tx; + void *fifo_vaddr_buff_rx; + void *fifo_phys_addr_tx; + void *fifo_phys_addr_rx; +}; + +struct omapl_pru_suart { + struct uart_port port[NR_SUART]; + struct device *dev; + unsigned long tx_empty[NR_SUART]; + struct clk *clk_mcasp; + struct suart_fifo suart_fifo_addr[NR_SUART]; + struct suart_handle suart_hdl[NR_SUART]; + struct pruss_suart_iomap suart_iomap; + struct tasklet_struct tx_task[NR_SUART]; + u32 clk_freq_pru; + u32 clk_freq_mcasp; + u32 tx_loadsz; +}; + +static u32 suart_get_duplex(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + return soft_uart->suart_hdl[uart_no].uart_type; +} + +static inline void __stop_tx(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + struct uart_port *port = &soft_uart->port[uart_no]; + u16 txready; + u32 i; + + /* Check if any TX in progress */ + for (i = 0, txready = 1; (i < 10000) && txready; i++) { + txready = (pru_softuart_get_tx_status + (dev, &soft_uart->suart_hdl[uart_no]) & + CHN_TXRX_STATUS_RDY); + } + /* To stop tx, disable the TX interrupt */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[uart_no].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl[uart_no]); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_stop_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + + __stop_tx(soft_uart, port->line); +} + +static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit; + struct device *dev = soft_uart->dev; + s32 count = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX)) + return; + + if (uart_circ_empty(xmit) || + uart_tx_stopped(&soft_uart->port[uart_no])) { + pruss_suart_stop_tx(&soft_uart->port[uart_no]); + set_bit(0, &soft_uart->tx_empty[uart_no]); + return; + } + + for (count = 0; count <= soft_uart->tx_loadsz; count++) { + *((s8 *)soft_uart->suart_fifo_addr[uart_no].fifo_vaddr_buff_tx + + count) = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + soft_uart->port[uart_no].icount.tx++; + if (uart_circ_empty(xmit)) { + uart_circ_clear(xmit); + break; + } + } + + if (count == (SUART_FIFO_LEN + 1)) + count = SUART_FIFO_LEN; + + /* Write the character to the data port */ + if (pru_softuart_write(dev, + &soft_uart->suart_hdl[uart_no], + (u32 *)&soft_uart->suart_fifo_addr + [uart_no].fifo_phys_addr_tx, count) != 0) { + dev_err(dev, "failed to tx data\n"); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&soft_uart->port[uart_no]); + +#if 0 + if (uart_circ_empty(xmit)) + __stop_tx(soft_uart, uart_no); +#endif +} + +static void suart_tx_task(unsigned long data) +{ + struct uart_port *port = (struct uart_port *)data; + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + + omapl_pru_tx_chars(soft_uart, port->line); +} + +static void omapl_pru_rx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct tty_struct *tty = NULL; + struct device *dev = soft_uart->dev; + s8 flags = TTY_NORMAL; + u16 rx_status, data_len = SUART_FIFO_LEN; + u32 data_len_read; + u8 suart_data[SUART_FIFO_LEN + 1]; + s32 i = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_RX)) + return; + + /* read the status */ + rx_status = pru_softuart_get_rx_status(dev, + &soft_uart->suart_hdl[uart_no]); + + pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no], + suart_data, data_len + 1, &data_len_read); + + tty = tty_port_tty_get(&soft_uart->port[uart_no].state->port); + + if (!tty) + return; + + /* check for errors */ + if (rx_status & CHN_TXRX_STATUS_ERR) { + if (rx_status & CHN_TXRX_STATUS_FE) + soft_uart->port[uart_no].icount.frame++; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + soft_uart->port[uart_no].icount.overrun++; + if (rx_status & CHN_TXRX_STATUS_BI) + soft_uart->port[uart_no].icount.brk++; + rx_status &= soft_uart->port[uart_no]. + read_status_mask; + if (rx_status & CHN_TXRX_STATUS_FE) + flags = TTY_FRAME; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + flags = TTY_OVERRUN; + if (rx_status & CHN_TXRX_STATUS_BI) + flags = TTY_BREAK; + +#ifdef SUPPORT_SYSRQ + soft_uart->port[uart_no].sysrq = 0; +#endif + } else { + for (i = 0; i <= data_len_read; i++) { + soft_uart->port[uart_no].icount.rx++; + /* check for sys rq */ + if (uart_handle_sysrq_char + (&soft_uart->port[uart_no], suart_data)) + continue; + } + tty_insert_flip_string(tty, suart_data, data_len_read); + } + + /* push data into tty */ + pru_softuart_clr_rx_status(dev, &soft_uart->suart_hdl[uart_no]); + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static irqreturn_t pruss_suart_interrupt(s32 irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u16 txrx_flag; + u32 ret; + unsigned long flags = 0; + u16 uart_num = port->line + 1; + + spin_lock_irqsave(&port->lock, flags); + + do { + ret = pru_softuart_get_isrstatus(dev, uart_num, &txrx_flag); + if (ret != 0) { + dev_err(dev, "suart%d: failed to get interrupt, ret:" + " 0x%X txrx_flag 0x%X\n", + port->line, ret, txrx_flag); + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_NONE; + } + if ((PRU_RX_INTR & txrx_flag) == PRU_RX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_RX_INTR); + if ((soft_uart->port[port->line].ignore_status_mask & + CHN_TXRX_STATUS_RDY) == CHN_TXRX_STATUS_RDY) { + pru_softuart_clr_rx_status(dev, + &soft_uart->suart_hdl + [port->line]); + } else { + omapl_pru_rx_chars(soft_uart, port->line); + } + } + + if ((PRU_TX_INTR & txrx_flag) == PRU_TX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_TX_INTR); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl + [port->line]); + tasklet_schedule(&soft_uart->tx_task[port->line]); + } + } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR)); + + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_HANDLED; +} + +static void pruss_suart_stop_rx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + /* disable rx interrupt */ + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_enable_ms(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + dev_err(dev, "modem control timer not supported\n"); +} + +static void pruss_suart_start_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + /* unmask the tx interrupts */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + spin_unlock_irqrestore(&port->lock, flags); + + if (test_and_clear_bit(0, &soft_uart->tx_empty[port->line])) + omapl_pru_tx_chars(soft_uart, port->line); +} + +static u32 pruss_suart_tx_empty(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + + return (pru_softuart_get_tx_status(dev, + &soft_uart->suart_hdl[port->line]) + & CHN_TXRX_STATUS_RDY) ? 0 : TIOCSER_TEMT; +} + +static u32 pruss_suart_get_mctrl(struct uart_port *port) +{ + return -ENOTSUPP; +} + +static void pruss_suart_set_mctrl(struct uart_port *port, u32 mctrl) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + dev_dbg(dev, "modem control not supported\n"); +} + +static void pruss_suart_break_ctl(struct uart_port *port, s32 break_state) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + + if (break_state == -1) + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + else + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u8 cval = 0; + unsigned long flags = 0; + u32 baud = 0; + u32 old_csize = old ? old->c_cflag & CSIZE : CS8; + +/* + * Do not allow unsupported configurations to be set + */ + if (1) { + termios->c_cflag &= ~(CRTSCTS | CMSPAR | CSTOPB + | PARENB | PARODD | CMSPAR); + } + + switch (termios->c_cflag & CSIZE) { + case CS6: + cval = ePRU_SUART_DATA_BITS6; + break; + case CS7: + cval = ePRU_SUART_DATA_BITS7; + break; + default: + case CS8: + cval = ePRU_SUART_DATA_BITS8; + break; + } + /* + * We do not support CS5. + */ + if ((termios->c_cflag & CSIZE) == CS5) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + } + if (pru_softuart_setdatabits + (dev, &soft_uart->suart_hdl[port->line], cval, cval) != 0) + dev_err(dev, "failed to set data bits to: %d\n", cval); + +/* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + +/* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&port->lock, flags); + + /* Set the baud */ + if (pru_softuart_setbaud(dev, &soft_uart->suart_hdl[port->line], + SUART_DEFAULT_BAUD / baud, + SUART_DEFAULT_BAUD / baud) != 0) + dev_err(dev, "failed to set baud to: %d\n", baud); + +/* + * update port->read_config_mask and port->ignore_config_mask + * to indicate the events we are interested in receiving + */ + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + port->read_status_mask = 0; + if (termios->c_iflag & INPCK) { /* Input parity check not supported, + just enabled FE */ + port->read_status_mask |= CHN_TXRX_STATUS_FE; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + if (termios->c_iflag & (BRKINT | PARMRK)) { + port->read_status_mask |= CHN_TXRX_STATUS_BI; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= CHN_TXRX_STATUS_BI; + /* + * If we're ignoring break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) { + port->ignore_status_mask |= + (CHN_TXRX_STATUS_OVRNERR | CHN_TXRX_STATUS_FE); + /* + * Overrun in case of RX + * Underrun in case of TX + */ + suart_intr_clrmask(dev, soft_uart-> + suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) { + port->ignore_status_mask |= CHN_TXRX_STATUS_RDY; + pruss_suart_stop_rx(port); + } + /* + * update the per port timeout + */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +/* + * Grab any interrupt resources and initialise any low level driver + * state. Enable the port for reception. It should not activate + * RTS nor DTR; this will be done via a separate call to set_mctrl. + * + * This method will only be called when the port is initially opened. + * + * Locking: port_sem taken. + * Interrupts: globally disabled. + */ +static s32 pruss_suart_startup(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + s32 retval; + + /* + * Disable interrupts from this port + */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); + + retval = request_irq(port->irq, pruss_suart_interrupt, + port->irqflags, "suart_irq", port); + if (retval) { + free_irq(port->irq, port); /* should we free this if err */ + goto out; + } + /* + * enable interrupts from this port + */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + spin_unlock_irqrestore(&port->lock, flags); + + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_TX) + == ePRU_SUART_HALF_TX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_TX_INTR, true); + } + /* Seed RX if port is half-rx or full-duplex */ + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_RX) + == ePRU_SUART_HALF_RX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_RX_INTR, true); + pru_softuart_read(dev, &soft_uart->suart_hdl[port->line], + (u32 *)&soft_uart->suart_fifo_addr[port->line]. + fifo_phys_addr_rx, SUART_FIFO_LEN); + } +out: + return retval; +} + +/* + * Disable the port, disable any break condition that may be in + * effect, and free any interrupt resources. It should not disable + * RTS nor DTR; this will have already been done via a separate + * call to set_mctrl. + * + * Drivers must not access port->info once this call has completed. + * + * This method will only be called when there are no more users of + * this port. + * + * Locking: port_sem taken. + * Interrupts: caller dependent. + */ + +static void pruss_suart_shutdown(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + /* + * Disable interrupts from this port + */ + /* Disable BI and FE intr */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); + + /* free interrupts */ + free_irq(port->irq, port); +} + +/* + * Return a pointer to a string constant describing the specified + * port, or return NULL, in which case the string 'unknown' is + * substituted. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static const char *pruss_suart_type(struct uart_port *port) +{ + return "suart_tty"; +} + +/* + * Release any memory and IO region resources currently in use by + * the port. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_release_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + + if (0 != pru_softuart_close(&soft_uart->suart_hdl[port->line])) + dev_err(&pdev->dev, "failed to close suart\n"); + + return; +} + +/* + * Request any memory and IO region resources required by the port. + * If any fail, no resources should be registered when this function + * returns, and it should return -EBUSY on failure. + * + * Locking: none. + * Interrupts: caller dependent. + * + * We need to d/l the f/w in probe and since this api + * is called per uart, the request_mem_region should + * be called in probe itself. + */ +static s32 pruss_suart_request_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + struct device *dev = soft_uart->dev; + struct suart_config pru_suart_config; + s16 timeout = 0; + u32 err = 0; + + if (soft_uart == NULL) { + dev_err(&pdev->dev, "soft_uart ptr failed\n"); + return -ENODEV; + } + err = pru_softuart_open(&soft_uart->suart_hdl[port->line]); + if (err != 0) { + dev_err(&pdev->dev, "failed to open suart: %d\n", err); + err = -ENODEV; + goto exit; + } + set_bit(0, &soft_uart->tx_empty[port->line]); + + /* set fifo /timeout */ + if (SUART_FIFO_TIMEOUT_MIN > suart_timeout) { + dev_err(&pdev->dev, "fifo timeout less than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MIN); + suart_timeout = SUART_FIFO_TIMEOUT_MIN; + } else if (SUART_FIFO_TIMEOUT_MAX < suart_timeout) { + dev_err(&pdev->dev, "fifo timeout more than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MAX); + suart_timeout = SUART_FIFO_TIMEOUT_MAX; + } + + /* This is only for x8 */ + timeout = (SUART_DEFAULT_BAUD * suart_timeout) / 1000; + pru_set_fifo_timeout(dev, timeout); + + if (soft_uart->suart_hdl[port->line].uart_num == PRU_SUART_UART1) { + pru_suart_config.tx_serializer = PRU_SUART0_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART0_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART2) { + pru_suart_config.tx_serializer = PRU_SUART1_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART1_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART3) { + pru_suart_config.tx_serializer = PRU_SUART2_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART2_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART4) { + pru_suart_config.tx_serializer = PRU_SUART3_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART3_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART5) { + pru_suart_config.tx_serializer = PRU_SUART4_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART4_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART6) { + pru_suart_config.tx_serializer = PRU_SUART5_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART5_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART7) { + pru_suart_config.tx_serializer = PRU_SUART6_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART6_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART8) { + pru_suart_config.tx_serializer = PRU_SUART7_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART7_CONFIG_RX_SER; + } else { + return -ENOTSUPP; + } + + /* Some defaults to startup. reconfigured by terimos later */ + pru_suart_config.tx_clk_divisor = 1; + pru_suart_config.rx_clk_divisor = 1; + pru_suart_config.tx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.rx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.oversampling = SUART_DEFAULT_OVRSMPL; + + if (pru_softuart_setconfig(dev, &soft_uart->suart_hdl[port->line], + &pru_suart_config) != 0) { + dev_err(&pdev->dev, + "pru_softuart_setconfig: failed to set config: %X\n", + err); + } +exit: + return err; +} + +/* + * Perform any autoconfiguration steps required for the port. `flag` + * contains a bit mask of the required configuration. UART_CONFIG_TYPE + * indicates that the port requires detection and identification. + * port->type should be set to the type found, or PORT_UNKNOWN if + * no port was detected. + * + * UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, + * which should be probed using standard kernel autoprobing techniques. + * This is not necessary on platforms where ports have interrupts + * internally hard wired (eg, system on a chip implementations). + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_config_port(struct uart_port *port, s32 flags) +{ + if (flags & UART_CONFIG_TYPE && pruss_suart_request_port(port) == 0) + port->type = PORT_DA8XX_PRU_SUART; +} + +/* + * Verify the new serial port information contained within serinfo is + * suitable for this port type. + * + * Locking: none. + * Interrupts: caller dependent. + */ +static s32 pruss_suart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + s32 ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DA8XX_PRU_SUART) + ret = -EINVAL; + if (soft_uart->port[port->line].irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (soft_uart->port[port->line].uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)soft_uart->port[port->line].mapbase != ser->iomem_base) + ret = -EINVAL; + if (soft_uart->port[port->line].iobase != ser->port) + ret = -EINVAL; + return ret; +} + +static struct uart_ops pruss_suart_ops = { + .tx_empty = pruss_suart_tx_empty, + .set_mctrl = pruss_suart_set_mctrl, + .get_mctrl = pruss_suart_get_mctrl, + .stop_tx = pruss_suart_stop_tx, + .start_tx = pruss_suart_start_tx, + .stop_rx = pruss_suart_stop_rx, + .enable_ms = pruss_suart_enable_ms, + .break_ctl = pruss_suart_break_ctl, + .startup = pruss_suart_startup, + .shutdown = pruss_suart_shutdown, + .set_termios = pruss_suart_set_termios, + .type = pruss_suart_type, + .release_port = pruss_suart_release_port, + .request_port = pruss_suart_request_port, + .config_port = pruss_suart_config_port, + .verify_port = pruss_suart_verify_port, +}; + +static struct uart_driver pruss_suart_reg = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttySU", + .major = 0, + .minor = 16, + .nr = NR_SUART, +}; + +static struct pruss_suart_initparams init_params = { + .tx_baud_value = SUART_DEFAULT_BAUD, + .rx_baud_value = SUART_DEFAULT_BAUD, + .oversampling = SUART_DEFAULT_OVRSMPL, +}; + +static s32 __devinit pruss_suart_probe(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart; + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + struct resource *res; + struct clk *clk_pruss = NULL; + const struct firmware *fw; + s32 err, i; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + (pdata->setup)(); + + soft_uart = kzalloc(sizeof(struct omapl_pru_suart), GFP_KERNEL); + if (!soft_uart) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get resource"); + return -ENOMEM; + } + + if (!request_mem_region(res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "mcasp memory region already claimed!\n"); + err = -EBUSY; + goto probe_exit; + } + + soft_uart->suart_iomap.mcasp_io_addr = ioremap(res->start, + resource_size(res)); + if (!soft_uart->suart_iomap.mcasp_io_addr) { + dev_err(&pdev->dev, "mcasp ioremap failed\n"); + err = -EFAULT; + goto probe_exit_1; + } + + soft_uart->suart_iomap.p_fifo_buff_virt_base = + sram_alloc(SUART_CNTX_SZ * NR_SUART * 2, + (dma_addr_t *) &soft_uart->suart_iomap.p_fifo_buff_phys_base); + if (!soft_uart->suart_iomap.p_fifo_buff_virt_base) + goto probe_exit_iounmap; + + clk_pruss = clk_get(NULL, "pruss"); + if (IS_ERR(clk_pruss)) { + dev_err(&pdev->dev, "no clock available: pruss\n"); + err = -ENODEV; + goto probe_exit_iounmap; + } + soft_uart->clk_freq_pru = clk_get_rate(clk_pruss); + clk_put(clk_pruss); + + soft_uart->clk_mcasp = clk_get(&pdev->dev, NULL); + if (IS_ERR(soft_uart->clk_mcasp)) { + dev_err(&pdev->dev, "no clock available: mcasp\n"); + err = -ENODEV; + soft_uart->clk_mcasp = NULL; + goto probe_exit_sram_free; + } + + soft_uart->clk_freq_mcasp = clk_get_rate(soft_uart->clk_mcasp); + clk_enable(soft_uart->clk_mcasp); + + err = request_firmware(&fw, "PRU_SUART_Emulation.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + dev_info(&pdev->dev, "fw size %td. downloading...\n", fw->size); + + /* download firmware into pru & init */ + err = pru_softuart_init(dev, &init_params, fw->data, fw->size, + soft_uart->clk_freq_pru / 1000000, + &soft_uart->suart_iomap); + if (err) { + dev_err(&pdev->dev, "pruss init error\n"); + err = -ENODEV; + goto probe_release_fw; + } + release_firmware(fw); + + platform_set_drvdata(pdev, &soft_uart->port[0]); + soft_uart->dev = dev; + + for (i = 0; i < NR_SUART; i++) { + soft_uart->port[i].ops = &pruss_suart_ops; + soft_uart->port[i].iotype = UPIO_MEM; + soft_uart->port[i].flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + soft_uart->port[i].mapbase = + (u32)soft_uart->suart_iomap.p_fifo_buff_virt_base; + soft_uart->port[i].membase = + soft_uart->suart_iomap.mcasp_io_addr; + soft_uart->port[i].type = PORT_DA8XX_PRU_SUART; + soft_uart->port[i].irq = + platform_get_irq(to_platform_device(dev->parent), i); + soft_uart->port[i].dev = &pdev->dev; + soft_uart->port[i].irqflags = IRQF_SHARED; + soft_uart->port[i].uartclk = soft_uart->clk_freq_mcasp; + soft_uart->port[i].fifosize = SUART_FIFO_LEN; + soft_uart->tx_loadsz = SUART_FIFO_LEN; + soft_uart->port[i].custom_divisor = 1; + soft_uart->port[i].line = i; + soft_uart->suart_hdl[i].uart_num = i + 1; + soft_uart->port[i].serial_in = NULL; + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_tx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_rx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_tx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_rx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->port[i].serial_out = NULL; + tasklet_init(&soft_uart->tx_task[i], suart_tx_task, + (unsigned long)&soft_uart->port[i]); + uart_add_one_port(&pruss_suart_reg, &soft_uart->port[i]); + } + + dev_info(&pdev->dev, + "%s device registered (pru_clk=%d, asp_clk=%d)\n", + DRV_NAME, soft_uart->clk_freq_pru, soft_uart->clk_freq_mcasp); + + return 0; + +probe_release_fw: + release_firmware(fw); +probe_exit_clk: + clk_put(soft_uart->clk_mcasp); + clk_disable(soft_uart->clk_mcasp); +probe_exit_sram_free: + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); +probe_exit_iounmap: + iounmap(soft_uart->suart_iomap.mcasp_io_addr); +probe_exit_1: + release_mem_region(res->start, + resource_size(res)); +probe_exit: + kfree(soft_uart); + return err; +} + +static s32 __devexit pruss_suart_remove(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart = platform_get_drvdata(pdev); + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + struct resource *res; + int i; + + pdata = dev->platform_data; + if (!pdata) + dev_err(&pdev->dev, "platform data not found\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get resource"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, NULL); + + if (soft_uart) { + for (i = 0; i < NR_SUART; i++) { + uart_remove_one_port(&pruss_suart_reg, + &soft_uart->port[i]); + } + } + + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); + clk_put(soft_uart->clk_mcasp); + pru_mcasp_deinit(); + clk_disable(soft_uart->clk_mcasp); + iounmap(soft_uart->suart_iomap.mcasp_io_addr); + if (pdata) { + release_mem_region(res->start, + resource_size(res)); + } + kfree(soft_uart); + return 0; +} + +#define pruss_suart_suspend NULL +#define pruss_suart_resume NULL + +static struct platform_driver serial_pruss_driver = { + .probe = pruss_suart_probe, + .remove = __devexit_p(pruss_suart_remove), + .suspend = pruss_suart_suspend, + .resume = pruss_suart_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static s32 __init pruss_suart_init(void) +{ + s32 ret; + + pruss_suart_reg.nr = NR_SUART; + ret = uart_register_driver(&pruss_suart_reg); + if (ret) + return ret; + ret = platform_driver_register(&serial_pruss_driver); + if (ret) + goto out; + + pr_debug("SUART serial driver loaded\n"); + return ret; +out: + uart_unregister_driver(&pruss_suart_reg); + return ret; +} + +module_init(pruss_suart_init); + +static void __exit pruss_suart_exit(void) +{ + platform_driver_unregister(&serial_pruss_driver); + uart_unregister_driver(&pruss_suart_reg); + pr_debug("SUART serial driver unloaded\n"); +} + +module_exit(pruss_suart_exit); + +/* Module information */ +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/tty/serial/pruss_suart.h b/drivers/tty/serial/pruss_suart.h new file mode 100644 index 0000000..f3a2a9d --- /dev/null +++ b/drivers/tty/serial/pruss_suart.h @@ -0,0 +1,1038 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _SUART_API_H_ +#define _SUART_API_H_ + +#include +#include +#include +#include + +#define SINGLE_PRU 0 +#define BOTH_PRU 1 +#define PRU_ACTIVE BOTH_PRU +#define PRU_CLK_228 228 +#define PRU_CLK_186 186 + +#define PRU_SUART_SERIALIZER_0 (0u) +#define PRU_SUART_SERIALIZER_1 (1u) +#define PRU_SUART_SERIALIZER_2 (2u) +#define PRU_SUART_SERIALIZER_3 (3u) +#define PRU_SUART_SERIALIZER_4 (4u) +#define PRU_SUART_SERIALIZER_5 (5u) +#define PRU_SUART_SERIALIZER_6 (6u) +#define PRU_SUART_SERIALIZER_7 (7u) +#define PRU_SUART_SERIALIZER_8 (8u) +#define PRU_SUART_SERIALIZER_9 (9u) +#define PRU_SUART_SERIALIZER_10 (10u) +#define PRU_SUART_SERIALIZER_11 (11u) +#define PRU_SUART_SERIALIZER_12 (12u) +#define PRU_SUART_SERIALIZER_13 (13u) +#define PRU_SUART_SERIALIZER_14 (14u) +#define PRU_SUART_SERIALIZER_15 (15u) +#define PRU_SUART_SERIALIZER_NONE (16u) + +#define PRU_SUART_UART1 (1u) +#define PRU_SUART_UART2 (2u) +#define PRU_SUART_UART3 (3u) +#define PRU_SUART_UART4 (4u) +#define PRU_SUART_UART5 (5u) +#define PRU_SUART_UART6 (6u) +#define PRU_SUART_UART7 (7u) +#define PRU_SUART_UART8 (8u) +#define PRU_SUART_UARTx_INVALID (9u) + +#define PRU_SUART_HALF_TX (1u) +#define PRU_SUART_HALF_RX (2u) +#define PRU_SUART_HALF_TX_DISABLED (4u) +#define PRU_SUART_HALF_RX_DISABLED (8u) + +#define PRU_SUART0_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART0_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART0_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART1_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART1_CONFIG_RX_SER (PRU_SUART_SERIALIZER_7) +#define PRU_SUART1_CONFIG_TX_SER (PRU_SUART_SERIALIZER_8) + +#define PRU_SUART2_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART2_CONFIG_RX_SER (PRU_SUART_SERIALIZER_9) +#define PRU_SUART2_CONFIG_TX_SER (PRU_SUART_SERIALIZER_10) + +#define PRU_SUART3_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART3_CONFIG_RX_SER (PRU_SUART_SERIALIZER_13) +#define PRU_SUART3_CONFIG_TX_SER (PRU_SUART_SERIALIZER_14) + +#define PRU_SUART4_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART4_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART4_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART5_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART5_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART5_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART6_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART6_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART6_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART7_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART7_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART7_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define SUART_NUM_OF_CHANNELS_PER_SUART 2 +#define SUART_NUM_OF_BYTES_PER_CHANNEL 16 + +#define PRU_TX_INTR 1 +#define PRU_RX_INTR 2 + +#define CHN_TXRX_STATUS_TIMEOUT BIT(6) +#define CHN_TXRX_STATUS_BI BIT(5) +#define CHN_TXRX_STATUS_FE BIT(4) +#define CHN_TXRX_STATUS_UNERR BIT(3) +#define CHN_TXRX_STATUS_OVRNERR BIT(3) +#define CHN_TXRX_STATUS_ERR BIT(2) +#define CHN_TXRX_STATUS_CMPLT BIT(1) +#define CHN_TXRX_STATUS_RDY BIT(0) + +#define CHN_TXRX_IE_MASK_TIMEOUT BIT(14) +#define CHN_TXRX_IE_MASK_BI BIT(13) +#define CHN_TXRX_IE_MASK_FE BIT(12) +#define CHN_TXRX_IE_MASK_CMPLT BIT(1) + +#define SUART_GBL_INTR_ERR_MASK BIT(9) +#define SUART_PRU_ID_MASK 0xFF + +#define SUART_FIFO_LEN 15 +#define SUART_8X_OVRSMPL 1 +#define SUART_16X_OVRSMPL 2 +#define SUART_TX_OVRSMPL 0 +#define SUART_DEFAULT_OVRSMPL SUART_8X_OVRSMPL + +#define SUART_DEFAULT_OVRSMPL_OFFSET 26 +#define SUART_CHN_OFFSET 31 +#define SERIALIZER_OFFSET 8 + +#if (SUART_DEFAULT_OVRSMPL == SUART_16X_OVRSMPL) +#define SUART_DEFAULT_BAUD 57600 +#else +#define SUART_DEFAULT_BAUD 115200 +#endif + +#define PRU_MODE_INVALID 0x0 +#define PRU_MODE_TX_ONLY 0x1 +#define PRU_MODE_RX_ONLY 0x2 +#define PRU_MODE_RX_TX_BOTH 0x3 + +#if (PRU_ACTIVE == BOTH_PRU) +#define PRU0_MODE PRU_MODE_RX_ONLY +#define PRU1_MODE PRU_MODE_TX_ONLY +#elif (PRU_ACTIVE == SINGLE_PRU) +#define PRU0_MODE PRU_MODE_RX_TX_BOTH +#define PRU1_MODE PRU_MODE_INVALID +#else +#define PRU0_MODE PRU_MODE_INVALID +#define PRU1_MODE PRU_MODE_INVALID +#endif + +#define MCASP_XBUF_BASE_ADDR (0x01d00200) +#define MCASP_RBUF_BASE_ADDR (0x01d00280) +#define MCASP_SRCTL_BASE_ADDR (0x01d00180) + +#define MCASP_SRCTL_TX_MODE (0x000D) +#define MCASP_SRCTL_RX_MODE (0x000E) + +/* Since only PRU0 can work as RX */ +#define RX_DEFAULT_DATA_DUMP_ADDR (0x00001FC) +#define PRU_NUM_OF_CHANNELS (16) + +/* MCASP */ + +#define OMAPL_MCASP_PFUNC_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PFUNC_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PFUNC_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PFUNC_AFSR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PFUNC_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PFUNC_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PFUNC_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PFUNC_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PFUNC_AFSX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PFUNC_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PFUNC_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PFUNC_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PFUNC_AMUTE_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PFUNC_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PFUNC_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PFUNC_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PFUNC_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PFUNC_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PFUNC_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PFUNC_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PFUNC_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PFUNC_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PFUNC_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PFUNC_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PFUNC_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PFUNC_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PFUNC_AXR9_RESETVAL (0x00000000u) +/* AXR9 Token */ +#define OMAPL_MCASP_PFUNC_AXR9_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR9_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PFUNC_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR8_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR8_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PFUNC_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PFUNC_AXR7_RESETVAL (0x00000000u) +/* AXR7 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR7_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR7_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PFUNC_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PFUNC_AXR6_RESETVAL (0x00000000u) +/* AXR6 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR6_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR6_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PFUNC_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PFUNC_AXR5_RESETVAL (0x00000000u) +/* AXR5 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR5_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR5_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PFUNC_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR4_RESETVAL (0x00000000u) +/* AXR4 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR4_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR4_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PFUNC_AXR3_RESETVAL (0x00000000u) +/* AXR3 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR3_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR3_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR2_RESETVAL (0x00000000u) +/* AXR2 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR2_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR2_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR1_RESETVAL (0x00000000u) +/* AXR1 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR1_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR1_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_RESETVAL (0x00000000u) +/* AXR0 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR0_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_PDIR_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PDIR_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PDIR_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PDIR_AFSR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PDIR_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PDIR_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PDIR_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PDIR_ACLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PDIR_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PDIR_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PDIR_AFSX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PDIR_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PDIR_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PDIR_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PDIR_ACLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PDIR_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PDIR_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PDIR_AMUTE_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AMUTE_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PDIR_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PDIR_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PDIR_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PDIR_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PDIR_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PDIR_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PDIR_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PDIR_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PDIR_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PDIR_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PDIR_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PDIR_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_OUTPUT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PDIR_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PDIR_AXR9_RESETVAL (0x00000000u) +/* AXR9 Tokens */ +#define OMAPL_MCASP_PDIR_AXR9_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR9_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PDIR_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PDIR_AXR8_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR8_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PDIR_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PDIR_AXR7_RESETVAL (0x00000000u) +/*----AXR7 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR7_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR7_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PDIR_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PDIR_AXR6_RESETVAL (0x00000000u) +/*----AXR6 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR6_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR6_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PDIR_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PDIR_AXR5_RESETVAL (0x00000000u) +/*----AXR5 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR5_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR5_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PDIR_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR4_RESETVAL (0x00000000u) +/*----AXR4 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR4_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR4_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PDIR_AXR3_RESETVAL (0x00000000u) +/*----AXR3 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR3_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR3_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR2_RESETVAL (0x00000000u) +/*----AXR2 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR2_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR2_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR1_RESETVAL (0x00000000u) +/*----AXR1 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR1_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR1_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_RESETVAL (0x00000000u) +/*----AXR0 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR0_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXP_MASK (0x00000080u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_SHIFT (0x00000007u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RESETVAL (0x00000000u) +/*----CLKXP Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RISINGEDGE (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_FALLINGEDGE (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_ASYNC_MASK (0x00000040u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SHIFT (0x00000006u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_RESETVAL (0x00000001u) +/*----ASYNC Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SYNC (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_ASYNC (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXM_MASK (0x00000020u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_SHIFT (0x00000005u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_RESETVAL (0x00000001u) +/*----CLKXM Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_MASK (0x0000001Fu) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_RESETVAL (0x00000060u) + +/* AHCLKXCTL */ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_MASK (0x00008000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_RESETVAL (0x00000001u) +/*----HCLKXM Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_MASK (0x00004000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_RESETVAL (0x00000000u) +/*----HCLKXP Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_NOTINVERTED (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_INVERTED (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_MASK (0x00000FFFu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_AHCLKXCTL_RESETVAL (0x00008000u) + +#define MCASP_SUART_GBLCTL (0X00000000) +#define MCASP_SUART_RGBLCTL (0X00000000) +#define MCASP_SUART_XGBLCTL (0X00000000) +#define MCASP_SUART_RMASK_8 (0x000000FF) +#define MCASP_SUART_RMASK_16 (0x0000FFFF) +#define MCASP_SUART_RFMT_8 (0x0000A038) +#define MCASP_SUART_RFMT_16 (0x0000A078) +#define MCASP_SUART_FSRM (0X00000002) +#define MCASP_SUART_CLKRM_CLKRP (0X000000A0) +#define MCASP_SUART_HCLKRP (0X00008000) +#define MCASP_SUART_RTDMS0 (0X00000001) +#define MCASP_SUART_RSYNCERR (0X00000002) +#define MCASP_SUART_RMAX_RPS_256 (0x00FF0008) +#define MCASP_SUART_XMASK_0_31 (0X0000FFFF) +#define MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0 (0x00002078) +#define MCASP_SUART_FSXM (0x00000002) +#define MCASP_SUART_CLKXM_ASYNC_CLKXP (0x000000E0) +#define MCASP_SUART_HCLKXM (0x00008000) +#define MCASP_SUART_XTDMS0 (0X00000001) +#define MCASP_SUART_XSYNCERR (0x00000002) +#define MCASP_SUART_XMAX_XPS_256 (0x00FF0008) +#define MCASP_SUART_SRCTL_DISMOD (0x0000000c) +#define MCASP_SUART_DIT_DISABLE (0X00000000) +#define MCASP_SUART_LOOPBACK_DISABLE (0x00000000) +#define MCASP_SUART_AMUTE_DISABLE (0X00000000) +#define MCASP_SUART_XSTAT (0x0000FFFF) +#define MCASP_SUART_RSTAT (0x0000FFFF) + +/* SUART REGS */ + +/* PRU0 DATA RAM base address */ +#define PRU0_DATARAM_OFFSET (0x0000u) +/* PRU1 DATA RAM base address */ +#define PRU1_DATARAM_OFFSET (0x2000u) + +/* PRU0 DATA RAM size */ +#define PRU0_DATARAM_SIZE (0x200u) +/* PRU1 DATA RAM size */ +#define PRU1_DATARAM_SIZE (0x200u) + +#define PRU_SUART_PRU0_CH0_OFFSET (0x0000) +#define PRU_SUART_PRU0_CH1_OFFSET (0x0010) +#define PRU_SUART_PRU0_CH2_OFFSET (0x0020) +#define PRU_SUART_PRU0_CH3_OFFSET (0x0030) +#define PRU_SUART_PRU0_CH4_OFFSET (0x0040) +#define PRU_SUART_PRU0_CH5_OFFSET (0x0050) +#define PRU_SUART_PRU0_CH6_OFFSET (0x0060) +#define PRU_SUART_PRU0_CH7_OFFSET (0x0070) +#define PRU_SUART_PRU0_IMR_OFFSET (0x0080) +/* Interrupt Mask Register */ +#define PRU_SUART_PRU0_ISR_OFFSET (0x0082) +/* Interrupt Status Register */ +#define PRU_SUART_PRU0_ID_ADDR (0x0084) +/* PRU ID Register */ +#define PRU_SUART_PRU0_RX_TX_MODE (0x0085) +#define PRU_SUART_PRU0_DELAY_OFFSET (0x0086) +#define PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET (0x0088) + +/* PRU 1 Macros */ +#define PRU_SUART_PRU1_CH0_OFFSET (0x2000) +#define PRU_SUART_PRU1_CH1_OFFSET (0x2010) +#define PRU_SUART_PRU1_CH2_OFFSET (0x2020) +#define PRU_SUART_PRU1_CH3_OFFSET (0x2030) +#define PRU_SUART_PRU1_CH4_OFFSET (0x2040) +#define PRU_SUART_PRU1_CH5_OFFSET (0x2050) +#define PRU_SUART_PRU1_CH6_OFFSET (0x2060) +#define PRU_SUART_PRU1_CH7_OFFSET (0x2070) +#define PRU_SUART_PRU1_IMR_OFFSET (0x2080) +#define PRU_SUART_PRU1_ISR_OFFSET (0x2082) +#define PRU_SUART_PRU1_ID_ADDR (0x2084) +#define PRU_SUART_PRU1_RX_TX_MODE (0x2085) +#define PRU_SUART_PRU1_DELAY_OFFSET (0x2086) +#define PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET (0x2088) + +/* SUART Channel Control Register bit descriptions */ +#define PRU_SUART_CH_CTRL_MODE_SHIFT 0x0000 +#define PRU_SUART_CH_CTRL_MODE_MASK 0x0003 +#define PRU_SUART_CH_CTRL_TX_MODE 0x0001 +#define PRU_SUART_CH_CTRL_RX_MODE 0x0002 + +/* Service Request */ +#define PRU_SUART_CH_CTRL_SREQ_SHIFT 0x0002 +#define PRU_SUART_CH_CTRL_SREQ_MASK 0x0004 +#define PRU_SUART_CH_CTRL_SREQ 0x0001 + +/* McASP Instance */ +#define PRU_SUART_CH_CTRL_MCASP_SHIFT 0x0003 +#define PRU_SUART_CH_CTRL_MCASP_MASK 0x0018 +#define PRU_SUART_CH_CTRL_SR_SHIFT 0x0008 +#define PRU_SUART_CH_CTRL_SR_MASK 0x0F00 + +/* SUART channel configuration1 register descriptions */ + +/* clock divisor - relative baud value */ +#define PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG1_DIVISOR_MASK 0x03FF +/* oversampling */ +#define PRU_SUART_CH_CONFIG1_OVS_SHIFT 0x000A +#define PRU_SUART_CH_CONFIG1_OVS_MASK 0x0C00 + +/* SUART channel configuration2 register descriptions */ +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK 0x000F + +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_DATALEN_SHIFT 0x0008 +#define PRU_SUART_CH_CONFIG2_DATALEN_MASK 0x0F00 + +/* SUART Channel STATUS Register*/ +#define PRU_SUART_CH_STATUS_EN_BIT_MASK 0x8000 + +/* SUART Channel register offsets */ +#define PRU_SUART_CH_CTRL_OFFSET 0x00 +#define PRU_SUART_CH_CONFIG1_OFFSET 0x02 +#define PRU_SUART_CH_CONFIG2_OFFSET 0x04 +#define PRU_SUART_CH_TXRXSTATUS_OFFSET 0x06 +#define PRU_SUART_CH_TXRXDATA_OFFSET 0x08 +#define PRU_SUART_CH_BYTESDONECNTR_OFFSET 0x0C + +/* SUART Event Numbers macros */ +#define PRU_SUART0_TX_EVT 34 +#define PRU_SUART0_RX_EVT 35 +#define PRU_SUART1_TX_EVT 36 +#define PRU_SUART1_RX_EVT 37 +#define PRU_SUART2_TX_EVT 38 +#define PRU_SUART2_RX_EVT 39 +#define PRU_SUART3_TX_EVT 40 +#define PRU_SUART3_RX_EVT 41 +#define PRU_SUART4_TX_EVT 42 +#define PRU_SUART4_RX_EVT 43 +#define PRU_SUART5_TX_EVT 44 +#define PRU_SUART5_RX_EVT 45 +#define PRU_SUART6_TX_EVT 46 +#define PRU_SUART6_RX_EVT 47 +#define PRU_SUART7_TX_EVT 48 +#define PRU_SUART7_RX_EVT 49 + +#define PRU_SUART0_TX_EVT_BIT BIT(2) +#define PRU_SUART0_RX_EVT_BIT BIT(3) +#define PRU_SUART1_TX_EVT_BIT BIT(4) +#define PRU_SUART1_RX_EVT_BIT BIT(5) +#define PRU_SUART2_TX_EVT_BIT BIT(6) +#define PRU_SUART2_RX_EVT_BIT BIT(7) +#define PRU_SUART3_TX_EVT_BIT BIT(8) +#define PRU_SUART3_RX_EVT_BIT BIT(9) +#define PRU_SUART4_TX_EVT_BIT BIT(10) +#define PRU_SUART4_RX_EVT_BIT BIT(11) +#define PRU_SUART5_TX_EVT_BIT BIT(12) +#define PRU_SUART5_RX_EVT_BIT BIT(13) +#define PRU_SUART6_TX_EVT_BIT BIT(14) +#define PRU_SUART6_RX_EVT_BIT BIT(15) +#define PRU_SUART7_TX_EVT_BIT BIT(16) +#define PRU_SUART7_RX_EVT_BIT BIT(17) + +/* Total number of baud rates supported */ +#define SUART_NUM_OF_BAUDS_SUPPORTED 13 + +#define MCASP_PDIR_VAL ( \ + OMAPL_MCASP_PDIR_AFSR_OUTPUT< + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include "pruss_suart.h" + +static u8 uart_statu_table[8]; +static struct pruss_suart_iomap suart_iomap; + +static u32 uart_rx[8] = {PRU_SUART0_CONFIG_RX_SER, PRU_SUART1_CONFIG_RX_SER, + PRU_SUART2_CONFIG_RX_SER, PRU_SUART3_CONFIG_RX_SER, + PRU_SUART4_CONFIG_RX_SER, PRU_SUART5_CONFIG_RX_SER, + PRU_SUART6_CONFIG_RX_SER, PRU_SUART7_CONFIG_RX_SER}; + +static u32 uart_tx[8] = {PRU_SUART0_CONFIG_TX_SER, PRU_SUART1_CONFIG_TX_SER, + PRU_SUART2_CONFIG_TX_SER, PRU_SUART3_CONFIG_TX_SER, + PRU_SUART4_CONFIG_TX_SER, PRU_SUART5_CONFIG_TX_SER, + PRU_SUART6_CONFIG_TX_SER, PRU_SUART7_CONFIG_TX_SER}; + +static u32 uart_config[8] = {PRU_SUART0_CONFIG_DUPLEX, PRU_SUART1_CONFIG_DUPLEX, + PRU_SUART2_CONFIG_DUPLEX, PRU_SUART3_CONFIG_DUPLEX, + PRU_SUART4_CONFIG_DUPLEX, PRU_SUART5_CONFIG_DUPLEX, + PRU_SUART6_CONFIG_DUPLEX, PRU_SUART7_CONFIG_DUPLEX}; + +static s32 pru_softuart_clr_rx_fifo(struct device *dev, + struct suart_handle *h_uart); +static s32 arm_to_pru_intr_init(struct device *dev); + +#if (PRU_ACTIVE == BOTH_PRU) +static void pru_set_ram_data(struct device *dev, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 datatowrite; + u32 i; + struct pru_suart_regs_ovly *pru_suart_regs = NULL; + u32 __iomem *p_sr_ctl_addr = (u32 __iomem *)(pruss_ioaddr-> + mcasp_io_addr + 0x180); + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* RX PRU - 0 Chanel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_RX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_RX_MODE, p_sr_ctl_addr + + uart_rx[i]); + } + /* + * RX is active by default, write the dummy received data at + * PRU RAM addr 0x1FC to avoid memory corruption. + */ + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 0); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x090 + (i * 0x020))); + datatowrite = (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + datatowrite); + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + datatowrite); + } + + /* PRU1 RAM BASE ADDR */ + pru_suart_regs = (struct pru_suart_regs_ovly *) PRU1_DATARAM_OFFSET; + + /* TX PRU - 1 */ + /* Channel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_TX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, 0xF, 8); + + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED) { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_TX_MODE, + p_sr_ctl_addr + uart_tx[i]); + } + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 1); + + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) + (PRU1_DATARAM_OFFSET + (0x0B0 + (i * 0x02C))); + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + datatowrite); + datatowrite = (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + datatowrite); + /* SUART1 TX formatted data base addr */ + datatowrite = (0x0090 + (i * 0x002C)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + datatowrite); + } +} +#else +static void pru_set_ram_data(struct device *dev, + struct pruss_suart_iomap *pruss_ioaddr) +{ + + struct pru_suart_regs_ovly *pru_suart_regs = + (struct pru_suart_regs_ovly *)pruss_ioaddr->pru_io_addr; + u32 i; + u32 *p_sr_ctl_addr = (u32 *)(pruss_ioaddr->mcasp_io_addr + 0x180); + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* Channel 0 context information is Tx */ + for (i = 0; i < 4; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_TX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED){ + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_TX_MODE, + p_sr_ctl_addr + uart_tx[i]); + } + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 1); + + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0B0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2))); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2))); + /* SUART1 TX formatted data base addr */ + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + (0x0090 + (i * 0x050))); + + /* Channel 1 is Rx context information */ + pru_suart_regs++; + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_RX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, 0xF, 8); + + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_RX_MODE, + p_sr_ctl_addr + uart_rx[i]); + } + /* + * RX is active by default, write the dummy received data + * at PRU RAM addr 0x1FC to avoid memory corruption + */ + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 0); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0C0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2))); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2))); + } +} +#endif + +static void pru_set_rx_tx_mode(struct device *dev, u32 pru_mode, u32 pru_num) +{ + u32 pru_offset; + + if (pru_num == PRUSS_NUM0) + pru_offset = PRU_SUART_PRU0_RX_TX_MODE; + else if (pru_num == PRUSS_NUM1) + pru_offset = PRU_SUART_PRU1_RX_TX_MODE; + else + return; + pruss_writeb(dev, pru_offset, (u8) pru_mode); +} + +static void pru_set_delay_count(struct device *dev, u32 pru_freq) +{ + u32 delay_cnt; + + if (pru_freq == PRU_CLK_228) + delay_cnt = 5; + else if (pru_freq == PRU_CLK_186) + delay_cnt = 5; + else + delay_cnt = 3; + + /* PRU 0 */ + pruss_writeb(dev, PRU_SUART_PRU0_DELAY_OFFSET, + (u8) delay_cnt); + + /* PRU 1 */ + pruss_writeb(dev, PRU_SUART_PRU1_DELAY_OFFSET, + (u8) delay_cnt); +} + +static s32 suart_set_pru_id(struct device *dev, u32 pru_no) +{ + u32 offset; + u8 reg_val = 0; + + if (PRUSS_NUM0 == pru_no) + offset = PRU_SUART_PRU0_ID_ADDR; + else if (PRUSS_NUM1 == pru_no) + offset = PRU_SUART_PRU1_ID_ADDR; + else + return -EINVAL; + + reg_val = pru_no; + pruss_writeb(dev, offset, reg_val); + + return 0; +} + +/* + * suart Initialization routine + */ +s32 pru_softuart_init(struct device *dev, + struct pruss_suart_initparams *init_params, + const u8 *pru_suart_emu_code, u32 fw_size, + u32 clk_rate_pruss, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 datatowrite[128] = {0}; + s16 status = 0; + s16 idx; + s16 retval; + u16 i; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) && + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) + return -EINVAL; + + suart_iomap.mcasp_io_addr = pruss_ioaddr->mcasp_io_addr; + suart_iomap.p_fifo_buff_phys_base = + pruss_ioaddr->p_fifo_buff_phys_base; + suart_iomap.p_fifo_buff_virt_base = + pruss_ioaddr->p_fifo_buff_virt_base; + /* Configure McASP0 */ + suart_mcasp_config(init_params->tx_baud_value, + init_params->rx_baud_value, + init_params->oversampling, pruss_ioaddr); + pruss_enable(dev, PRUSS_NUM0); + + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_enable(dev, PRUSS_NUM1); + + /* Reset PRU RAM */ + for (i = 0; i < (PRU0_DATARAM_SIZE / sizeof(int)); i++) + pruss_writel(dev, (PRU0_DATARAM_OFFSET + (i * sizeof(int))), + datatowrite[i]); + if (PRU1_MODE != PRU_MODE_INVALID) { + for (i = 0; i < (PRU1_DATARAM_SIZE / sizeof(int)); i++) + pruss_writel(dev, (PRU1_DATARAM_OFFSET + + (i * sizeof(int))), datatowrite[i]); + } + + pruss_load(dev, PRUSS_NUM0, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_load(dev, PRUSS_NUM1, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + + retval = arm_to_pru_intr_init(dev); + if (-1 == retval) + return status; + pru_set_delay_count(dev, clk_rate_pruss); + suart_set_pru_id(dev, PRUSS_NUM0); + if (PRU1_MODE != PRU_MODE_INVALID) + suart_set_pru_id(dev, PRUSS_NUM1); + + pru_set_rx_tx_mode(dev, PRU0_MODE, PRUSS_NUM0); + if (PRU1_MODE != PRU_MODE_INVALID) + pru_set_rx_tx_mode(dev, PRU1_MODE, PRUSS_NUM1); + + pru_set_ram_data(dev, pruss_ioaddr); + pruss_run(dev, PRUSS_NUM0); + + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_run(dev, PRUSS_NUM1); + + /* Initialize uart_statu_table */ + for (idx = 0; idx < 8; idx++) + uart_statu_table[idx] = ePRU_SUART_UART_FREE; + + return status; +} + +void pru_set_fifo_timeout(struct device *dev, s16 timeout) +{ + pruss_writew(dev, PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET, (u16) timeout); + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_writew(dev, PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET, + (u16) timeout); +} + +void pru_mcasp_deinit(void) +{ + suart_mcasp_reset(&suart_iomap); +} + +/* suart Instance open routine */ +s32 pru_softuart_open(struct suart_handle *h_suart) +{ + s16 status = 0; + u16 uart_num = h_suart->uart_num - 1; + + if (uart_statu_table[h_suart->uart_num - 1] == + ePRU_SUART_UART_IN_USE) { + return -EUSERS; + } else { + h_suart->uart_type = uart_config[uart_num]; + h_suart->uart_tx_channel = uart_tx[uart_num]; + h_suart->uart_rx_channel = uart_rx[uart_num]; + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + uart_statu_table[h_suart->uart_num - 1] = + ePRU_SUART_UART_IN_USE; + } + return status; +} + +/* suart instance close routine */ +s32 pru_softuart_close(struct suart_handle *h_uart) +{ + s16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } else { + uart_statu_table[h_uart->uart_num - 1] = + ePRU_SUART_UART_FREE; + /* Reset the Instance to Invalid */ + h_uart->uart_num = PRU_SUART_UARTx_INVALID; + h_uart->uart_status = ePRU_SUART_UART_FREE; + } + return status; +} + +static s32 search_chnum(u16 uart_num, u16 *ch_num, u32 *pru_offset, u16 mode) +{ + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + *ch_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (uart_num <= 4) { + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + *ch_num -= 8; + } + (mode == 2) ? ++*ch_num : *ch_num; + } else if (mode == 1) { + if (PRU0_MODE == PRU_MODE_TX_ONLY) + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + else if (PRU1_MODE == PRU_MODE_TX_ONLY) + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if (mode == 2) { + if (PRU0_MODE == PRU_MODE_RX_ONLY) + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + else if (PRU1_MODE == PRU_MODE_RX_ONLY) + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } + return 0; +} + +/* + * suart routine for setting relative baud rate + */ +s32 pru_softuart_setbaud(struct device *dev, struct suart_handle *h_uart, + u16 tx_clk_divisor, u16 rx_clk_divisor) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 regval = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* Set the clock divisor value s32o the McASP */ + if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0)) + return -EINVAL; + if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0)) + return -EINVAL; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + if (tx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writew(dev, offset, regval); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + regval = 0; + if (rx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writew(dev, offset, regval); + } + return status; +} + +/* + * suart routine for setting number of bits per character for a specific uart + */ +s32 pru_softuart_setdatabits(struct device *dev, struct suart_handle *h_uart, + u16 tx_data_bits, u16 rx_data_bits) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u32 reg_val; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * The supported data bits are 6,7,8,9,10,11,12 bits per character + */ + + if ((tx_data_bits < ePRU_SUART_DATA_BITS6) + || (tx_data_bits > ePRU_SUART_DATA_BITS12)) + return -EINVAL; + + if ((rx_data_bits < ePRU_SUART_DATA_BITS6) + || (rx_data_bits > ePRU_SUART_DATA_BITS12)) + return -EINVAL; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + if (tx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val); + reg_val &= ~(0xF); + reg_val |= tx_data_bits; + pruss_writeb(dev, offset, (u8) reg_val); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + if (rx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val); + reg_val &= ~(0xF); + reg_val |= rx_data_bits; + pruss_writeb(dev, offset, (u8) rx_data_bits); + } + + return status; +} + +/* + * suart routine to configure specific uart + */ +s32 pru_softuart_setconfig(struct device *dev, struct suart_handle *h_uart, + struct suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + if ((config_uart->tx_clk_divisor > 384) + || (config_uart->rx_clk_divisor > 384)) { + return -EINVAL; + } + if ((config_uart->tx_bits_per_char < 8) + || (config_uart->tx_bits_per_char > 14)) { + return -EINVAL; + } + if ((config_uart->rx_bits_per_char < 8) + || (config_uart->rx_bits_per_char > 14)) { + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + /* + * Configuring the Transmit part of the given UART + * Serializer has been as TX in mcasp config, by writing 1 in bits + * corresponding to tx serializer in PFUNC regsiter ie already set + * to GPIO mode PRU code will set then back to MCASP mode once TX + * request for that serializer is posted.It is required because at this + * pos32 Mcasp is accessed by both PRU and DSP have lower priority for + * Mcasp in comparison to PRU and DPS keeps on looping there only + * + * suart_mcasp_tx_serialzier_set + * (config_uart->tx_serializer, &suart_iomap); + */ + + /* Configuring TX serializer */ + if (config_uart->tx_serializer != PRU_SUART_SERIALIZER_NONE) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->tx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writew(dev, offset, reg_val); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->tx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writew(dev, offset, reg_val); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->tx_bits_per_char << + PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT); + pruss_writew(dev, offset, reg_val); + } + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + + /* Configuring the Transmit part of the given UART */ + if (config_uart->rx_serializer != PRU_SUART_SERIALIZER_NONE) { + /* Configuring RX serializer */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->rx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Configuring RX prescalar value and Oversampling */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->rx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT) | + (config_uart->oversampling << + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->rx_bits_per_char << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writew(dev, offset, reg_val); + } + return status; +} + +/* + * suart routine for getting the number of bytes transfered + */ +s32 pru_softuart_get_tx_data_len(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 read_value = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) &read_value); + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + return read_value; +} + +/* + * suart routine for getting the number of bytes received + */ +s32 pru_softuart_get_rx_data_len(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 read_value = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) &read_value); + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + return read_value; +} + +/* + * suart routine to get the configuration information from a specific uart + */ +s32 pru_softuart_getconfig(struct device *dev, + struct suart_handle *h_uart, + struct suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + s16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + /* Configuring the Transmit part of the given UART */ + /* Configuring TX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->tx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + /* Configuring TX prescalar value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->tx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + /* Configuring TX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->tx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + /* Configuring the Transmit part of the given UART */ + /* Configuring RX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->rx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + + /* Configuring RX prescalar value and oversampling */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->rx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + config_uart->oversampling = ((reg_val & + PRU_SUART_CH_CONFIG1_OVS_MASK) >> + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->rx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + return status; +} + +s32 pru_softuart_pending_tx_request(struct device *dev) +{ + u32 offset = 0; + u32 ISR_value = 0; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + return 0; + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&ISR_value); + if ((ISR_value & 0x1) == 0x1) + return -EINVAL; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&ISR_value); + if ((ISR_value & 0x2) == 0x2) + return -EINVAL; + } else { + return 0; + } + + return 0; +} + +/* + * suart data transmit routine + */ +s32 pru_softuart_write(struct device *dev, struct suart_handle *h_uart, + u32 *pt_tx_data_buf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) + pru_num = h_uart->uart_num; + else if (PRU0_MODE == PRU_MODE_TX_ONLY) + pru_num = 0; + else if (PRU1_MODE == PRU_MODE_TX_ONLY) + pru_num = 1; + else + return 0; + + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Writing the data pos32er to channel TX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writel(dev, offset, (u32) *pt_tx_data_buf); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_TX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart data receive routine + */ +s32 pru_softuart_read(struct device *dev, struct suart_handle *h_uart, + u32 *ptDataBuf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + pru_num = h_uart->uart_num; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_num = 0; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_num = 1; + } else { + return 0; + } + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Writing the data pos32er to channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writel(dev, offset, (u32) *ptDataBuf); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* enable the timeout s32errupt */ + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart routine to read the data from the RX FIFO + */ +s32 pru_softuart_read_data(struct device *dev, struct suart_handle *h_uart, + u8 *p_data_buffer, s32 max_len, + u32 *pdata_read) +{ + s16 ret_val = 0; + u8 *psrc_addr = NULL; + u32 data_read = 0; + u32 data_len = 0; + u32 char_len = 0; + u32 offset = 0; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + /* Get the data pos32er from channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_readb_multi(dev, offset, (u8 *) &psrc_addr, 4); + + /* Reading data length from SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) &data_len); + + /* read the character length */ + char_len = data_len & PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK; + char_len -= 2; /* remove the START & STOP bit */ + + data_len &= PRU_SUART_CH_CONFIG2_DATALEN_MASK; + data_len = data_len >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT; + data_len++; + + /* if the character length is greater than 8, then the size doubles */ + if (char_len > 8) + data_len *= 2; + + /* Check if the time-out had occured. If, yes, then we need to find the + * number of bytes read from PRU. Else, we need to + * read the requested bytes + */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + if (CHN_TXRX_STATUS_TIMEOUT == (status & CHN_TXRX_STATUS_TIMEOUT)) { + /* determine the number of bytes read s32o the FIFO */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; + pruss_readb(dev, offset, (u8 *) &data_read); + + /* if the character length is greater than 8, + then the size doubles */ + if (char_len > 8) + data_read *= 2; + +/* + * the data corresponding is loaded in second + * half during the timeout + */ + if (data_read > data_len) { + data_read -= data_len; + psrc_addr += data_len; + } + + pru_softuart_clr_rx_fifo(dev, h_uart); + } else { + data_read = data_len; +/* + * if the bit is set, the data is in the first + * half of the FIFO else the data is in the second half + */ + /* Determine the buffer index by reading FIFO_OddEven flag*/ + if (status & CHN_TXRX_STATUS_CMPLT) + psrc_addr += data_len; + } + + /* we should be copying only max len given by the application */ + if (data_read > max_len) + data_read = max_len; + +/* evaluate the virtual address of the FIFO address + * based on the physical addr + */ + psrc_addr = (u8 *)((u32) psrc_addr - + (u32) suart_iomap.p_fifo_buff_phys_base + + (u32) suart_iomap.p_fifo_buff_virt_base); + + /* Now we have both the data length and the source address. copy */ + for (offset = 0; offset < data_read; offset++) + *p_data_buffer++ = *psrc_addr++; + *pdata_read = data_read; + ret_val = 0; + + return ret_val; +} + +/* + * suart routine to disable the receive functionality. + * This routine stops the PRU from receiving on selected + * UART and also disables the McASP serializer corresponding + * to this UART Rx line. + */ +s32 pru_softuart_stop_receive(struct device *dev, struct suart_handle *h_uart) +{ + u16 ret_status = 0; + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 status; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + /* read the existing value of status flag */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + + /* we need to clear the busy bit corresponding to receive channel */ + status &= ~(CHN_TXRX_STATUS_RDY); + pruss_writeb(dev, offset, (u8) status); + + /* get the serizlizer number being used for this Rx channel */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) &status); + status &= PRU_SUART_CH_CTRL_SR_MASK; + status = status >> PRU_SUART_CH_CTRL_SR_SHIFT; + + /* we need to de-activate the serializer corresponding to this rx */ + ret_status = suart_asp_serializer_deactivate(status, &suart_iomap); + + return ret_status; +} + +/* + * suart routine to get the tx status for a specific uart + */ +s32 pru_softuart_get_tx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + return status; +} + +s32 pru_softuart_clr_tx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + status &= ~(0x2); + pruss_writeb(dev, offset, (u8) status); + return status; +} + +/* + * suart routine to get the rx status for a specific uart + */ +s32 pru_softuart_get_rx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + return status; +} + +static s32 pru_softuart_clr_rx_fifo(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val; + u16 uart_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + uart_num = h_uart->uart_num; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + if (PRU0_MODE == PRU_MODE_RX_ONLY) + uart_num = 0; + else if (PRU1_MODE == PRU_MODE_RX_ONLY) + uart_num = 1; + + /* Reset the number of bytes read into the FIFO */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= 0x00; + pruss_writew(dev, offset, reg_val); + + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << PRU_SUART_CH_CTRL_MODE_SHIFT) | + (PRU_SUART_CH_CTRL_SREQ << PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writew(dev, offset, reg_val); + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, uart_num); + + return status; +} + +s32 pru_softuart_clr_rx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + status &= ~(0x3C); + pruss_writeb(dev, offset, (u8) status); + return status; +} + +/* + * suart_s32r_status_read: Gets the Global Interrupt status register + * for the specified SUART. + * uart_num < 1 to 6 > + * txrx_flag < Indicates TX or RX s32errupt for the uart > + */ +s32 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, u16 *txrx_flag) +{ + u32 intc_offset; + u32 ch_num = 0xFF; + u32 reg_val = 0; + u32 reg_val2 = 0; + u32 ISR_value = 0; + u32 ack_reg_val = 0; + u32 stat_inx_clr_regoffset = 0; + + /* initialize the status & Flag to known value */ + *txrx_flag = 0; + + stat_inx_clr_regoffset = (u32) (PRUSS_INTC_STATIDXCLR & 0xFFFF); + + /* Read PRU Interrupt Status Register from PRU */ + intc_offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* Check if the interrupt occured for Tx */ + ch_num = uart_num * 2 - 2; + reg_val2 = PRU_SUART0_TX_EVT_BIT << ((uart_num - 1) * 2); + if (ISR_value & reg_val2) { + /* interupt occured for TX */ + *txrx_flag |= PRU_TX_INTR; + /* acknowledge the RX interrupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + } + + /* Check if the interrupt occured for Rx */ + reg_val2 = PRU_SUART0_RX_EVT_BIT << ((uart_num - 1) * 2); + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); + if (ISR_value & reg_val2) { + /* interupt occured for RX */ + *txrx_flag |= PRU_RX_INTR; + ch_num += 1; + + /* acknowledge the RX interrupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + } + } else { + ch_num = uart_num - 1; + if ((ISR_value & 0x03FC) != 0) { + reg_val2 = 1 << (uart_num + 1); + if (ISR_value & reg_val2) { + /* acknowledge the s32errupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + *txrx_flag |= PRU_RX_INTR; + } + } + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); + if (ISR_value & 0x3FC00) { + reg_val2 = 1 << (uart_num + 9); + if (ISR_value & reg_val2) { + /* acknowledge the s32errupt */ + ack_reg_val = ch_num + PRU_SUART4_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + *txrx_flag |= PRU_TX_INTR; + } + } + } + return reg_val; +} + +s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, u32 txrxmode) +{ + u32 offset; + u16 txrx_flag = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (uart_num <= 4) { + /* PRU0 */ + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else { + /* PRU1 */ + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + } else { + return 0; + } + + pruss_readb(dev, offset, (u8 *) &txrx_flag); + txrx_flag &= ~(0x2); + pruss_writeb(dev, offset, (u8) txrx_flag); + + return 0; +} + +s32 suart_arm_to_pru_intr(struct device *dev, u16 uart_num) +{ + u32 value; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + if ((uart_num > 0) && (uart_num <= 4)) + value = 0x20; /* PRU0 SYS_EVT32 */ + else if ((uart_num > 4) && (uart_num <= 8)) + value = 0x21; /* PRU0 SYS_EVT33 */ + else + return -EINVAL; + } + if ((PRU0_MODE == PRU_MODE_RX_ONLY) + || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + if (uart_num == PRUSS_NUM0) + value = 0x20; /* PRU0 SYS_EVT32 */ + else if (uart_num == PRUSS_NUM1) + value = 0x21; /* PRU0 SYS_EVT33 */ + else + return -EINVAL; + } + return pruss_writel(dev, PRUSS_INTC_STATIDXSET, value); +} + +static s32 arm_to_pru_intr_init(struct device *dev) +{ + u32 value; + u32 int_offset; + + /* Clear all the host interrupts */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXCLR, int_offset); + + /* Enable the global s32errupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); + + /* Enable the Host interrupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_rmwl(dev, (u32) (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF), + 0, int_offset); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP0 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP0_CHAN); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP1 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP1_CHAN); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP2 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP2_CHAN); + + /* MAP Channel 0 to SYS_EVT31 */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP7 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP7_SYS_EVT31); + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* Sets the channels for the system interrupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_FULL); + } + if ((PRU0_MODE == PRU_MODE_RX_ONLY) + || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + + /* Sets the channels for the system interrupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_HALF); + } + + /* Clear required set of system events + * and enable them using indexed register + */ + for (int_offset = 0; int_offset < 18; int_offset++) { + value = 32 + int_offset; + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, value); + } + + /* enable only the HOST to PRU interrupts and let the PRU to Host events + * enabled by the separate API on demand basis. + */ + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 31); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 32); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 33); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 50); + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); + + /* Enable the Host interrupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXSET, int_offset); + + return 0; +} + +s32 suart_pru_to_host_intr_enable(struct device *dev, u16 uart_num, + u32 txrxmode, s32 flag) +{ + u32 chn_num; + u32 value; + s16 retval = 0; + + if (uart_num > 8) + return -EINVAL; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + chn_num = (uart_num * 2) - 2; + if (2 == txrxmode) /* Rx mode */ + chn_num++; + value = 34 + chn_num; + } else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_RX_ONLY)) + value = 34 + chn_num; + else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_RX_ONLY)) + value = 42 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_TX_ONLY)) + value = 34 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_TX_ONLY)) + value = 42 + chn_num; + else + return -EINVAL; + + retval = flag ? pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, value) : + pruss_idx_writel(dev, PRUSS_INTC_ENIDXCLR, value); + return retval; +} + +s32 suart_intr_setmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u32 offset; + u32 pru_offset; + u32 regval = 0; + u32 chn_num = uart_num - 1; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + chn_num -= 8; + } else { + return -EINVAL; + } + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + offset = PRU_SUART_PRU1_IMR_OFFSET; + } else + return 0; + + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) + pruss_rmww(dev, offset, regval, regval); + + if ((rmask & SUART_GBL_INTR_ERR_MASK) == + SUART_GBL_INTR_ERR_MASK) { + regval = SUART_GBL_INTR_ERR_MASK; + pruss_rmww(dev, offset, regval, regval); + } + + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + /* Framing Error Interrupt Masked */ + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_FE); + regval |= CHN_TXRX_IE_MASK_FE; + pruss_writew(dev, offset, (u16) regval); + } + + /* Break Indicator Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_BI); + regval |= CHN_TXRX_IE_MASK_BI; + pruss_writew(dev, offset, (u16) regval); + } + + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + regval |= CHN_TXRX_IE_MASK_TIMEOUT; + pruss_writew(dev, offset, (u16) regval); + } + return 0; +} + +s32 suart_intr_clrmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u32 offset; + u32 pru_offset; + u16 regval = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else + return -EINVAL; + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + offset = PRU_SUART_PRU1_IMR_OFFSET; + } else + return 0; + + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) + pruss_rmww(dev, offset, regval, 0); + + if ((rmask & SUART_GBL_INTR_ERR_MASK) == SUART_GBL_INTR_ERR_MASK) + pruss_rmww(dev, offset, SUART_GBL_INTR_ERR_MASK, 0); + + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + + /* Framing Error Interrupt Masked */ + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_FE); + pruss_writew(dev, offset, regval); + } + + /* Break Indicator Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_BI); + pruss_writew(dev, offset, regval); + } + + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + pruss_writew(dev, offset, regval); + } + return 0; +} + +s32 suart_intr_getmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u16 chn_num; + u32 offset; + u16 txrx_flag; + u16 regval = 1; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else + return -EINVAL; + + if (2 == txrxmode) + chn_num++; + + } else if (PRU0_MODE == txrxmode) + offset = PRU_SUART_PRU0_IMR_OFFSET; + else if (PRU1_MODE == txrxmode) + offset = PRU_SUART_PRU1_IMR_OFFSET; + else + return 0; + + regval = regval << chn_num; + pruss_readw(dev, offset, (u16 *) &txrx_flag); + txrx_flag &= regval; + + if ((rmask && (txrx_flag == regval)) || (!rmask && !txrx_flag)) + return 1; + + return 0; +} diff --git a/drivers/tty/serial/pruss_suart_utils.c b/drivers/tty/serial/pruss_suart_utils.c new file mode 100644 index 0000000..5ee340e --- /dev/null +++ b/drivers/tty/serial/pruss_suart_utils.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + + +#include +#include "pruss_suart.h" + +#define SUART_TRX_DIV_CONF_SZ 4 + +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + struct pruss_suart_iomap *pruss_ioaddr); +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr); + +/* + * Lookup table for TX baud rate + * The divisor value is calculated using the formula + * + * ACLKX = (AUXCLK)/(CLKXDIV * HCLKXDIV) + * + * Where + * CLKXDIV takes values from 1-32 + * HCLKXDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +static u32 lt_tx_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { + /*BaudRate, Divisor, CLKXDIV,HCLKXDIV */ + {300, 80000, 24, 3200}, + {600, 40000, 15, 2500}, + {1800, 13333, 10, 1212}, + {2400, 10000, 4, 2000}, + {4800, 5000, 1, 2500}, + {7200, 3333, 0, 3333}, + {9600, 2500, 0, 2500}, + {14400, 1666, 0, 1666}, + {19200, 1250, 0, 1250}, + {38400, 625, 0, 625}, + {57600, 416, 0, 416}, + {115200, 208, 0, 208}, + {230400, 104, 0, 104} +}; + +/* + * Lookup table for RX baud rate for 8 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +static u32 lt_rx_8x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/* BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 10000, 4, 2000}, + {600, 5000, 1, 2500}, + {1800, 1667, 0, 1667}, + {2400, 1250, 0, 1250}, + {7200, 417, 0, 417}, + {4800, 625, 0, 625}, + {9600, 312, 0, 312}, + {14400, 208, 0, 208}, + {19200, 156, 0, 156}, + {38400, 78, 0, 78}, + {57600, 52, 0, 52}, + {115200, 26, 0, 26}, + {230400, 13, 0, 13} +}; + +/* + * Lookup table for RX baud rate for 16 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +static u32 lt_rx_16x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/*BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 5000, 1, 2500}, + {600, 2500, 0, 2500}, + {1800, 833, 0, 833}, + {2400, 625, 0, 625}, + {4800, 312, 0, 312}, + {7200, 208, 0, 208}, + {9600, 156, 0, 156}, + {14400, 104, 0, 104}, + {19200, 78, 0, 78}, + {38400, 39, 0, 39}, + {57600, 26, 0, 26}, + {115200, 13, 0, 13}, + {230400, 6, 0, 6} +}; + +/* + * McASP configuration routine + */ + +void suart_mcasp_reset(struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + /* reset mcasp. */ + iowrite32(MCASP_SUART_GBLCTL, &mcasp0_regs->gblctl); + iowrite32(MCASP_SUART_RGBLCTL, &mcasp0_regs->rgblctl); + iowrite32(MCASP_SUART_XGBLCTL, &mcasp0_regs->xgblctl); + iowrite32(MCASP_SUART_XSTAT, &mcasp0_regs->xstat); + iowrite32(MCASP_SUART_RSTAT, &mcasp0_regs->rstat); +} + +void suart_mcasp_config(u32 tx_baud_value, + u32 rx_baud_value, + u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* reset mcasp */ + iowrite32(MCASP_SUART_GBLCTL, &mcasp0_regs->gblctl); + iowrite32(MCASP_SUART_RGBLCTL, &mcasp0_regs->rgblctl); + iowrite32(MCASP_SUART_XGBLCTL, &mcasp0_regs->xgblctl); + + /* configure receive registers */ + if ((SUART_8X_OVRSMPL == oversampling) || (0 == oversampling)) { + iowrite32(MCASP_SUART_RMASK_8, &mcasp0_regs->rmask); + iowrite32(MCASP_SUART_RFMT_8, &mcasp0_regs->rfmt); + } + if (SUART_16X_OVRSMPL == oversampling) { + iowrite32(MCASP_SUART_RMASK_16, &mcasp0_regs->rmask); + iowrite32(MCASP_SUART_RFMT_16, &mcasp0_regs->rfmt); + + } + + iowrite32(MCASP_SUART_FSRM, &mcasp0_regs->afsrctl); + iowrite32(MCASP_SUART_CLKRM_CLKRP, &mcasp0_regs->aclkrctl); + iowrite32(MCASP_SUART_HCLKRP, &mcasp0_regs->ahclkrctl); + suart_mcasp_rx_baud_set(rx_baud_value, oversampling, pruss_ioaddr); + iowrite32(MCASP_SUART_RTDMS0, &mcasp0_regs->rtdm); + iowrite32(MCASP_SUART_RSYNCERR, &mcasp0_regs->rintctl); + iowrite32(MCASP_SUART_RMAX_RPS_256, &mcasp0_regs->rclkchk); + + /* configure transmit registers. */ + iowrite32(MCASP_SUART_XMASK_0_31, &mcasp0_regs->xmask); + iowrite32(MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0, &mcasp0_regs->xfmt); + iowrite32(MCASP_SUART_FSXM, &mcasp0_regs->afsxctl); + iowrite32(MCASP_SUART_CLKXM_ASYNC_CLKXP, &mcasp0_regs->aclkxctl); + iowrite32(MCASP_SUART_HCLKXM, &mcasp0_regs->ahclkxctl); + + suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + iowrite32(MCASP_SUART_XTDMS0, &mcasp0_regs->xtdm); + iowrite32(MCASP_SUART_XSYNCERR, &mcasp0_regs->xintctl); + iowrite32(MCASP_SUART_XMAX_XPS_256, &mcasp0_regs->xclkchk); + + /* Serializer as a transmitter */ + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl0); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl1); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl2); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl3); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl4); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl5); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl6); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl7); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl8); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl9); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl10); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl11); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl12); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl13); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl14); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl15); + + /* Configure all AXR[n] as McASP pins */ + + /* + * Setting all TX MCASP AXR[n] Pin mapped to Even Serializer number + * (0,2,4,6,8,10,12,14) to GPIO Mode by default. During setting the + * serializer to TX mode in PRU assembly code, the MCASP AXR[n] Pin + * would get configured to MCASP mode of operation, + * before Actual Data Transfer + */ + + /* Setting all TX Pin to GPIO Mode by default */ + temp_reg = (OMAPL_MCASP_PFUNC_RESETVAL) | + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER); + iowrite32(temp_reg, &mcasp0_regs->pfunc); + + iowrite32(0xFFF, &mcasp0_regs->pdout); + + /* config pin function and direction */ + iowrite32(0x00000000, &mcasp0_regs->pdir); + temp_reg = + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER) | + (MCASP_PDIR_VAL); + iowrite32(temp_reg, &mcasp0_regs->pdir); + + iowrite32(MCASP_SUART_DIT_DISABLE, &mcasp0_regs->ditctl); + iowrite32(MCASP_SUART_LOOPBACK_DISABLE, &mcasp0_regs->dlbctl); + iowrite32(MCASP_SUART_AMUTE_DISABLE, &mcasp0_regs->amute); + + iowrite32(MCASP_SUART_XSTAT, &mcasp0_regs->xstat); + iowrite32(MCASP_SUART_RSTAT, &mcasp0_regs->rstat); +} + +void suart_mcasp_tx_serialzier_set(u32 serializer_num, + struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + temp_reg = ioread32(&mcasp0_regs->pfunc); + temp_reg |= (0x1 << serializer_num); + iowrite32(temp_reg, &mcasp0_regs->pfunc); +} + +/* + * mcasp TX buard rate setting routine + */ +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val; + u32 loop_cnt; + s16 status = 0; + s16 found_val = false; + + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* Search the supported baud rate in the table */ + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (tx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + found_val = true; + break; + } + } + if (found_val == true) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkxctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkxctl); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = ioread32(&mcasp0_regs->ahclkxctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkxctl); + } else { + return -EINVAL ; + } + return status; +} + +/* + * mcasp RX buard rate setting routine + */ +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, + u32 oversampling, struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val = 0; + u32 loop_cnt = 0; + s16 status = 0; + u32 temp_reg = 0; + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + + switch (oversampling) { + case SUART_8X_OVRSMPL: + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_8x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_8x_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); + + clk_div_val = + lt_rx_8x_baud_rate[loop_cnt][3] - 1; + + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); + break; + } + } + status = -EINVAL; + break; + case SUART_16X_OVRSMPL: + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_16x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][3]; + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); + break; + } + } + status = -EINVAL; + break; + case SUART_TX_OVRSMPL: + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); + break; + } + } + status = -EINVAL; + break; + default: + status = -EINVAL; + break; + } + + return status; +} + +/* + * mcasp buard rate setting routine + */ +s16 suart_asp_baud_set(u32 tx_baud_value, u32 rx_baud_value, u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = 0; + + status = suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + status = suart_mcasp_rx_baud_set(rx_baud_value, oversampling, + pruss_ioaddr); + + return status; +} + +/* + * mcasp deactivate the selected serializer + */ +s16 suart_asp_serializer_deactivate(u16 sr_num, + struct pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = 0; + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + if (sr_num > 15) + status = -EINVAL; + else + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl0); + + return status; +} diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 758c5b0..eae37fe 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -202,6 +202,8 @@ /* VIA VT8500 SoC */ #define PORT_VT8500 97 +#define PORT_DA8XX_PRU_SUART 98 + #ifdef __KERNEL__ #include -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:27 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:27 +0530 Subject: [PATCH v4 09/11] mfd: pruss CAN private data. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-10-git-send-email-subhasish@mistralsolutions.com> This patch adds the PRUSS CAN data. Signed-off-by: Subhasish Ghosh --- include/linux/mfd/pruss.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/include/linux/mfd/pruss.h b/include/linux/mfd/pruss.h index c5e2af2..3b46156 100644 --- a/include/linux/mfd/pruss.h +++ b/include/linux/mfd/pruss.h @@ -92,6 +92,11 @@ struct da850_evm_pruss_suart_data { int (*setup)(void); }; +struct da850_evm_pruss_can_data { + u32 version; + int (*setup)(void); +}; + s32 pruss_enable(struct device *dev, u8 pruss_num); s32 pruss_load(struct device *dev, u8 pruss_num, -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:28 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:28 +0530 Subject: [PATCH v4 10/11] da850: pruss CAN platform specific additions. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-11-git-send-email-subhasish@mistralsolutions.com> This patch adds the necessary pins for the pruss CAN. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/da850.c | 5 +++++ arch/arm/mach-davinci/include/mach/mux.h | 5 +++++ 2 files changed, 10 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index a7cf2d0..84a5e64 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -581,7 +581,12 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, EMA_CLK, 6, 0, 15, 1, false) MUX_CFG(DA850, EMA_WAIT_1, 6, 24, 15, 1, false) MUX_CFG(DA850, NEMA_CS_2, 7, 0, 15, 1, false) + /* PRU functions for soft CAN */ + MUX_CFG(DA850, PRUSS_PRU0_R31_0, 7, 28, 15, 0, false) + MUX_CFG(DA850, PRUSS_PRU1_R30_15, 12, 0, 15, 4, false) + MUX_CFG(DA850, PRUSS_PRU1_R31_18, 11, 20, 15, 0, false) /* GPIO function */ + MUX_CFG(DA850, GPIO2_0, 6, 28, 15, 8, false) MUX_CFG(DA850, GPIO2_4, 6, 12, 15, 8, false) MUX_CFG(DA850, GPIO2_6, 6, 4, 15, 8, false) MUX_CFG(DA850, GPIO2_8, 5, 28, 15, 8, false) diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index 5d4e0fe..85b3a0d 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -906,8 +906,13 @@ enum davinci_da850_index { DA850_EMA_CLK, DA850_EMA_WAIT_1, DA850_NEMA_CS_2, + /* PRU I/O */ + DA850_PRUSS_PRU0_R31_0, + DA850_PRUSS_PRU1_R30_15, + DA850_PRUSS_PRU1_R31_18, /* GPIO function */ + DA850_GPIO2_0, DA850_GPIO2_4, DA850_GPIO2_6, DA850_GPIO2_8, -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:08:29 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:38:29 +0530 Subject: [PATCH v4 11/11] da850: pruss CAN board specific additions. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-12-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss CAN pinmux and registers the device with the pruss mfd driver. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/board-da850-evm.c | 46 +++++++++++++++++++++++++++++++ 1 files changed, 46 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index e7fdf31..e1ff18c 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -48,6 +48,7 @@ #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) +#define DA850_PRUSS_CAN_TRX_PIN GPIO_TO_PIN(2, 0) #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) @@ -1117,6 +1118,43 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +static const short da850_evm_pruss_can_pins[] = { + DA850_PRUSS_PRU0_R31_0, DA850_PRUSS_PRU1_R30_15, + DA850_PRUSS_PRU1_R31_18, DA850_GPIO2_0, + -1 +}; + +static int __init da850_evm_pruss_can_setup(void) +{ + int ret, val = 0; + void __iomem *cfg_chip3_reg; + + ret = davinci_cfg_reg_list(da850_evm_pruss_can_pins); + if (ret) + pr_warning("%s: da850_evm_pruss_can_pins mux setup " + "failed:%d\n", __func__, ret); + cfg_chip3_reg = DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG); + val = __raw_readl(cfg_chip3_reg); + val |= BIT(3); + __raw_writel(val, cfg_chip3_reg); + + /* value = 0 to enable the CAN transceiver */ + ret = gpio_request_one(DA850_PRUSS_CAN_TRX_PIN, + GPIOF_OUT_INIT_LOW, "pruss_can_en"); + if (ret) { + pr_warning("Cannot setup GPIO %d\n", DA850_PRUSS_CAN_TRX_PIN); + gpio_free(DA850_PRUSS_CAN_TRX_PIN); + } + + return ret; +} + +static struct da850_evm_pruss_can_data can_data = { + .version = 1, + .setup = da850_evm_pruss_can_setup, +}; + + static const short da850_evm_pruss_suart_pins[] = { DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, @@ -1161,6 +1199,14 @@ static struct mfd_cell cell[] = { .resources = da850_evm_pruss_suart_resource, }, { + .id = 1, + .name = "da8xx_pruss_can", + .platform_data = &can_data, + .data_size = sizeof(can_data), + .num_resources = 0, + .resources = NULL, + }, + { .name = NULL, }, }; -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:11:06 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:41:06 +0530 Subject: [PATCH v4 0/1] pruss CAN driver. Message-ID: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> This patch adds a SocketCAN complaint CAN Network driver implemented on the Programmable Real Time Unit (PRUSS) available on da8xx processors from Texas Instruments. It utilizes both available PRUs on da8xx for concurrent Rx/Tx processing and load sharing. PRU0 is used as Rx and PRU1 as Tx. This controller also supports ID filtering at the device level. Eight different ID can be programmed into the device through the sysfs interface exported by the driver. version 4: ========= * added NAPI * added sysfs entry for programming mailbox IDs * added bittiming interface from the kernel * removed unused function * improved coding style * removed unwanted files * merged required files into the driver * code cleanup as per review comments NOTE: The versioning is being maintained in sequence with the complete set of patches submitted for the PRUSS. Subhasish Ghosh (1): can: add pruss CAN driver. drivers/net/can/Kconfig | 7 + drivers/net/can/Makefile | 1 + drivers/net/can/pruss_can.c | 1074 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1082 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/pruss_can.c -- 1.7.2.3 From subhasish at mistralsolutions.com Fri Apr 22 07:11:07 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 22 Apr 2011 17:41:07 +0530 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474267-6344-2-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the CAN device emulated on PRUSS. Signed-off-by: Subhasish Ghosh --- drivers/net/can/Kconfig | 7 + drivers/net/can/Makefile | 1 + drivers/net/can/pruss_can.c | 1074 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1082 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/pruss_can.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 5dec456..4682a4f 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -111,6 +111,13 @@ config PCH_CAN embedded processor. This driver can access CAN bus. +config CAN_TI_DA8XX_PRU + depends on CAN_DEV && ARCH_DAVINCI && ARCH_DAVINCI_DA850 + tristate "PRU based CAN emulation for DA8XX" + ---help--- + Enable this to emulate a CAN controller on the PRU of DA8XX. + If not sure, mark N + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 53c82a7..d0b7cbd 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_AT91) += at91_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o +obj-$(CONFIG_CAN_TI_DA8XX_PRU) += pruss_can.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o diff --git a/drivers/net/can/pruss_can.c b/drivers/net/can/pruss_can.c new file mode 100644 index 0000000..7702509 --- /dev/null +++ b/drivers/net/can/pruss_can.c @@ -0,0 +1,1074 @@ +/* + * TI DA8XX PRU CAN Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU CAN Emulation and the + * specs for the same is available at + * + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed as is WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRUSS_CAN_RX_PRU_0 PRUSS_NUM0 +#define PRUSS_CAN_TX_PRU_1 PRUSS_NUM1 +#define PRUSS_CAN_TX_SYS_EVT 34 +#define PRUSS_CAN_RX_SYS_EVT 33 +#define PRUSS_CAN_ARM2DSP_SYS_EVT 32 +#define PRUSS_CAN_DELAY_LOOP_LENGTH 2 +#define PRUSS_CAN_TIMER_SETUP_DELAY 14 +#define PRUSS_CAN_GPIO_SETUP_DELAY 150 +#define PRUSS_CAN_TX_GBL_STAT_REG (PRUSS_PRU1_BASE_ADDRESS + 0x04) +#define PRUSS_CAN_TX_INTR_MASK_REG (PRUSS_PRU1_BASE_ADDRESS + 0x08) +#define PRUSS_CAN_TX_INTR_STAT_REG (PRUSS_PRU1_BASE_ADDRESS + 0x0C) +#define PRUSS_CAN_TX_MBOX0_STAT_REG (PRUSS_PRU1_BASE_ADDRESS + 0x10) +#define PRUSS_CAN_TX_ERR_CNTR_REG (PRUSS_PRU1_BASE_ADDRESS + 0x30) +#define PRUSS_CAN_TX_INT_STAT 0x2 +#define PRUSS_CAN_MBOX_OFFSET 0x40 +#define PRUSS_CAN_MBOX_SIZE 0x10 +#define PRUSS_CAN_MBOX_STAT_OFFSET 0x10 +#define PRUSS_CAN_MBOX_STAT_SIZE 0x04 +#define PRUSS_CAN_GBL_STAT_REG_VAL 0x00000040 +#define PRUSS_CAN_INTR_MASK_REG_VAL 0x00004000 +#define PRUSS_CAN_TIMING_VAL_TX (PRUSS_PRU1_BASE_ADDRESS + 0xC0) +#define PRUSS_CAN_TIMING_SETUP (PRUSS_PRU1_BASE_ADDRESS + 0xC4) +#define PRUSS_CAN_RX_GBL_STAT_REG (PRUSS_PRU0_BASE_ADDRESS + 0x04) +#define PRUSS_CAN_RX_INTR_MASK_REG (PRUSS_PRU0_BASE_ADDRESS + 0x08) +#define PRUSS_CAN_RX_INTR_STAT_REG (PRUSS_PRU0_BASE_ADDRESS + 0x0C) +#define PRUSS_CAN_RX_ERR_CNTR_REG (PRUSS_PRU0_BASE_ADDRESS + 0x34) +#define PRUSS_CAN_TIMING_VAL_RX (PRUSS_PRU0_BASE_ADDRESS + 0xD0) +#define PRUSS_CAN_BIT_TIMING_VAL_RX (PRUSS_PRU0_BASE_ADDRESS + 0xD4) +#define PRUSS_CAN_ID_MAP (PRUSS_PRU0_BASE_ADDRESS + 0xF0) +#define PRUSS_CAN_ERROR_ACTIVE 128 +#define PRUSS_CAN_GBL_STAT_REG_MASK 0x1F +#define PRUSS_CAN_GBL_STAT_REG_SET_TX 0x80 +#define PRUSS_CAN_GBL_STAT_REG_SET_RX 0x40 +#define PRUSS_CAN_GBL_STAT_REG_STOP_TX 0x7F +#define PRUSS_CAN_GBL_STAT_REG_STOP_RX 0xBF +#define PRUSS_CAN_SET_TX_REQ 0x00000080 +#define PRUSS_CAN_STD_FRAME_MASK 18 +#define PRUSS_CAN_START 1 +#define PRUSS_CAN_MB_MIN 0 +#define PRUSS_CAN_MB_MAX 7 +#define PRUSS_CAN_MID_IDE BIT(29) +#define PRUSS_CAN_ISR_BIT_CCI BIT(15) +#define PRUSS_CAN_ISR_BIT_ESI BIT(14) +#define PRUSS_CAN_ISR_BIT_SRDI BIT(13) +#define PRUSS_CAN_ISR_BIT_RRI BIT(8) +#define PRUSS_CAN_GSR_BIT_EPM BIT(4) +#define PRUSS_CAN_GSR_BIT_BFM BIT(3) +#define PRUSS_CAN_RTR_BUFF_NUM 8 +#define PRUSS_DEF_NAPI_WEIGHT 8 +#define PRUSS_CAN_DRV_NAME "da8xx_pruss_can" +#define PRUSS_CAN_DRV_DESC "TI PRU CAN Controller Driver v1.0" + +enum can_tx_dir { + TRANSMIT = 1, + RECEIVE +}; + +struct can_mbox { + canid_t can_id; + u8 data[8]; + u16 datalength; + u16 crc; +}; + +struct can_emu_cntx { + enum can_tx_dir txdir; + struct can_mbox mbox; + u32 mboxno; + u8 pruno; + u32 gbl_stat; + u32 intr_stat; + u32 mbox_stat; +}; + +struct can_emu_priv { + struct can_priv can; + struct napi_struct napi; + struct net_device *ndev; + struct device *dev; + struct clk *clk_timer; + struct can_emu_cntx can_tx_cntx; + struct can_emu_cntx can_rx_cntx; + unsigned int clk_freq_pru; + unsigned int trx_irq; + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_next; + unsigned int mbx_id[8]; +}; + +static struct can_bittiming_const pru_can_bittiming_const = { + .name = PRUSS_CAN_DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +static int pru_can_mbox_write(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + u32 offset = 0; + + offset = PRUSS_PRU1_BASE_ADDRESS + PRUSS_CAN_MBOX_OFFSET + + (PRUSS_CAN_MBOX_SIZE * emu_cntx->mboxno); + + pruss_writel_multi(dev, offset, (u32 *) &(emu_cntx->mbox), 4); + + return 0; +} + +static int pru_can_mbox_read(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + u32 offset = 0; + + offset = PRUSS_PRU0_BASE_ADDRESS + PRUSS_CAN_MBOX_OFFSET + + (PRUSS_CAN_MBOX_SIZE * emu_cntx->mboxno); + + pruss_readl_multi(dev, offset, (u32 *) &emu_cntx->mbox, 4); + + return 0; +} + +static int pru_can_rx_id_set(struct device *dev, u32 nodeid, u32 mboxno) +{ + pruss_writel(dev, (PRUSS_CAN_ID_MAP + (mboxno * 4)), nodeid); + + return 0; +} + +static int pru_can_intr_stat_get(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + if (emu_cntx->pruno == PRUCORE_1) + pruss_readl(dev, PRUSS_CAN_TX_INTR_STAT_REG, + &emu_cntx->intr_stat); + else if (emu_cntx->pruno == PRUCORE_0) + pruss_readl(dev, PRUSS_CAN_RX_INTR_STAT_REG, + &emu_cntx->intr_stat); + else + return -EINVAL; + + return 0; +} + +static int pru_can_gbl_stat_get(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + if (emu_cntx->pruno == PRUCORE_1) + pruss_readl(dev, PRUSS_CAN_TX_GBL_STAT_REG, + &emu_cntx->gbl_stat); + else if (emu_cntx->pruno == PRUCORE_0) + pruss_readl(dev, PRUSS_CAN_RX_GBL_STAT_REG, + &emu_cntx->gbl_stat); + else + return -EINVAL; + return 0; +} + +static int pru_can_tx_mode_set(struct device *dev, bool transfer_flag, + enum can_tx_dir ecan_trx) +{ + u32 value; + + if (ecan_trx == TRANSMIT) { + pruss_readl(dev, PRUSS_CAN_RX_GBL_STAT_REG, &value); + if (transfer_flag) { + value &= PRUSS_CAN_GBL_STAT_REG_MASK; + value |= PRUSS_CAN_GBL_STAT_REG_SET_TX; + } else + value &= PRUSS_CAN_GBL_STAT_REG_STOP_TX; + + pruss_writel(dev, PRUSS_CAN_RX_GBL_STAT_REG, value); + pruss_writel(dev, PRUSS_CAN_TX_GBL_STAT_REG, value); + } else if (ecan_trx == RECEIVE) { + pruss_readl(dev, PRUSS_CAN_RX_GBL_STAT_REG, &value); + if (transfer_flag) { + value &= PRUSS_CAN_GBL_STAT_REG_MASK; + value |= PRUSS_CAN_GBL_STAT_REG_SET_RX; + } else + value &= PRUSS_CAN_GBL_STAT_REG_STOP_RX; + + pruss_writel(dev, PRUSS_CAN_RX_GBL_STAT_REG, value); + pruss_writel(dev, PRUSS_CAN_TX_GBL_STAT_REG, value); + } else + return -EINVAL; + + return 0; +} + +static u32 pruss_intc_init[19][3] = { + {PRUSS_INTC_POLARITY0, PRU_INTC_REGMAP_MASK, 0xFFFFFFFF}, + {PRUSS_INTC_POLARITY1, PRU_INTC_REGMAP_MASK, 0xFFFFFFFF}, + {PRUSS_INTC_TYPE0, PRU_INTC_REGMAP_MASK, 0x1C000000}, + {PRUSS_INTC_TYPE1, PRU_INTC_REGMAP_MASK, 0}, + {PRUSS_INTC_GLBLEN, 0, 1}, + {PRUSS_INTC_HOSTMAP0, PRU_INTC_REGMAP_MASK, 0x03020100}, + {PRUSS_INTC_HOSTMAP1, PRU_INTC_REGMAP_MASK, 0x07060504}, + {PRUSS_INTC_HOSTMAP2, PRU_INTC_REGMAP_MASK, 0x0000908}, + {PRUSS_INTC_CHANMAP0, PRU_INTC_REGMAP_MASK, 0}, + {PRUSS_INTC_CHANMAP8, PRU_INTC_REGMAP_MASK, 0x00020200}, + {PRUSS_INTC_STATIDXCLR, 0, 32}, + {PRUSS_INTC_STATIDXCLR, 0, 19}, + {PRUSS_INTC_ENIDXSET, 0, 19}, + {PRUSS_INTC_STATIDXCLR, 0, 18}, + {PRUSS_INTC_ENIDXSET, 0, 18}, + {PRUSS_INTC_STATIDXCLR, 0, 34}, + {PRUSS_INTC_ENIDXSET, 0, 34}, + {PRUSS_INTC_ENIDXSET, 0, 32}, + {PRUSS_INTC_HOSTINTEN, 0, 5} +}; + +static int pru_can_emu_init(struct device *dev, u32 pruclock) +{ + u32 value[8] = {[0 ... 7] = 1}; + u32 i; + + /* PRU Internal Registers Initializations */ + pruss_writel_multi(dev, PRUSS_CAN_TX_MBOX0_STAT_REG, value, 8); + + *value = PRUSS_CAN_GBL_STAT_REG_VAL; + pruss_writel(dev, PRUSS_CAN_TX_GBL_STAT_REG, value[0]); + pruss_writel(dev, PRUSS_CAN_RX_GBL_STAT_REG, value[0]); + + *value = PRUSS_CAN_INTR_MASK_REG_VAL; + pruss_writel(dev, PRUSS_CAN_TX_INTR_MASK_REG, value[0]); + pruss_writel(dev, PRUSS_CAN_RX_INTR_MASK_REG, value[0]); + + for (i = 0; i < 19; i++) + (i < 12) ? pruss_rmwl(dev, pruss_intc_init[i][0], + pruss_intc_init[i][1], + pruss_intc_init[i][2]) : + pruss_idx_writel(dev, pruss_intc_init[i][0], + pruss_intc_init[i][2]); + return 0; +} + +static void pru_can_emu_exit(struct device *dev) +{ + pruss_disable(dev, PRUSS_CAN_RX_PRU_0); + pruss_disable(dev, PRUSS_CAN_TX_PRU_1); +} + +static int pru_can_tx(struct device *dev, u8 mboxnumber, u8 pruno) +{ + u32 value = 0; + + if (PRUCORE_1 == pruno) { + value = PRUSS_CAN_SET_TX_REQ; + pruss_writel(dev, ((PRUSS_PRU1_BASE_ADDRESS + + (PRUSS_CAN_MBOX_STAT_OFFSET + + (PRUSS_CAN_MBOX_STAT_SIZE * mboxnumber))) + & 0xFFFF), value); + } else if (PRUCORE_0 == pruno) { + pruss_readl(dev, PRUSS_CAN_RX_INTR_STAT_REG, &value); + value = value & ~(1 << mboxnumber); + pruss_writel(dev, PRUSS_CAN_RX_INTR_STAT_REG, value); + value = 0; + pruss_writel(dev, ((PRUSS_PRU0_BASE_ADDRESS + + (PRUSS_CAN_MBOX_STAT_OFFSET + + (PRUSS_CAN_MBOX_STAT_SIZE * mboxnumber))) + & 0xFFFF), value); + } else + return -EINVAL; + return 0; +} + +static int pru_can_reset_tx(struct device *dev) +{ + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, PRUSS_CAN_ARM2DSP_SYS_EVT); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, PRUSS_CAN_ARM2DSP_SYS_EVT); + pruss_idx_writel(dev, PRUSS_INTC_STATIDXSET, PRUSS_CAN_ARM2DSP_SYS_EVT); + return 0; +} + +static void pru_can_mask_ints(struct device *dev, u32 trx, bool enable) +{ + u32 value = 0; + + value = (trx == PRUSS_CAN_TX_PRU_1) ? + PRUSS_CAN_TX_SYS_EVT : PRUSS_CAN_RX_SYS_EVT; + enable ? pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, value) : + pruss_idx_writel(dev, PRUSS_INTC_ENIDXCLR, value); +} + +static unsigned int pru_can_get_error_cnt(struct device *dev, u8 pruno) +{ + u32 value = 0; + + if (pruno == PRUSS_CAN_RX_PRU_0) + pruss_readl(dev, PRUSS_CAN_RX_ERR_CNTR_REG, &value); + else if (pruno == PRUSS_CAN_TX_PRU_1) + pruss_readl(dev, PRUSS_CAN_TX_ERR_CNTR_REG, &value); + else + return -EINVAL; + + return value; +} + +static unsigned int pru_can_get_intc_status(struct device *dev) +{ + u32 value = 0; + + pruss_readl(dev, PRUSS_INTC_STATCLRINT1, &value); + return value; +} + +static void pru_can_clr_intc_status(struct device *dev, u32 trx) +{ + u32 value = 0; + + value = (trx == PRUSS_CAN_TX_PRU_1) ? + PRUSS_CAN_TX_SYS_EVT : PRUSS_CAN_RX_SYS_EVT; + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, value); +} + +static int pru_can_get_state(const struct net_device *ndev, + enum can_state *state) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + *state = priv->can.state; + + return 0; +} + +static int pru_can_set_bittiming(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 value; + + value = priv->can.clock.freq / bt->bitrate; + pruss_writel(priv->dev, PRUSS_CAN_TIMING_VAL_TX, value); + pruss_writel(priv->dev, PRUSS_CAN_BIT_TIMING_VAL_RX, value); + + value = (bt->phase_seg2 + bt->phase_seg1 + + bt->prop_seg + 1) * bt->brp; + value = (value >> 1) - PRUSS_CAN_TIMER_SETUP_DELAY; + value = (value << 16) | value; + pruss_writel(priv->dev, PRUSS_CAN_TIMING_VAL_RX, value); + + value = (PRUSS_CAN_GPIO_SETUP_DELAY * + (priv->clk_freq_pru / 1000000) / 1000) / + PRUSS_CAN_DELAY_LOOP_LENGTH; + + pruss_writel(priv->dev, PRUSS_CAN_TIMING_SETUP, value); + return 0; +} + +static void pru_can_stop(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + + pru_can_mask_ints(priv->dev, PRUSS_CAN_TX_PRU_1, false); + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, false); + pru_can_reset_tx(priv->dev); + priv->can.state = CAN_STATE_STOPPED; +} + +/* + * This is to just set the can state to ERROR_ACTIVE + * ip link set canX up type can bitrate 125000 + */ +static void pru_can_start(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + + if (priv->can.state != CAN_STATE_STOPPED) + pru_can_stop(ndev); + + pru_can_mask_ints(priv->dev, PRUSS_CAN_TX_PRU_1, true); + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, true); + + pru_can_gbl_stat_get(priv->dev, &priv->can_tx_cntx); + pru_can_gbl_stat_get(priv->dev, &priv->can_rx_cntx); + + if (PRUSS_CAN_GSR_BIT_EPM & priv->can_tx_cntx.gbl_stat) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + else if (PRUSS_CAN_GSR_BIT_BFM & priv->can_tx_cntx.gbl_stat) + priv->can.state = CAN_STATE_BUS_OFF; + else + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static int pru_can_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + pru_can_start(ndev); + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +static netdev_tx_t pru_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + int count; + u8 *data = cf->data; + u8 dlc = cf->can_dlc; + u8 *pdata = NULL; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(ndev); + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + priv->can_tx_cntx.mbox.can_id = + (cf->can_id & CAN_EFF_MASK) | PRUSS_CAN_MID_IDE; + else /* Standard frame format */ + priv->can_tx_cntx.mbox.can_id = + (cf->can_id & CAN_SFF_MASK) << PRUSS_CAN_STD_FRAME_MASK; + + if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ + priv->can_tx_cntx.mbox.can_id |= CAN_RTR_FLAG; + + pdata = &priv->can_tx_cntx.mbox.data[0] + (dlc - 1); + for (count = 0; count < (u8) dlc; count++) + *pdata-- = *data++; + + priv->can_tx_cntx.mbox.datalength = (u16) dlc; + priv->can_tx_cntx.mbox.crc = 0; +/* + * search for the next available mbx + * if the next mbx is busy, then try the next + 1 + * do this until the head is reached. + * if still unable to tx, stop accepting any packets + * if able to tx and the head is reached, then reset next to tail, i.e mbx0 + * if head is not reached, then just point to the next mbx + */ + for (; priv->tx_next <= priv->tx_head; priv->tx_next++) { + priv->can_tx_cntx.mboxno = priv->tx_next; + if (-1 == pru_can_mbox_write(priv->dev, + &priv->can_tx_cntx)) { + if (priv->tx_next == priv->tx_head) { + priv->tx_next = priv->tx_tail; + netif_stop_queue(ndev); /* IF stalled */ + dev_err(priv->dev, + "%s: no tx mbx available", __func__); + return NETDEV_TX_BUSY; + } else + continue; + } else { + /* set transmit request */ + pru_can_tx(priv->dev, priv->tx_next, + PRUSS_CAN_TX_PRU_1); + pru_can_tx_mode_set(priv->dev, false, RECEIVE); + pru_can_tx_mode_set(priv->dev, true, TRANSMIT); + pru_can_reset_tx(priv->dev); + priv->tx_next++; + can_put_echo_skb(skb, ndev, 0); + break; + } + } + if (priv->tx_next > priv->tx_head) + priv->tx_next = priv->tx_tail; + + return NETDEV_TX_OK; +} + +static int pru_can_rx(struct net_device *ndev, u32 mbxno) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 *data = NULL; + u8 *pdata = NULL; + int count = 0; + + skb = alloc_can_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_skb() failed\n"); + return -ENOMEM; + } + data = cf->data; + /* get payload */ + priv->can_rx_cntx.mboxno = mbxno; + if (pru_can_mbox_read(priv->dev, &priv->can_rx_cntx)) { + dev_err(priv->dev, "failed to get data from mailbox\n"); + return -EAGAIN; + } + /* give ownweship to pru */ + pru_can_tx(priv->dev, mbxno, PRUSS_CAN_RX_PRU_0); + + /* get data length code */ + cf->can_dlc = get_can_dlc(priv->can_rx_cntx.mbox.datalength & 0xF); + if (cf->can_dlc <= 4) { + pdata = &priv->can_rx_cntx.mbox.data[4] + (4 - cf->can_dlc); + for (count = 0; count < cf->can_dlc; count++) + *data++ = *pdata++; + } else { + pdata = &priv->can_rx_cntx.mbox.data[4]; + for (count = 0; count < 4; count++) + *data++ = *pdata++; + pdata = &priv->can_rx_cntx.mbox.data[3] - (cf->can_dlc - 5); + for (count = 0; count < cf->can_dlc - 4; count++) + *data++ = *pdata++; + } + + /* get id extended or std */ + if (priv->can_rx_cntx.mbox.can_id & PRUSS_CAN_MID_IDE) + cf->can_id = (priv->can_rx_cntx.mbox.can_id & CAN_EFF_MASK) + | CAN_EFF_FLAG; + else + cf->can_id = (priv->can_rx_cntx.mbox.can_id >> 18) + & CAN_SFF_MASK; + + if (priv->can_rx_cntx.mbox.can_id & CAN_RTR_FLAG) + cf->can_id |= CAN_RTR_FLAG; + + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + stats->rx_packets++; + return 0; +} + +static int pru_can_err(struct net_device *ndev, int int_status, + int err_status) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 tx_err_cnt, rx_err_cnt; + + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_err_skb() failed\n"); + return -ENOMEM; + } + + if (err_status & PRUSS_CAN_GSR_BIT_EPM) { /* error passive int */ + priv->can.state = CAN_STATE_ERROR_PASSIVE; + ++priv->can.can_stats.error_passive; + cf->can_id |= CAN_ERR_CRTL; + tx_err_cnt = pru_can_get_error_cnt(priv->dev, + PRUSS_CAN_TX_PRU_1); + rx_err_cnt = pru_can_get_error_cnt(priv->dev, + PRUSS_CAN_RX_PRU_0); + if (tx_err_cnt > PRUSS_CAN_ERROR_ACTIVE - 1) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (rx_err_cnt > PRUSS_CAN_ERROR_ACTIVE - 1) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + + dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + } + + if (err_status & PRUSS_CAN_GSR_BIT_BFM) { + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + /* + * Disable all interrupts in bus-off to avoid int hog + * this should be handled by the pru + */ + pru_can_mask_ints(priv->dev, PRUSS_CAN_TX_PRU_1, false); + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, false); + can_bus_off(ndev); + dev_dbg(priv->ndev->dev.parent, "Bus off mode\n"); + } + + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +static int pru_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct can_emu_priv *priv = netdev_priv(ndev); + u32 bit_set, mbxno = 0; + u32 num_pkts = 0; + + if (!netif_running(ndev)) + return 0; + + do { + /* rx int sys_evt -> 33 */ + pru_can_clr_intc_status(priv->dev, PRUSS_CAN_RX_PRU_0); + if (pru_can_intr_stat_get(priv->dev, &priv->can_rx_cntx)) + return num_pkts; + + if (PRUSS_CAN_ISR_BIT_RRI & priv->can_rx_cntx.intr_stat) { + mbxno = PRUSS_CAN_RTR_BUFF_NUM; + pru_can_rx(ndev, mbxno); + num_pkts++; + } else { + /* Extract the mboxno from the status */ + bit_set = fls(priv->can_rx_cntx.intr_stat & 0xFF); + if (bit_set) { + num_pkts++; + mbxno = bit_set - 1; + if (PRUSS_CAN_ISR_BIT_ESI & priv->can_rx_cntx. + intr_stat) { + pru_can_gbl_stat_get(priv->dev, + &priv->can_rx_cntx); + pru_can_err(ndev, + priv->can_rx_cntx.intr_stat, + priv->can_rx_cntx.gbl_stat); + } else + pru_can_rx(ndev, mbxno); + } else + break; + } + } while (((PRUSS_CAN_TX_INT_STAT & pru_can_get_intc_status(priv->dev)) + && (num_pkts < quota))); + + /* Enable packet interrupt if all pkts are handled */ + if (!(PRUSS_CAN_TX_INT_STAT & pru_can_get_intc_status(priv->dev))) { + napi_complete(napi); + /* Re-enable RX mailbox interrupts */ + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, true); + } + return num_pkts; +} + +static irqreturn_t pru_tx_can_intr(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct can_emu_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 bit_set, mbxno; + + pru_can_intr_stat_get(priv->dev, &priv->can_tx_cntx); + if ((PRUSS_CAN_ISR_BIT_CCI & priv->can_tx_cntx.intr_stat) + || (PRUSS_CAN_ISR_BIT_SRDI & priv->can_tx_cntx.intr_stat)) { + dev_dbg(priv->ndev->dev.parent, "tx_int_status = 0x%X\n", + priv->can_tx_cntx.intr_stat); + can_free_echo_skb(ndev, 0); + } else { + bit_set = fls(priv->can_tx_cntx.intr_stat & 0xFF); + if (!bit_set) { + dev_err(priv->dev, "%s: invalid mailbox number\n", + __func__); + can_free_echo_skb(ndev, 0); + } else { + mbxno = bit_set - 1; + if (PRUSS_CAN_ISR_BIT_ESI & priv->can_tx_cntx. + intr_stat) { + /* read gsr and ack pru */ + pru_can_gbl_stat_get(priv->dev, + &priv->can_tx_cntx); + pru_can_err(ndev, priv->can_tx_cntx.intr_stat, + priv->can_tx_cntx.gbl_stat); + } else { + stats->tx_packets++; + /* stats->tx_bytes += dlc; */ + /*can_get_echo_skb(ndev, 0);*/ + } + } + } + netif_wake_queue(ndev); + can_get_echo_skb(ndev, 0); + pru_can_tx_mode_set(priv->dev, true, RECEIVE); + return IRQ_HANDLED; +} + +static irqreturn_t pru_rx_can_intr(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct can_emu_priv *priv = netdev_priv(ndev); + u32 intc_status = 0; + + intc_status = pru_can_get_intc_status(priv->dev); + + /* tx int sys_evt -> 34 */ + if (intc_status & 4) { + pru_can_clr_intc_status(priv->dev, PRUSS_CAN_TX_PRU_1); + return pru_tx_can_intr(irq, dev_id); + } + /* Disable RX mailbox interrupts and let NAPI reenable them */ + if (intc_status & 2) { + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, false); + napi_schedule(&priv->napi); + } + + return IRQ_HANDLED; +} + +static int pru_can_open(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + s32 err; + + /* register interrupt handler */ + err = request_irq(priv->trx_irq, &pru_rx_can_intr, IRQF_SHARED, + "pru_can_irq", ndev); + if (err) { + dev_err(priv->dev, "error requesting rx interrupt\n"); + goto exit_trx_irq; + } + err = open_candev(ndev); + if (err) { + dev_err(priv->dev, "open_candev() failed %d\n", err); + goto exit_open; + } + + pru_can_emu_init(priv->dev, priv->can.clock.freq); + priv->tx_tail = PRUSS_CAN_MB_MIN; + priv->tx_head = PRUSS_CAN_MB_MAX; + pru_can_start(ndev); + napi_enable(&priv->napi); + netif_start_queue(ndev); + return 0; + +exit_open: + free_irq(priv->trx_irq, ndev); +exit_trx_irq: + return err; +} + +static int pru_can_close(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + close_candev(ndev); + free_irq(priv->trx_irq, ndev); + return 0; +} + +static const struct net_device_ops pru_can_netdev_ops = { + .ndo_open = pru_can_open, + .ndo_stop = pru_can_close, + .ndo_start_xmit = pru_can_start_xmit, +}; + +/* Shows all the mailbox IDs */ +static ssize_t pru_sysfs_mbx_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct can_emu_priv *priv = netdev_priv(to_net_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "\n" + "0:0x%X 1:0x%X 2:0x%X 3:0x%X " + "4:0x%X 5:0x%X 6:0x%X 7:0x%X\n", + priv->mbx_id[0], priv->mbx_id[1], + priv->mbx_id[2], priv->mbx_id[3], + priv->mbx_id[4], priv->mbx_id[5], + priv->mbx_id[6], priv->mbx_id[7]); +} + +/* + * Sets Mailbox IDs + * This should be programmed as mbx_num:mbx_id (in hex) + * eg: $ echo 0:0x123 > /sys/class/net/can0/mbx_id + */ +static ssize_t pru_sysfs_mbx_id_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct net_device *ndev = to_net_dev(dev); + struct can_emu_priv *priv = netdev_priv(ndev); + unsigned long can_id; + unsigned long mbx_num; + char mbx[2] = {*buf, '\0'}; /* mbx num */ + ssize_t ret = count; + s32 err; + + if (ndev->flags & IFF_UP) { + ret = -EBUSY; + goto out; + } + + if (*(buf + 1) != ':') { + ret = -EINVAL; + goto out; + } + + err = strict_strtoul(mbx, 0, &mbx_num); + if (err) { + ret = err; + goto out; + } + + if (mbx_num > 7) { + ret = -EINVAL; + goto out; + } + + err = strict_strtoul((buf + 2), 0, &can_id); + if (err) { + ret = err; + goto out; + } + + priv->mbx_id[mbx_num] = can_id; + pru_can_rx_id_set(priv->dev, priv->mbx_id[mbx_num], mbx_num); + + return ret; +out: + dev_err(priv->dev, "invalid buffer format\n"); + return ret; +} + +static DEVICE_ATTR(mbx_id, S_IWUSR | S_IRUGO, + pru_sysfs_mbx_id_show, pru_sysfs_mbx_id_set); + +static struct attribute *pru_sysfs_attrs[] = { + &dev_attr_mbx_id.attr, + NULL, +}; + +static struct attribute_group pru_sysfs_attr_group = { + .attrs = pru_sysfs_attrs, +}; + +static int __devinit pru_can_probe(struct platform_device *pdev) +{ + struct net_device *ndev = NULL; + const struct da850_evm_pruss_can_data *pdata; + struct can_emu_priv *priv = NULL; + struct device *dev = &pdev->dev; + struct clk *clk_pruss; + const struct firmware *fw_rx; + const struct firmware *fw_tx; + u32 err; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + (pdata->setup)(); + + ndev = alloc_candev(sizeof(struct can_emu_priv), PRUSS_CAN_MB_MAX + 1); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + err = -ENOMEM; + goto probe_exit; + } + + ndev->sysfs_groups[0] = &pru_sysfs_attr_group; + + priv = netdev_priv(ndev); + + priv->trx_irq = platform_get_irq(to_platform_device(dev->parent), 0); + if (!priv->trx_irq) { + dev_err(&pdev->dev, "unable to get pru " + "interrupt resources!\n"); + err = -ENODEV; + goto probe_exit; + } + + priv->ndev = ndev; + priv->dev = dev; + + priv->can.bittiming_const = &pru_can_bittiming_const; + priv->can.do_set_bittiming = pru_can_set_bittiming; + priv->can.do_set_mode = pru_can_set_mode; + priv->can.do_get_state = pru_can_get_state; + priv->can_tx_cntx.pruno = PRUSS_CAN_TX_PRU_1; + priv->can_rx_cntx.pruno = PRUSS_CAN_RX_PRU_0; + + /* we support local echo, no arp */ + ndev->flags |= (IFF_ECHO | IFF_NOARP); + + /* pdev->dev->device_private->driver_data = ndev */ + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &pru_can_netdev_ops; + + priv->clk_timer = clk_get(&pdev->dev, "pll1_sysclk2"); + if (IS_ERR(priv->clk_timer)) { + dev_err(&pdev->dev, "no timer clock available\n"); + err = PTR_ERR(priv->clk_timer); + priv->clk_timer = NULL; + goto probe_exit_candev; + } + + priv->can.clock.freq = clk_get_rate(priv->clk_timer); + + clk_pruss = clk_get(NULL, "pruss"); + if (IS_ERR(clk_pruss)) { + dev_err(&pdev->dev, "no clock available: pruss\n"); + err = -ENODEV; + goto probe_exit_clk; + } + priv->clk_freq_pru = clk_get_rate(clk_pruss); + clk_put(clk_pruss); + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + err = request_firmware(&fw_tx, "PRU_CAN_Emulation_Tx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + dev_info(&pdev->dev, "fw_tx size %d. downloading...\n", fw_tx->size); + + err = request_firmware(&fw_rx, "PRU_CAN_Emulation_Rx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_release_fw; + } + dev_info(&pdev->dev, "fw_rx size %d. downloading...\n", fw_rx->size); + + /* init the pru */ + pru_can_emu_init(priv->dev, priv->can.clock.freq); + udelay(200); + + netif_napi_add(ndev, &priv->napi, pru_can_rx_poll, + PRUSS_DEF_NAPI_WEIGHT); + + pruss_enable(priv->dev, PRUSS_CAN_RX_PRU_0); + pruss_enable(priv->dev, PRUSS_CAN_TX_PRU_1); + + /* download firmware into pru */ + err = pruss_load(priv->dev, PRUSS_CAN_RX_PRU_0, + (u32 *)fw_rx->data, (fw_rx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + release_firmware(fw_rx); + err = pruss_load(priv->dev, PRUSS_CAN_TX_PRU_1, + (u32 *)fw_tx->data, (fw_tx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + release_firmware(fw_tx); + + pruss_run(priv->dev, PRUSS_CAN_RX_PRU_0); + pruss_run(priv->dev, PRUSS_CAN_TX_PRU_1); + + dev_info(&pdev->dev, + "%s device registered (trx_irq = %d, clk = %d)\n", + PRUSS_CAN_DRV_NAME, priv->trx_irq, priv->can.clock.freq); + + return 0; + +probe_release_fw_1: + release_firmware(fw_rx); +probe_release_fw: + release_firmware(fw_tx); +probe_exit_clk: + clk_put(priv->clk_timer); +probe_exit_candev: + if (NULL != ndev) + free_candev(ndev); +probe_exit: + return err; +} + +static int __devexit pru_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct can_emu_priv *priv = netdev_priv(ndev); + + pru_can_stop(ndev); + pru_can_emu_exit(priv->dev); + clk_put(priv->clk_timer); + unregister_candev(ndev); + free_candev(ndev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int pru_can_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} + +static int pru_can_resume(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} +#else +#define pru_can_suspend NULL +#define pru_can_resume NULL +#endif + +static struct platform_driver omapl_pru_can_driver = { + .probe = pru_can_probe, + .remove = __devexit_p(pru_can_remove), + .suspend = pru_can_suspend, + .resume = pru_can_resume, + .driver = { + .name = PRUSS_CAN_DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init pru_can_init(void) +{ + pr_debug(KERN_INFO PRUSS_CAN_DRV_DESC "\n"); + return platform_driver_register(&omapl_pru_can_driver); +} + +module_init(pru_can_init); + +static void __exit pru_can_exit(void) +{ + pr_debug(KERN_INFO PRUSS_CAN_DRV_DESC " unloaded\n"); + platform_driver_unregister(&omapl_pru_can_driver); +} + +module_exit(pru_can_exit); + +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("omapl pru CAN netdevice driver"); -- 1.7.2.3 From khilman at ti.com Fri Apr 22 18:06:42 2011 From: khilman at ti.com (Kevin Hilman) Date: Fri, 22 Apr 2011 16:06:42 -0700 Subject: [linux-pm] [RFC PATCH V3 4/4] cpuidle: Single/Global registration of idle states In-Reply-To: <4DAFB847.50404@linux.vnet.ibm.com> (Trinabh Gupta's message of "Thu, 21 Apr 2011 10:23:27 +0530") References: <20110420065445.332.13688.stgit@tringupt.in.ibm.com> <20110420065608.332.30043.stgit@tringupt.in.ibm.com> <87k4eo6d5m.fsf@ti.com> <4DAFB847.50404@linux.vnet.ibm.com> Message-ID: <871v0tvqbh.fsf@ti.com> Hi Trinabh, Trinabh Gupta writes: [...] > I just wanted to get comments on the design and understand how it > affects various architectures in question. It looks to me as if the > design should be okay and infact better for architectures like ARM > since they do not have different idle states for different cpus and > thus do not require per-cpu registration. Global registration would > work and be simpler; please correct me if I am wrong. Yes, I agree that the new design is better, I especially like that it's more clear (and expected) that final state decision making is to be done directly in the driver without the back-and-forth in the current setup. Thanks, Kevin From michael.williamson at criticallink.com Sat Apr 23 12:31:35 2011 From: michael.williamson at criticallink.com (Michael Williamson) Date: Sat, 23 Apr 2011 13:31:35 -0400 Subject: [PATCH] i2c: davinci: Fix null dereference bug in i2c_davinci_calc_clk_dividers Message-ID: <1303579895-9441-1-git-send-email-michael.williamson@criticallink.com> The davinci-i2c bus driver currently supports being probed with a NULL platform_data structure by using a default configuration when none is provided. However, the i2c_davinci_calc_clk_dividers does not check the provided platform_data for NULL prior to using it, so the support is incomplete. Rather than have each runtime call check for NULL and avoid future problems, copy a pointer to the default data to the device structure during the probe. This should be OK, as the information in this structure is not modified by the driver. Tested on a MityDSP-L138 module (OMAP-L138 based SOM). Signed-off-by: Micahel Williamson --- drivers/i2c/busses/i2c-davinci.c | 12 +++++------- 1 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 5795c83..1db8fc9 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -163,8 +163,7 @@ static void i2c_recover_bus(struct davinci_i2c_dev *dev) flag |= DAVINCI_I2C_MDR_NACK; /* write the data into mode register */ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); - if (pdata) - generic_i2c_clock_pulse(pdata->scl_pin); + generic_i2c_clock_pulse(pdata->scl_pin); /* Send STOP */ flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); flag |= DAVINCI_I2C_MDR_STP; @@ -235,10 +234,11 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev) */ static int i2c_davinci_init(struct davinci_i2c_dev *dev) { - struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + struct davinci_i2c_platform_data *pdata; - if (!pdata) - pdata = &davinci_i2c_platform_data_default; + if (!dev->dev->platform_data) + dev->dev->platform_data = &davinci_i2c_platform_data_default; + pdata = dev->dev->platform_data; /* put I2C into reset */ davinci_i2c_reset_ctrl(dev, 0); @@ -313,8 +313,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) u16 w; int r; - if (!pdata) - pdata = &davinci_i2c_platform_data_default; /* Introduce a delay, required for some boards (e.g Davinci EVM) */ if (pdata->bus_delay) udelay(pdata->bus_delay); -- 1.7.0.4 From manjunath.hadli at ti.com Mon Apr 25 03:49:46 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 25 Apr 2011 14:19:46 +0530 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <201104211218.06340.laurent.pinchart@ideasonboard.com> Message-ID: Laurent, Thank you for your comments. Please find my updates below. The updated patches will follow today. -Manju On Thu, Apr 21, 2011 at 15:48:05, Laurent Pinchart wrote: > Hi Manjunath, > > On Wednesday 20 April 2011 17:30:08 Hadli, Manjunath wrote: > > Hi Laurent, > > Thank you for you very valuable and detailed comments. I have fixed a > > lot of your suggestions and there are a few questions I need more > > explanation for. I will send the fixed and updated patches as a > > follow-up after your clarifications. > > OK. Please see below for answsers. > > > On Thu, Apr 07, 2011 at 17:28:20, Laurent Pinchart wrote: > > > On Saturday 02 April 2011 11:40:49 Manjunath Hadli wrote: > > [snip] > > > > > +static u32 video2_numbuffers = 3; static u32 video3_numbuffers = > > > > +3; > > > > > > Why is the number of buffers set by a module parameter ? It should > > > be negotiated dynamically with REQBUFS. > > > > This is the minimum number of buffers to be allocated by the system as > > there is no scatter-gather mechanism in Davinci. To make sure of > > availability of a minimum numbers of buffers for the system which may > > not be available otherwise due to fragmentation, these are used. > > But you don't reserve the memory when the driver is probed, so how does this help ? That was how it was done earlier. A detailed look at the code revealed that it is not applicable anymore. Fixed it. > > [snip] > > > > > + struct vpbe_display *disp_dev = (struct vpbe_display > > > > + *)disp_obj; > > > > + > > > > + /* Convert time represention from jiffies to timeval */ > > > > + jiffies_to_timeval(jiffies_time, &timevalue); > > > > > > Please use ktime_get_ts() or ktime_get_real_ts() to get the timestamp. > > > > Fixed. Used do_gettimeofday(). > > Please use ktime_get_ts() instead. It will return a monotonic clock timestamp. > Otherwise the buffer timestamp will vary when the system clock is modified (as a result of an NTP time update for instance). Fixed. > > > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > > + layer = disp_dev->dev[i]; > > > > + /* If streaming is started in this layer */ > > > > + if (!layer->started) > > > > + continue; > > > > + /* Check the field format */ > > > > + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && > > > > + (event & OSD_END_OF_FRAME)) { > > > > + /* Progressive mode */ > > > > + if (layer_first_int[i]) { > > > > + layer_first_int[i] = 0; > > > > + continue; > > > > + } > > > > + /* > > > > + * Mark status of the cur_frm to > > > > + * done and unlock semaphore on it > > > > + */ > > > > + > > > > + if (layer->cur_frm != layer->next_frm) { > > > > + layer->cur_frm->ts = timevalue; > > > > + layer->cur_frm->state = VIDEOBUF_DONE; > > > > > > Please use videobuf2. > > > > I would like to get to videobuf2 as a next set of changes. I want to > > get the Dm6446 driver fisrt, add it with Dm365 and do the videobuf2 > > later. I hope it is okay. > > We're trying to get rid of videobuf1, so accepting new drivers that make use of videobuf1 is a bit problematic. Have you had a look at videobuf2 ? How much time do you think it would take to convert this driver to videobuf2 ? Let's first address all the other issues, and then tackle that one. I have gone through the videobuf2 in a limited way. I can get the changes done for videobuf2, but my major objective is to get this version of the driver in. Immediately after that I can take up videobuf2. > > [snip] > > > > > +/* interrupt service routine */ > > > > +static irqreturn_t venc_isr(int irq, void *arg) { > > > > + static unsigned last_event; > > > > + unsigned event = 0; > > > > + > > > > + if (venc_is_second_field()) > > > > + event |= VENC_SECOND_FIELD; > > > > + else > > > > + event |= VENC_FIRST_FIELD; > > > > + > > > > + if (event == (last_event & ~VENC_END_OF_FRAME)) { > > > > + /* > > > > + * If the display is non-interlaced, then we need to > > > > + flag > > > > the + * end-of-frame event at every interrupt regardless of > > > > the + * value of the FIDST bit. We can conclude that the > > > > display is + * non-interlaced if the value of the FIDST bit > > > > is unchanged + * from the previous interrupt. > > > > + */ > > > > > > What about checking pix_fmt.field instead ? > > > > Not sure what you mean here. We get the FIRST or second field event > > from the hardware so we need to check the register value rather than > > pix_fmt.field. > > Interlacing is configured by userspace. When configured in interlaced mode, I expect the device to alternate fields. When configured in progressive mode, I expect it to always return the same field. If that's correct, the FIDST bit is only needed to identify the active field when configured in interlaced mode. > pix_fmt.field can be use to check whether we're in interlaced or progressive mode. In the isr we check both for the hardware setting and the pix_fmt.field if you see. It meant as a double check. I would like to keep it this way if you do not mind. > > [snip] > > > > > +static int vpbe_display_querycap(struct file *file, void *priv, > > > > + struct v4l2_capability *cap) { > > > > + struct vpbe_fh *fh = file->private_data; > > > > + struct vpbe_display_obj *layer = fh->layer; > > > > + > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > > + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); > > > > > > Do you really need a debugging call here ? > > > > I am Ok either ways. When debugging is enabled, it is just one of the > > data points since there are multiple windows. > > You could leave it, but a debug call in the querycap handler doesn't look very useful to me. > > > > > + *cap = vpbe_display_videocap; > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +static int vpbe_display_g_fmt(struct file *file, void *priv, > > > > + struct v4l2_format *fmt) { > > > > + struct vpbe_fh *fh = file->private_data; > > > > + struct vpbe_display_obj *layer = fh->layer; > > > > + > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > > + "VIDIOC_G_FMT, layer id = %d\n", > > > > + layer->device_id); > > > > + > > > > + /* If buffer type is video output */ > > > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > > > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > > > + /* Fill in the information about format */ > > > > + *pixfmt = layer->pix_fmt; > > > > > > I don't see anything wrong in doing > > > > > > fmt->fmt.pix = layer->pix_fmt; > > > > > > directly. > > > > Wel,. In the past patches we had a large amount of multiple > > indirections, and as part of a suggestion on open source I removed all > > of them with a general rule of not having more than 2 indirections. > > What is your take on this? How many indirection levels do you allow? > > There's no hard rule for that. I usually use intermediate pointers when the number of indirection levels grow too high, except when I only need to perform the indirection once or twice, like in this case. OK. Addressed. > > > > > + } else { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > > > + return -EINVAL; > > > > + } > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +static int vpbe_display_s_fmt(struct file *file, void *priv, > > > > + struct v4l2_format *fmt) { > > > > + int ret = 0; > > > > + struct vpbe_fh *fh = file->private_data; > > > > + struct vpbe_display *disp_dev = video_drvdata(file); > > > > + struct vpbe_display_obj *layer = fh->layer; > > > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > > > > > Variables are often declared in longuest to shortest line order in > > > kernel drivers. It might not be a requirement though, but I find it > > > to make code more readable. > > > > cannot do as suggested since the structure variable assignments have > > dependency on previous structure variables. > > Oops, my bad. > > > > > + > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > > + "VIDIOC_S_FMT, layer id = %d\n", > > > > + layer->device_id); > > > > + > > > > + /* If streaming is started, return error */ > > > > + if (layer->started) { > > > > > > I'm pretty sure there's a race condition here. > > > > Not sure about this. The entire driver is under V4L2 lock per layer > > handle and it would not allow another call to come here once in. > > I missed that. Probably because I dislike that lock :-) > > > Can you elaborate how is it a race condition? > > > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > > > + return -EBUSY; > > > > + } > > > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); > > > > + return -EINVAL; > > > > + } else { > > > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > > > + /* Check for valid pixel format */ > > > > + ret = vpbe_try_format(disp_dev, pixfmt, 1); > > > > + if (ret) > > > > + return ret; > > > > + > > > > + /* YUV420 is requested, check availability of the > > > > + other video window */ > > > > + > > > > + layer->pix_fmt = *pixfmt; > > > > + > > > > + /* Get osd layer config */ > > > > + osd_device->ops.get_layer_config(osd_device, > > > > + layer->layer_info.id, cfg); > > > > + /* Store the pixel format in the layer object */ > > > > + cfg->xsize = pixfmt->width; > > > > + cfg->ysize = pixfmt->height; > > > > + cfg->line_length = pixfmt->bytesperline; > > > > + cfg->ypos = 0; > > > > + cfg->xpos = 0; > > > > + cfg->interlaced = > > > > + vpbe_dev->current_timings.interlaced; > > > > + > > > > + /* Change of the default pixel format for both video > > > > windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { > > > > + struct vpbe_display_obj *otherlayer; > > > > > > If the requested format isn't NV12, cfg->pixfmt won't be modified. > > > If it has been set to NV12 by a previous S_FMT call, it won't become > > > YUYV. Is that intentional ? > > > > It is reset to YUYV as part of Open. So it is changed only if it is NV12. > > As stated below, you shouldn't reset formats in open(). Addressed. > > > > > + cfg->pixfmt = PIXFMT_NV12; > > > > + otherlayer = _vpbe_display_get_other_win(disp_dev, > > > > + layer); > > > > + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; > > > > + } > > > > + > > > > + /* Set the layer config in the osd window */ > > > > + ret = osd_device->ops.set_layer_config(osd_device, > > > > + layer->layer_info.id, cfg); > > > > + if (ret < 0) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Error in S_FMT params:\n"); > > > > + return -EINVAL; > > > > + } > > > > + > > > > + /* Readback and fill the local copy of current pix > > > > + format > > > > */ + osd_device->ops.get_layer_config(osd_device, > > > > + layer->layer_info.id, cfg); > > > > + > > > > + /* verify if readback values are as expected */ > > > > + if (layer->pix_fmt.width != cfg->xsize || > > > > + layer->pix_fmt.height != cfg->ysize || > > > > + layer->pix_fmt.bytesperline != layer->layer_info. > > > > + config.line_length || (cfg->interlaced && > > > > + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || > > > > + (!cfg->interlaced && layer->pix_fmt.field != > > > > + V4L2_FIELD_NONE)) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "mismatch:layer conf params:\n"); > > > > + return -EINVAL; > > > > + } > > > > + } > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +/** > > > > + * vpbe_display_g_std - Get the standard in the current encoder > > > > + * > > > > + * Get the standard in the current encoder. Return the status. 0 > > > > +- > > > > success + * -EINVAL on error > > > > + */ > > > > +static int vpbe_display_g_std(struct file *file, void *priv, > > > > + v4l2_std_id *std_id) { > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); > > > > + > > > > + /* Get the standard from the current encoder */ > > > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > > > > + *std_id = vpbe_dev->current_timings.timings.std_id; > > > > + return 0; > > > > + } > > > > + return -EINVAL; > > > > > > Where do you set timings_type ? When can this return an error ? > > > > It can return an error if the driver is set to a DV_PRESET mode. > > timings_type would tell whether is is an SD standard or a DV_PRESET. > > But you don't seem to set it anywhere. It is set in set_dv_presets and s_std, but is part of vpbe.c file. > > > > > +} > > [snip] > > > > > +static int vpbe_display_cfg_layer_default(enum > > > > +vpbe_display_device_id > > > > id, + struct vpbe_display *disp_dev) > > > > +{ > > > > + int err = 0; > > > > + struct osd_layer_config *layer_config; > > > > + struct vpbe_display_obj *layer = disp_dev->dev[id]; > > > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > > > + > > > > + /* First claim the layer for this device */ > > > > + err = osd_device->ops.request_layer(osd_device, > > > > + layer->layer_info.id); > > > > + if (err < 0) { > > > > + /* Couldn't get layer */ > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Display Manager failed to allocate layer\n"); > > > > + return -EBUSY; > > > > + } > > > > + > > > > + layer_config = cfg; > > > > + /* Set the default image and crop values */ > > > > + layer_config->pixfmt = PIXFMT_YCbCrI; > > > > + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; > > > > + layer->pix_fmt.bytesperline = layer_config->line_length = > > > > + vpbe_dev->current_timings.xres * 2; > > > > + > > > > + layer->pix_fmt.width = layer_config->xsize = > > > > + vpbe_dev->current_timings.xres; > > > > + layer->pix_fmt.height = layer_config->ysize = > > > > + vpbe_dev->current_timings.yres; > > > > + layer->pix_fmt.sizeimage = > > > > + layer->pix_fmt.bytesperline * layer->pix_fmt.height; > > > > + layer_config->xpos = 0; > > > > + layer_config->ypos = 0; > > > > + layer_config->interlaced = > > > > + vpbe_dev->current_timings.interlaced; > > > > > > You shouldn't reinitialized the format every time the device is opened. > > > The previously set format should be kept. > > > > This strictly followed across drivers? I am Ok if that is the expectation. > > It's how V4L2 drivers should behave. Some drivers might not follow that rule, but that would be a bug :-) Alright. Fixed. > > > > > + > > > > + /* > > > > + * turn off ping-pong buffer and field inversion to fix > > > > + * the image shaking problem in 1080I mode > > > > + */ > > > > + > > > > + if (cfg->interlaced) > > > > + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; > > > > + else > > > > + layer->pix_fmt.field = V4L2_FIELD_NONE; > > > > + > > > > + err = osd_device->ops.set_layer_config(osd_device, > > > > + layer->layer_info.id, > > > > + layer_config); > > > > + if (err < 0) { > > > > + /* Couldn't set layer */ > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Display Manager failed to set osd layer\n"); > > > > + return -EBUSY; > > > > + } > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +/* > > > > + * vpbe_display_probe() > > > > + * This function creates device entries by register itself to the > > > > +V4L2 > > > > driver + * and initializes fields of each layer objects > > > > + */ > > > > +static __devinit int vpbe_display_probe(struct platform_device > > > > +*pdev) { > > > > + int i, j = 0, k, err = 0; > > > > + struct vpbe_display *disp_dev; > > > > + struct video_device *vbd = NULL; > > > > + struct vpbe_display_obj *vpbe_display_layer = NULL; > > > > + struct resource *res; > > > > + int irq; > > > > + > > > > + printk(KERN_DEBUG "vpbe_display_probe\n"); > > > > + > > > > + /* Allocate memory for vpbe_display */ > > > > + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); > > > > + if (!disp_dev) { > > > > + printk(KERN_ERR "ran out of memory\n"); > > > > + return -ENOMEM; > > > > + } > > > > + > > > > + /* Allocate memory for four plane display objects */ > > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > > + disp_dev->dev[i] = > > > > + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); > > > > + /* If memory allocation fails, return error */ > > > > + if (!disp_dev->dev[i]) { > > > > + printk(KERN_ERR "ran out of memory\n"); > > > > + err = -ENOMEM; > > > > + goto probe_out; > > > > + } > > > > + spin_lock_init(&disp_dev->dev[i]->irqlock); > > > > + mutex_init(&disp_dev->dev[i]->opslock); > > > > + } > > > > + spin_lock_init(&disp_dev->dma_queue_lock); > > > > + > > > > + err = init_vpbe_layer_objects(i); > > > > + if (err) { > > > > + printk(KERN_ERR "Error initializing vpbe display\n"); > > > > + return err; > > > > + } > > > > + > > > > + /* > > > > + * Scan all the platform devices to find the vpbe > > > > + * controller device and get the vpbe_dev object > > > > + */ > > > > + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, > > > > + vpbe_device_get); > > > > + if (err < 0) > > > > + return err; > > > > + > > > > + /* Initialize the vpbe display controller */ > > > > + if (NULL != vpbe_dev->ops.initialize) { > > > > + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); > > > > + if (err) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing > > > > vpbe\n"); + err = -ENOMEM; > > > > + goto probe_out; > > > > + } > > > > + } > > > > + > > > > + /* check the name of davinci device */ > > > > + if (vpbe_dev->cfg->module_name != NULL) > > > > + strcpy(vpbe_display_videocap.card, > > > > + vpbe_dev->cfg->module_name); > > > > + > > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > > + /* Get the pointer to the layer object */ > > > > + vpbe_display_layer = disp_dev->dev[i]; > > > > + /* Allocate memory for video device */ > > > > + vbd = video_device_alloc(); > > > > + if (vbd == NULL) { > > > > + for (j = 0; j < i; j++) { > > > > + video_device_release( > > > > + disp_dev->dev[j]->video_dev); > > > > + } > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of > > > > memory\n"); + err = -ENOMEM; > > > > + goto probe_out; > > > > + } > > > > + /* Initialize field of video device */ > > > > + vbd->release = video_device_release; > > > > + vbd->fops = &vpbe_fops; > > > > + vbd->ioctl_ops = &vpbe_ioctl_ops; > > > > + vbd->minor = -1; > > > > + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; > > > > + vbd->lock = &vpbe_display_layer->opslock; > > > > + > > > > + if (vpbe_dev->current_timings.timings_type & > > > > + VPBE_ENC_STD) > > > > { + vbd->tvnorms = (V4L2_STD_525_60 | > > > > V4L2_STD_625_50); + vbd->current_norm = > > > > + vpbe_dev->current_timings.timings.std_id; > > > > + } else > > > > + vbd->current_norm = 0; > > > > + > > > > + snprintf(vbd->name, sizeof(vbd->name), > > > > + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", > > > > + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, > > > > + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, > > > > + (VPBE_DISPLAY_VERSION_CODE) & 0xff); > > > > + > > > > + /* Set video_dev to the video device */ > > > > + vpbe_display_layer->video_dev = vbd; > > > > + vpbe_display_layer->device_id = i; > > > > + > > > > + vpbe_display_layer->layer_info.id = > > > > + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); > > > > + if (display_buf_config_params.numbuffers[i] == 0) > > > > + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; > > > > + else > > > > + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; > > > > + > > > > + /* Initialize field of the display layer objects */ > > > > + vpbe_display_layer->usrs = 0; > > > > + vpbe_display_layer->io_usrs = 0; > > > > + vpbe_display_layer->started = 0; > > > > + > > > > + /* Initialize prio member of layer object */ > > > > + v4l2_prio_init(&vpbe_display_layer->prio); > > > > + > > > > + /* Register video device */ > > > > + v4l2_info(&vpbe_dev->v4l2_dev, > > > > + "Trying to register VPBE display device.\n"); > > > > + v4l2_info(&vpbe_dev->v4l2_dev, > > > > + "layer=%x,layer->video_dev=%x\n", > > > > + (int)vpbe_display_layer, > > > > + (int)&vpbe_display_layer->video_dev); > > > > + > > > > + err = video_register_device(vpbe_display_layer-> > > > > + video_dev, > > > > + VFL_TYPE_GRABBER, > > > > + vpbe_display_nr[i]); > > > > + if (err) > > > > + goto probe_out; > > > > + /* set the driver data in platform device */ > > > > + platform_set_drvdata(pdev, disp_dev); > > > > + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); > > > > + } > > > > + > > > > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > > > > + if (!res) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Unable to get VENC interrupt resource\n"); > > > > + err = -ENODEV; > > > > + goto probe_out; > > > > + } > > > > + irq = res->start; > > > > + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, > > > > + disp_dev)) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request > > > > interrupt\n"); + err = -ENODEV; > > > > + goto probe_out; > > > > + } > > > > > > You probably want to get the resources and register the interrupt > > > handler before registering the V4L2 devices, otherwise userspace > > > will be able to open devices before you're done with the initialization. > > > > I do not think anyone would attempt to open the device so soon as the > > boot process is not complete yet. > > Unless you compile the driver as a module. In that case an application could open the device as soon as it gets registered. hal is known to do that. > > > Also, if the irq is registered before, the interrupts start and the > > driver crashes for lack of initialized structure variables. > > Why would an interrupt occur before the device gets opened ? I got the interrupt and the driver crashed because of initialized pointers if I put this before. I will see why and fix it. > > > > > + printk(KERN_DEBUG "Successfully completed the probing of vpbe > > > > + v4l2 > > > > device\n"); + return 0; > > > > +probe_out: > > > > + kfree(disp_dev); > > > > + > > > > + for (k = 0; k < j; k++) { > > > > + /* Get the pointer to the layer object */ > > > > + vpbe_display_layer = disp_dev->dev[k]; > > > > + /* Unregister video device */ > > > > + video_unregister_device(vpbe_display_layer->video_dev); > > > > + /* Release video device */ > > > > + video_device_release(vpbe_display_layer->video_dev); > > > > + vpbe_display_layer->video_dev = NULL; > > > > + } > > > > + return err; > > > > +} > > > > > > [snip] > > > > > > > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); > > > > > > Should this be "TI DM644x" instead ? > > > > This is a common IP for DM644x, Dm355 and Dm365 for which the patches > > will follow after this set. So I think it is OK. > > What about "TI DM644x/DM355/DM365" then ? DMXXX makes it look like it supports all DaVinci chips. > > -- > Regards, > > Laurent Pinchart > From nsekhar at ti.com Mon Apr 25 10:51:57 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 25 Apr 2011 21:21:57 +0530 Subject: [GIT PULL] DaVinci fixes for 2.6.39-rc Message-ID: Hi Russell, Please pull the following DaVinci fixes for 2.6.39-rc. Thanks, Sekhar The following changes since commit f0e615c3cb72b42191b558c130409335812621d8: Linus Torvalds (1): Linux 2.6.39-rc4 are available in the git repository at: git://gitorious.org/linux-davinci/linux-davinci.git davinci-fixes Kevin Hilman (1): davinci: fix DEBUG_LL code for p2v changes Michael Williamson (2): davinci: mityomapl138: Use correct id for NAND controller davinci: mityomapl138: Use auto-probe to determine attached PHY ID Russell King - ARM Linux (1): ARM: Davinci: Fix I2C build errors Sergei Shtylyov (1): DA830: fix SPI1 base address arch/arm/mach-davinci/Kconfig | 6 ++++++ arch/arm/mach-davinci/board-mityomapl138.c | 4 ++-- arch/arm/mach-davinci/devices-da8xx.c | 12 +++++++++--- arch/arm/mach-davinci/include/mach/debug-macro.S | 13 ++++++++----- arch/arm/mach-davinci/include/mach/serial.h | 2 +- 5 files changed, 26 insertions(+), 11 deletions(-) From greg at kroah.com Mon Apr 25 16:20:56 2011 From: greg at kroah.com (Greg KH) Date: Mon, 25 Apr 2011 14:20:56 -0700 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> Message-ID: <20110425212056.GA29313@kroah.com> On Fri, Apr 22, 2011 at 05:38:26PM +0530, Subhasish Ghosh wrote: > This patch adds support for the TTY compliant > Soft-UART device emulated on PRUSS. > > This patch depends on: > davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. > https://patchwork.kernel.org/patch/615681/ > davinci: changed SRAM allocator to shared ram. > https://patchwork.kernel.org/patch/549351/ Who is going to be applying these patches to the tree? Should this driver go through a davinci subtree because of these dependancies? thanks, greg k-h From vladoman at gmail.com Tue Apr 26 01:37:35 2011 From: vladoman at gmail.com (Vladimir Pantelic) Date: Tue, 26 Apr 2011 08:37:35 +0200 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1301737249-4012-1-git-send-email-manjunath.hadli@ti.com> References: <1301737249-4012-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DB6682F.2010109@gmail.com> Manjunath Hadli wrote: > This is the display driver for Texas Instruments's DM644X family > SoC. This patch contains the main implementation of the driver with the > V4L2 interface. The driver implements the streaming model with > support for both kernel allocated buffers and user pointers. It also > implements all of the necessary IOCTLs necessary and supported by the > video display device. is there any hope/chance to make this share code with the omap v4l2 driver in drivers/media/video/omap? From nsekhar at ti.com Tue Apr 26 01:51:04 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 26 Apr 2011 12:21:04 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <20110425212056.GA29313@kroah.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <20110425212056.GA29313@kroah.com> Message-ID: On Tue, Apr 26, 2011 at 02:50:56, Greg KH wrote: > On Fri, Apr 22, 2011 at 05:38:26PM +0530, Subhasish Ghosh wrote: > > This patch adds support for the TTY compliant > > Soft-UART device emulated on PRUSS. > > > > This patch depends on: > > davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. > > https://patchwork.kernel.org/patch/615681/ This is already in mainline. Plus this patch doesn't really seem to depend on this commit. > > davinci: changed SRAM allocator to shared ram. > > https://patchwork.kernel.org/patch/549351/ There should be no build time dependency with this patch (the above patch just changes which pool of SRAM the allocation happens from) But, this brings out an important dependency of the patch calling platform specific sram allocator functions. There has been SRAM allocator consolidation work done by Russell and as a result the SRAM allocator API for DaVinci will actually change. The driver should probably just get sram space through platform data so that it doesn't depend on the platform specific sram allocation function. > > Who is going to be applying these patches to the tree? > > Should this driver go through a davinci subtree because of these > dependancies? No, driver and platform changes can be merged separately if the above aspect is taken care of. Russell has been pushing back on merging driver patches through his tree unless absolutely required. Thanks, Sekhar From lrg at slimlogic.co.uk Tue Apr 26 03:16:56 2011 From: lrg at slimlogic.co.uk (Liam Girdwood) Date: Tue, 26 Apr 2011 09:16:56 +0100 Subject: [PATCH 0/4] davinci-mcasp: fix tdm_slots and CBM/CFS In-Reply-To: References: Message-ID: <1303805816.3332.15.camel@odin> On Thu, 2011-04-21 at 14:19 -0400, Ben Gardiner wrote: > This patch series is comprised of three bugfixes and one cleanup that > were performed during prototyping of McASP operation in codec clock- > master frame-slave mode. > > First we noticed that the check of the number of tdm slots requested > by platform data was always returning true -- unrelated to CMB/CFS > mode. > > Then a cleanup: the PDIR values set are currently based on magic > numbers and there are available bitfield definitions. Not strictly > needed but it makes the changes introduced in the last patch simpler > to read. > > It was found that the hardware parameters assigned when > codec clock-master frame-slave is requested were incorrect. This > change is required for correct CBM/CFS operation. > > Finally, the direction of the pins is corrected to reflect the > implications of codec clock-master frame-slave -- i.e. mcasp clock- > input frame-output. This change is also required for correct operation. > > The combination was tested with a logic analyzer and the hrtimer pwm > device from Bill Gatliff's PWM framework [1] on a da850evm with > hardware modifications to access the McASP lines. > > [1] http://article.gmane.org/gmane.linux.kernel.embedded/3486/ > > Ben Gardiner (4): > davinci-mcasp: correct tdm_slots limit > davinci-mcasp: use bitfield definitions for PDIR > davinci-mcasp: fix _CBM_CFS hw_params > davinci-mcasp: fix _CBM_CFS pin directions > > sound/soc/davinci/davinci-mcasp.c | 19 ++++++++++++------- > 1 files changed, 12 insertions(+), 7 deletions(-) > All Acked-by: Liam Girdwood From gasparini at imavis.com Tue Apr 26 05:42:12 2011 From: gasparini at imavis.com (Andrea Gasparini) Date: Tue, 26 Apr 2011 12:42:12 +0200 Subject: Davinci DM365 NAND Flash SYNDROME Support In-Reply-To: <70E876B0EA86DD4BAF101844BC814DFE093F97739B@Cloud.RL.local> References: <70E876B0EA86DD4BAF101844BC814DFE093F97739B@Cloud.RL.local> Message-ID: <201104261242.12174.gasparini@imavis.com> Hi Michael, hi jon! I'm resurrecting this thread just to know if anyone landed on a practical solution. Jon Povey wrote: > Michael Hallak-Stamler wrote: > > So as I understand I am on my own regarding this issue. I'm > > just in absolute wonder how it is that I am the only one in > > this very large world who has this issue? Am I the first?? > > Hard to believe. > > Quite possibly. Not all that many people want to do such a big update > to firmware already in the field - they either do incremental bugfixes > or there is such high churn that they're already end-of-lifing it and > selling the next thing. > That's my impression, anyway. I'm currently on this situation too. (well, i had issues with NAND support on almost every TI SDK versions... Jon knows :P ) Michael: have you resolved this or at least made some steps forward? Thanks, bye! -- Andrea Gasparini ---- ImaVis S.r.l. ---- web: www.imavis.com From broonie at opensource.wolfsonmicro.com Tue Apr 26 05:46:36 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Tue, 26 Apr 2011 11:46:36 +0100 Subject: [PATCH 1/4] davinci-mcasp: correct tdm_slots limit In-Reply-To: References: Message-ID: <20110426104635.GA13252@opensource.wolfsonmicro.com> On Thu, Apr 21, 2011 at 02:19:01PM -0400, Ben Gardiner wrote: > The current check for the number of tdm-slots specified by platform data is > always true (x >= 2 || x <= 32); therefore the else branch that warns of an > incorrect number of slots can never be taken. Applied all of these. Please always try to ensure that your commit logs are consistent with the rest of the subsystem so they don't need to be rewritten. From sshtylyov at mvista.com Tue Apr 26 05:57:13 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 26 Apr 2011 14:57:13 +0400 Subject: [PATCH v4 11/11] da850: pruss CAN board specific additions. In-Reply-To: <1303474109-6212-12-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-12-git-send-email-subhasish@mistralsolutions.com> Message-ID: <4DB6A509.2010009@mvista.com> Hello. On 22-04-2011 16:08, Subhasish Ghosh wrote: > This patch adds the pruss CAN pinmux and registers the device > with the pruss mfd driver. > Signed-off-by: Subhasish Ghosh > --- > arch/arm/mach-davinci/board-da850-evm.c | 46 +++++++++++++++++++++++++++++++ > 1 files changed, 46 insertions(+), 0 deletions(-) > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index e7fdf31..e1ff18c 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c [...] > @@ -1117,6 +1118,43 @@ static __init int da850_evm_init_cpufreq(void) > static __init int da850_evm_init_cpufreq(void) { return 0; } > #endif > > +static const short da850_evm_pruss_can_pins[] = { > + DA850_PRUSS_PRU0_R31_0, DA850_PRUSS_PRU1_R30_15, > + DA850_PRUSS_PRU1_R31_18, DA850_GPIO2_0, > + -1 > +}; > + > +static int __init da850_evm_pruss_can_setup(void) > +{ > + int ret, val = 0; > + void __iomem *cfg_chip3_reg; > + > + ret = davinci_cfg_reg_list(da850_evm_pruss_can_pins); > + if (ret) > + pr_warning("%s: da850_evm_pruss_can_pins mux setup " > + "failed:%d\n", __func__, ret); Yet you continue to initialize... you should stop here I think. > + /* value = 0 to enable the CAN transceiver */ > + ret = gpio_request_one(DA850_PRUSS_CAN_TRX_PIN, > + GPIOF_OUT_INIT_LOW, "pruss_can_en"); > + if (ret) { > + pr_warning("Cannot setup GPIO %d\n", DA850_PRUSS_CAN_TRX_PIN); > + gpio_free(DA850_PRUSS_CAN_TRX_PIN); Doesn't gpio_request_one() handle freeing GPIO on error? WBR, Sergei From sshtylyov at mvista.com Tue Apr 26 06:06:30 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 26 Apr 2011 15:06:30 +0400 Subject: [PATCH v4 03/11] da850: pruss platform specific additions. In-Reply-To: <1303474109-6212-4-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-4-git-send-email-subhasish@mistralsolutions.com> Message-ID: <4DB6A736.3030609@mvista.com> Hello. On 22-04-2011 16:08, Subhasish Ghosh wrote: > This patch adds the platform device and assignes the platform resources > for the PRUSS mfd driver. > Signed-off-by: Subhasish Ghosh [...] > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index 09b8ddb..0c23035 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h [...] > #include > @@ -73,6 +75,7 @@ extern unsigned int da850_max_speed; > #define DA8XX_DDR2_CTL_BASE 0xb0000000 > #define DA8XX_ARM_RAM_BASE 0xffff0000 > #define DA8XX_SHARED_RAM_BASE 0x80000000 > +#define DA8XX_PRUSS_MEM_BASE 0x01C30000 Keep the list sorted please. Also, this macro doesn't seem used outside devices-da8xx.c, so should be moved there... WBR, Sergei From gregkh at suse.de Tue Apr 26 07:45:19 2011 From: gregkh at suse.de (Greg KH) Date: Tue, 26 Apr 2011 05:45:19 -0700 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <20110425212056.GA29313@kroah.com> Message-ID: <20110426124519.GC5977@suse.de> On Tue, Apr 26, 2011 at 12:21:04PM +0530, Nori, Sekhar wrote: > On Tue, Apr 26, 2011 at 02:50:56, Greg KH wrote: > > On Fri, Apr 22, 2011 at 05:38:26PM +0530, Subhasish Ghosh wrote: > > > This patch adds support for the TTY compliant > > > Soft-UART device emulated on PRUSS. > > > > > > This patch depends on: > > > davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. > > > https://patchwork.kernel.org/patch/615681/ > > This is already in mainline. Plus this patch > doesn't really seem to depend on this commit. > > > > davinci: changed SRAM allocator to shared ram. > > > https://patchwork.kernel.org/patch/549351/ > > There should be no build time dependency with this patch > (the above patch just changes which pool of SRAM the > allocation happens from) > > But, this brings out an important dependency of the patch > calling platform specific sram allocator functions. There > has been SRAM allocator consolidation work done by Russell > and as a result the SRAM allocator API for DaVinci will > actually change. The driver should probably just get sram > space through platform data so that it doesn't depend on the > platform specific sram allocation function. Ok, care to fix up the code then? > > Who is going to be applying these patches to the tree? > > > > Should this driver go through a davinci subtree because of these > > dependancies? > > No, driver and platform changes can be merged separately > if the above aspect is taken care of. Russell has been > pushing back on merging driver patches through his tree > unless absolutely required. That's fine, I'll take it through my tree then, care to resolve the above issue and resend it? thanks, greg k-h From bengardiner at nanometrics.ca Tue Apr 26 08:20:50 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 26 Apr 2011 09:20:50 -0400 Subject: [PATCH 1/4] davinci-mcasp: correct tdm_slots limit In-Reply-To: <20110426104635.GA13252@opensource.wolfsonmicro.com> References: <20110426104635.GA13252@opensource.wolfsonmicro.com> Message-ID: On Tue, Apr 26, 2011 at 6:46 AM, Mark Brown wrote: > On Thu, Apr 21, 2011 at 02:19:01PM -0400, Ben Gardiner wrote: >> The current check for the number of tdm-slots specified by platform data is >> always true (x >= 2 || x <= 32); therefore the else branch that warns of an >> incorrect number of slots can never be taken. > > Applied all of these. ?Please always try to ensure that your commit logs > are consistent with the rest of the subsystem so they don't need to be > rewritten. Thanks, Mark, for taking the patches anyways (and Liam for the Ack's) -- Sorry I forgot the 'ASoC' tag (I noticed this patch was committed as 049cfaa ASoC: davinci-mcasp: correct tdm_slots limit). Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From manjunath.hadli at ti.com Tue Apr 26 08:29:50 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 26 Apr 2011 18:59:50 +0530 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <4DB6682F.2010109@gmail.com> References: <1301737249-4012-1-git-send-email-manjunath.hadli@ti.com>, <4DB6682F.2010109@gmail.com> Message-ID: Hello Vladimir, Davinci family of devices consist of mainly Dm6446, Dm6467, Dm365, Dm355 which are dissimilar to OMAP devices in features and registers, and some (for ex Dm6467) in entire architecture. the current driver which you are seeing is DM6446, but the intent is to build-up the same code with support for DM355 and dM365 which belong to the same family and have almost same features and registers. There is no plan to share code with OMAP directory. Thanks and Regards, -Manju ________________________________________ From: Vladimir Pantelic [vladoman at gmail.com] Sent: Tuesday, April 26, 2011 12:07 PM To: Hadli, Manjunath Cc: LMML; Kevin Hilman; LAK; Nori, Sekhar; dlos Subject: Re: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC Manjunath Hadli wrote: > This is the display driver for Texas Instruments's DM644X family > SoC. This patch contains the main implementation of the driver with the > V4L2 interface. The driver implements the streaming model with > support for both kernel allocated buffers and user pointers. It also > implements all of the necessary IOCTLs necessary and supported by the > video display device. is there any hope/chance to make this share code with the omap v4l2 driver in drivers/media/video/omap? From manjunath.hadli at ti.com Tue Apr 26 09:47:45 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 26 Apr 2011 20:17:45 +0530 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: Message-ID: Laurent, Can you please review the patches with your suggestions from : http://git.linuxtv.org/mhadli/v4l-dvb-davinci_devices.git?a=shortlog;h=refs/heads/forkhilman2 and let me know if you think all your suggestions are taken care of? The patch you reviewed was : http://git.linuxtv.org/mhadli/v4l-dvb-davinci_devices.git?a=commitdiff;h=69f60ed7577ab9184ceabd7efbe5bb3453bf7ef1;hp=a400604f47c339831880c50eda6f6b03221579e3 -Manju -----Original Message----- From: Hadli, Manjunath Sent: Monday, April 25, 2011 2:20 PM To: 'Laurent Pinchart' Cc: davinci-linux-open-source at linux.davincidsp.com; LMML; Kevin Hilman; LAK; Nori, Sekhar Subject: RE: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC Laurent, Thank you for your comments. Please find my updates below. The updated patches will follow today. -Manju On Thu, Apr 21, 2011 at 15:48:05, Laurent Pinchart wrote: > Hi Manjunath, > > On Wednesday 20 April 2011 17:30:08 Hadli, Manjunath wrote: > > Hi Laurent, > > Thank you for you very valuable and detailed comments. I have fixed a > > lot of your suggestions and there are a few questions I need more > > explanation for. I will send the fixed and updated patches as a > > follow-up after your clarifications. > > OK. Please see below for answsers. > > > On Thu, Apr 07, 2011 at 17:28:20, Laurent Pinchart wrote: > > > On Saturday 02 April 2011 11:40:49 Manjunath Hadli wrote: > > [snip] > > > > > +static u32 video2_numbuffers = 3; static u32 video3_numbuffers = > > > > +3; > > > > > > Why is the number of buffers set by a module parameter ? It should > > > be negotiated dynamically with REQBUFS. > > > > This is the minimum number of buffers to be allocated by the system as > > there is no scatter-gather mechanism in Davinci. To make sure of > > availability of a minimum numbers of buffers for the system which may > > not be available otherwise due to fragmentation, these are used. > > But you don't reserve the memory when the driver is probed, so how does this help ? That was how it was done earlier. A detailed look at the code revealed that it is not applicable anymore. Fixed it. > > [snip] > > > > > + struct vpbe_display *disp_dev = (struct vpbe_display > > > > + *)disp_obj; > > > > + > > > > + /* Convert time represention from jiffies to timeval */ > > > > + jiffies_to_timeval(jiffies_time, &timevalue); > > > > > > Please use ktime_get_ts() or ktime_get_real_ts() to get the timestamp. > > > > Fixed. Used do_gettimeofday(). > > Please use ktime_get_ts() instead. It will return a monotonic clock timestamp. > Otherwise the buffer timestamp will vary when the system clock is modified (as a result of an NTP time update for instance). Fixed. > > > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > > + layer = disp_dev->dev[i]; > > > > + /* If streaming is started in this layer */ > > > > + if (!layer->started) > > > > + continue; > > > > + /* Check the field format */ > > > > + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && > > > > + (event & OSD_END_OF_FRAME)) { > > > > + /* Progressive mode */ > > > > + if (layer_first_int[i]) { > > > > + layer_first_int[i] = 0; > > > > + continue; > > > > + } > > > > + /* > > > > + * Mark status of the cur_frm to > > > > + * done and unlock semaphore on it > > > > + */ > > > > + > > > > + if (layer->cur_frm != layer->next_frm) { > > > > + layer->cur_frm->ts = timevalue; > > > > + layer->cur_frm->state = VIDEOBUF_DONE; > > > > > > Please use videobuf2. > > > > I would like to get to videobuf2 as a next set of changes. I want to > > get the Dm6446 driver fisrt, add it with Dm365 and do the videobuf2 > > later. I hope it is okay. > > We're trying to get rid of videobuf1, so accepting new drivers that make use of videobuf1 is a bit problematic. Have you had a look at videobuf2 ? How much time do you think it would take to convert this driver to videobuf2 ? Let's first address all the other issues, and then tackle that one. I have gone through the videobuf2 in a limited way. I can get the changes done for videobuf2, but my major objective is to get this version of the driver in. Immediately after that I can take up videobuf2. > > [snip] > > > > > +/* interrupt service routine */ > > > > +static irqreturn_t venc_isr(int irq, void *arg) { > > > > + static unsigned last_event; > > > > + unsigned event = 0; > > > > + > > > > + if (venc_is_second_field()) > > > > + event |= VENC_SECOND_FIELD; > > > > + else > > > > + event |= VENC_FIRST_FIELD; > > > > + > > > > + if (event == (last_event & ~VENC_END_OF_FRAME)) { > > > > + /* > > > > + * If the display is non-interlaced, then we need to > > > > + flag > > > > the + * end-of-frame event at every interrupt regardless of > > > > the + * value of the FIDST bit. We can conclude that the > > > > display is + * non-interlaced if the value of the FIDST bit > > > > is unchanged + * from the previous interrupt. > > > > + */ > > > > > > What about checking pix_fmt.field instead ? > > > > Not sure what you mean here. We get the FIRST or second field event > > from the hardware so we need to check the register value rather than > > pix_fmt.field. > > Interlacing is configured by userspace. When configured in interlaced mode, I expect the device to alternate fields. When configured in progressive mode, I expect it to always return the same field. If that's correct, the FIDST bit is only needed to identify the active field when configured in interlaced mode. > pix_fmt.field can be use to check whether we're in interlaced or progressive mode. In the isr we check both for the hardware setting and the pix_fmt.field if you see. It meant as a double check. I would like to keep it this way if you do not mind. > > [snip] > > > > > +static int vpbe_display_querycap(struct file *file, void *priv, > > > > + struct v4l2_capability *cap) { > > > > + struct vpbe_fh *fh = file->private_data; > > > > + struct vpbe_display_obj *layer = fh->layer; > > > > + > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > > + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); > > > > > > Do you really need a debugging call here ? > > > > I am Ok either ways. When debugging is enabled, it is just one of the > > data points since there are multiple windows. > > You could leave it, but a debug call in the querycap handler doesn't look very useful to me. > > > > > + *cap = vpbe_display_videocap; > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +static int vpbe_display_g_fmt(struct file *file, void *priv, > > > > + struct v4l2_format *fmt) { > > > > + struct vpbe_fh *fh = file->private_data; > > > > + struct vpbe_display_obj *layer = fh->layer; > > > > + > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > > + "VIDIOC_G_FMT, layer id = %d\n", > > > > + layer->device_id); > > > > + > > > > + /* If buffer type is video output */ > > > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > > > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > > > + /* Fill in the information about format */ > > > > + *pixfmt = layer->pix_fmt; > > > > > > I don't see anything wrong in doing > > > > > > fmt->fmt.pix = layer->pix_fmt; > > > > > > directly. > > > > Wel,. In the past patches we had a large amount of multiple > > indirections, and as part of a suggestion on open source I removed all > > of them with a general rule of not having more than 2 indirections. > > What is your take on this? How many indirection levels do you allow? > > There's no hard rule for that. I usually use intermediate pointers when the number of indirection levels grow too high, except when I only need to perform the indirection once or twice, like in this case. OK. Addressed. > > > > > + } else { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > > > + return -EINVAL; > > > > + } > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +static int vpbe_display_s_fmt(struct file *file, void *priv, > > > > + struct v4l2_format *fmt) { > > > > + int ret = 0; > > > > + struct vpbe_fh *fh = file->private_data; > > > > + struct vpbe_display *disp_dev = video_drvdata(file); > > > > + struct vpbe_display_obj *layer = fh->layer; > > > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > > > > > Variables are often declared in longuest to shortest line order in > > > kernel drivers. It might not be a requirement though, but I find it > > > to make code more readable. > > > > cannot do as suggested since the structure variable assignments have > > dependency on previous structure variables. > > Oops, my bad. > > > > > + > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > > > + "VIDIOC_S_FMT, layer id = %d\n", > > > > + layer->device_id); > > > > + > > > > + /* If streaming is started, return error */ > > > > + if (layer->started) { > > > > > > I'm pretty sure there's a race condition here. > > > > Not sure about this. The entire driver is under V4L2 lock per layer > > handle and it would not allow another call to come here once in. > > I missed that. Probably because I dislike that lock :-) > > > Can you elaborate how is it a race condition? > > > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > > > + return -EBUSY; > > > > + } > > > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); > > > > + return -EINVAL; > > > > + } else { > > > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > > > + /* Check for valid pixel format */ > > > > + ret = vpbe_try_format(disp_dev, pixfmt, 1); > > > > + if (ret) > > > > + return ret; > > > > + > > > > + /* YUV420 is requested, check availability of the > > > > + other video window */ > > > > + > > > > + layer->pix_fmt = *pixfmt; > > > > + > > > > + /* Get osd layer config */ > > > > + osd_device->ops.get_layer_config(osd_device, > > > > + layer->layer_info.id, cfg); > > > > + /* Store the pixel format in the layer object */ > > > > + cfg->xsize = pixfmt->width; > > > > + cfg->ysize = pixfmt->height; > > > > + cfg->line_length = pixfmt->bytesperline; > > > > + cfg->ypos = 0; > > > > + cfg->xpos = 0; > > > > + cfg->interlaced = > > > > + vpbe_dev->current_timings.interlaced; > > > > + > > > > + /* Change of the default pixel format for both video > > > > windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { > > > > + struct vpbe_display_obj *otherlayer; > > > > > > If the requested format isn't NV12, cfg->pixfmt won't be modified. > > > If it has been set to NV12 by a previous S_FMT call, it won't become > > > YUYV. Is that intentional ? > > > > It is reset to YUYV as part of Open. So it is changed only if it is NV12. > > As stated below, you shouldn't reset formats in open(). Addressed. > > > > > + cfg->pixfmt = PIXFMT_NV12; > > > > + otherlayer = _vpbe_display_get_other_win(disp_dev, > > > > + layer); > > > > + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; > > > > + } > > > > + > > > > + /* Set the layer config in the osd window */ > > > > + ret = osd_device->ops.set_layer_config(osd_device, > > > > + layer->layer_info.id, cfg); > > > > + if (ret < 0) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Error in S_FMT params:\n"); > > > > + return -EINVAL; > > > > + } > > > > + > > > > + /* Readback and fill the local copy of current pix > > > > + format > > > > */ + osd_device->ops.get_layer_config(osd_device, > > > > + layer->layer_info.id, cfg); > > > > + > > > > + /* verify if readback values are as expected */ > > > > + if (layer->pix_fmt.width != cfg->xsize || > > > > + layer->pix_fmt.height != cfg->ysize || > > > > + layer->pix_fmt.bytesperline != layer->layer_info. > > > > + config.line_length || (cfg->interlaced && > > > > + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || > > > > + (!cfg->interlaced && layer->pix_fmt.field != > > > > + V4L2_FIELD_NONE)) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "mismatch:layer conf params:\n"); > > > > + return -EINVAL; > > > > + } > > > > + } > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +/** > > > > + * vpbe_display_g_std - Get the standard in the current encoder > > > > + * > > > > + * Get the standard in the current encoder. Return the status. 0 > > > > +- > > > > success + * -EINVAL on error > > > > + */ > > > > +static int vpbe_display_g_std(struct file *file, void *priv, > > > > + v4l2_std_id *std_id) { > > > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); > > > > + > > > > + /* Get the standard from the current encoder */ > > > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > > > > + *std_id = vpbe_dev->current_timings.timings.std_id; > > > > + return 0; > > > > + } > > > > + return -EINVAL; > > > > > > Where do you set timings_type ? When can this return an error ? > > > > It can return an error if the driver is set to a DV_PRESET mode. > > timings_type would tell whether is is an SD standard or a DV_PRESET. > > But you don't seem to set it anywhere. It is set in set_dv_presets and s_std, but is part of vpbe.c file. > > > > > +} > > [snip] > > > > > +static int vpbe_display_cfg_layer_default(enum > > > > +vpbe_display_device_id > > > > id, + struct vpbe_display *disp_dev) > > > > +{ > > > > + int err = 0; > > > > + struct osd_layer_config *layer_config; > > > > + struct vpbe_display_obj *layer = disp_dev->dev[id]; > > > > + struct osd_layer_config *cfg = &layer->layer_info.config; > > > > + > > > > + /* First claim the layer for this device */ > > > > + err = osd_device->ops.request_layer(osd_device, > > > > + layer->layer_info.id); > > > > + if (err < 0) { > > > > + /* Couldn't get layer */ > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Display Manager failed to allocate layer\n"); > > > > + return -EBUSY; > > > > + } > > > > + > > > > + layer_config = cfg; > > > > + /* Set the default image and crop values */ > > > > + layer_config->pixfmt = PIXFMT_YCbCrI; > > > > + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; > > > > + layer->pix_fmt.bytesperline = layer_config->line_length = > > > > + vpbe_dev->current_timings.xres * 2; > > > > + > > > > + layer->pix_fmt.width = layer_config->xsize = > > > > + vpbe_dev->current_timings.xres; > > > > + layer->pix_fmt.height = layer_config->ysize = > > > > + vpbe_dev->current_timings.yres; > > > > + layer->pix_fmt.sizeimage = > > > > + layer->pix_fmt.bytesperline * layer->pix_fmt.height; > > > > + layer_config->xpos = 0; > > > > + layer_config->ypos = 0; > > > > + layer_config->interlaced = > > > > + vpbe_dev->current_timings.interlaced; > > > > > > You shouldn't reinitialized the format every time the device is opened. > > > The previously set format should be kept. > > > > This strictly followed across drivers? I am Ok if that is the expectation. > > It's how V4L2 drivers should behave. Some drivers might not follow that rule, but that would be a bug :-) Alright. Fixed. > > > > > + > > > > + /* > > > > + * turn off ping-pong buffer and field inversion to fix > > > > + * the image shaking problem in 1080I mode > > > > + */ > > > > + > > > > + if (cfg->interlaced) > > > > + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; > > > > + else > > > > + layer->pix_fmt.field = V4L2_FIELD_NONE; > > > > + > > > > + err = osd_device->ops.set_layer_config(osd_device, > > > > + layer->layer_info.id, > > > > + layer_config); > > > > + if (err < 0) { > > > > + /* Couldn't set layer */ > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Display Manager failed to set osd layer\n"); > > > > + return -EBUSY; > > > > + } > > > > + > > > > + return 0; > > > > +} > > [snip] > > > > > +/* > > > > + * vpbe_display_probe() > > > > + * This function creates device entries by register itself to the > > > > +V4L2 > > > > driver + * and initializes fields of each layer objects > > > > + */ > > > > +static __devinit int vpbe_display_probe(struct platform_device > > > > +*pdev) { > > > > + int i, j = 0, k, err = 0; > > > > + struct vpbe_display *disp_dev; > > > > + struct video_device *vbd = NULL; > > > > + struct vpbe_display_obj *vpbe_display_layer = NULL; > > > > + struct resource *res; > > > > + int irq; > > > > + > > > > + printk(KERN_DEBUG "vpbe_display_probe\n"); > > > > + > > > > + /* Allocate memory for vpbe_display */ > > > > + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); > > > > + if (!disp_dev) { > > > > + printk(KERN_ERR "ran out of memory\n"); > > > > + return -ENOMEM; > > > > + } > > > > + > > > > + /* Allocate memory for four plane display objects */ > > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > > + disp_dev->dev[i] = > > > > + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); > > > > + /* If memory allocation fails, return error */ > > > > + if (!disp_dev->dev[i]) { > > > > + printk(KERN_ERR "ran out of memory\n"); > > > > + err = -ENOMEM; > > > > + goto probe_out; > > > > + } > > > > + spin_lock_init(&disp_dev->dev[i]->irqlock); > > > > + mutex_init(&disp_dev->dev[i]->opslock); > > > > + } > > > > + spin_lock_init(&disp_dev->dma_queue_lock); > > > > + > > > > + err = init_vpbe_layer_objects(i); > > > > + if (err) { > > > > + printk(KERN_ERR "Error initializing vpbe display\n"); > > > > + return err; > > > > + } > > > > + > > > > + /* > > > > + * Scan all the platform devices to find the vpbe > > > > + * controller device and get the vpbe_dev object > > > > + */ > > > > + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, > > > > + vpbe_device_get); > > > > + if (err < 0) > > > > + return err; > > > > + > > > > + /* Initialize the vpbe display controller */ > > > > + if (NULL != vpbe_dev->ops.initialize) { > > > > + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); > > > > + if (err) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing > > > > vpbe\n"); + err = -ENOMEM; > > > > + goto probe_out; > > > > + } > > > > + } > > > > + > > > > + /* check the name of davinci device */ > > > > + if (vpbe_dev->cfg->module_name != NULL) > > > > + strcpy(vpbe_display_videocap.card, > > > > + vpbe_dev->cfg->module_name); > > > > + > > > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > > > + /* Get the pointer to the layer object */ > > > > + vpbe_display_layer = disp_dev->dev[i]; > > > > + /* Allocate memory for video device */ > > > > + vbd = video_device_alloc(); > > > > + if (vbd == NULL) { > > > > + for (j = 0; j < i; j++) { > > > > + video_device_release( > > > > + disp_dev->dev[j]->video_dev); > > > > + } > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of > > > > memory\n"); + err = -ENOMEM; > > > > + goto probe_out; > > > > + } > > > > + /* Initialize field of video device */ > > > > + vbd->release = video_device_release; > > > > + vbd->fops = &vpbe_fops; > > > > + vbd->ioctl_ops = &vpbe_ioctl_ops; > > > > + vbd->minor = -1; > > > > + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; > > > > + vbd->lock = &vpbe_display_layer->opslock; > > > > + > > > > + if (vpbe_dev->current_timings.timings_type & > > > > + VPBE_ENC_STD) > > > > { + vbd->tvnorms = (V4L2_STD_525_60 | > > > > V4L2_STD_625_50); + vbd->current_norm = > > > > + vpbe_dev->current_timings.timings.std_id; > > > > + } else > > > > + vbd->current_norm = 0; > > > > + > > > > + snprintf(vbd->name, sizeof(vbd->name), > > > > + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", > > > > + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, > > > > + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, > > > > + (VPBE_DISPLAY_VERSION_CODE) & 0xff); > > > > + > > > > + /* Set video_dev to the video device */ > > > > + vpbe_display_layer->video_dev = vbd; > > > > + vpbe_display_layer->device_id = i; > > > > + > > > > + vpbe_display_layer->layer_info.id = > > > > + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); > > > > + if (display_buf_config_params.numbuffers[i] == 0) > > > > + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; > > > > + else > > > > + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; > > > > + > > > > + /* Initialize field of the display layer objects */ > > > > + vpbe_display_layer->usrs = 0; > > > > + vpbe_display_layer->io_usrs = 0; > > > > + vpbe_display_layer->started = 0; > > > > + > > > > + /* Initialize prio member of layer object */ > > > > + v4l2_prio_init(&vpbe_display_layer->prio); > > > > + > > > > + /* Register video device */ > > > > + v4l2_info(&vpbe_dev->v4l2_dev, > > > > + "Trying to register VPBE display device.\n"); > > > > + v4l2_info(&vpbe_dev->v4l2_dev, > > > > + "layer=%x,layer->video_dev=%x\n", > > > > + (int)vpbe_display_layer, > > > > + (int)&vpbe_display_layer->video_dev); > > > > + > > > > + err = video_register_device(vpbe_display_layer-> > > > > + video_dev, > > > > + VFL_TYPE_GRABBER, > > > > + vpbe_display_nr[i]); > > > > + if (err) > > > > + goto probe_out; > > > > + /* set the driver data in platform device */ > > > > + platform_set_drvdata(pdev, disp_dev); > > > > + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); > > > > + } > > > > + > > > > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > > > > + if (!res) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, > > > > + "Unable to get VENC interrupt resource\n"); > > > > + err = -ENODEV; > > > > + goto probe_out; > > > > + } > > > > + irq = res->start; > > > > + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, > > > > + disp_dev)) { > > > > + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request > > > > interrupt\n"); + err = -ENODEV; > > > > + goto probe_out; > > > > + } > > > > > > You probably want to get the resources and register the interrupt > > > handler before registering the V4L2 devices, otherwise userspace > > > will be able to open devices before you're done with the initialization. > > > > I do not think anyone would attempt to open the device so soon as the > > boot process is not complete yet. > > Unless you compile the driver as a module. In that case an application could open the device as soon as it gets registered. hal is known to do that. > > > Also, if the irq is registered before, the interrupts start and the > > driver crashes for lack of initialized structure variables. > > Why would an interrupt occur before the device gets opened ? I got the interrupt and the driver crashed because of initialized pointers if I put this before. I will see why and fix it. > > > > > + printk(KERN_DEBUG "Successfully completed the probing of vpbe > > > > + v4l2 > > > > device\n"); + return 0; > > > > +probe_out: > > > > + kfree(disp_dev); > > > > + > > > > + for (k = 0; k < j; k++) { > > > > + /* Get the pointer to the layer object */ > > > > + vpbe_display_layer = disp_dev->dev[k]; > > > > + /* Unregister video device */ > > > > + video_unregister_device(vpbe_display_layer->video_dev); > > > > + /* Release video device */ > > > > + video_device_release(vpbe_display_layer->video_dev); > > > > + vpbe_display_layer->video_dev = NULL; > > > > + } > > > > + return err; > > > > +} > > > > > > [snip] > > > > > > > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); > > > > > > Should this be "TI DM644x" instead ? > > > > This is a common IP for DM644x, Dm355 and Dm365 for which the patches > > will follow after this set. So I think it is OK. > > What about "TI DM644x/DM355/DM365" then ? DMXXX makes it look like it supports all DaVinci chips. > > -- > Regards, > > Laurent Pinchart > From subhasish at mistralsolutions.com Wed Apr 27 00:23:38 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 10:53:38 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <20110426124519.GC5977@suse.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <20110425212056.GA29313@kroah.com> <20110426124519.GC5977@suse.de> Message-ID: <35F38DB5B5C4408EA80AF0DB8A6FA178@subhasishg> >> There should be no build time dependency with this patch >> (the above patch just changes which pool of SRAM the >> allocation happens from) >> >> But, this brings out an important dependency of the patch >> calling platform specific sram allocator functions. There >> has been SRAM allocator consolidation work done by Russell >> and as a result the SRAM allocator API for DaVinci will >> actually change. I earlier had an implementation where I would get the sram memory addresses through the .resource structure and ioremap it in the driver. >>The driver should probably just get sram >> space through platform data so that it doesn't depend on the >> platform specific sram allocation function. Are you suggesting that I go back to that implementation. Also, should I remove the dependency list from the patch comments then. -------------------------------------------------- From: "Greg KH" Sent: Tuesday, April 26, 2011 6:15 PM To: "Nori, Sekhar" Cc: "Greg KH" ; "Subhasish Ghosh" ; ; ; "Watkins, Melissa" ; ; "Andrew Morton" ; "Randy Dunlap" ; "open list" Subject: Re: [PATCH v4 08/11] tty: add pruss SUART driver > On Tue, Apr 26, 2011 at 12:21:04PM +0530, Nori, Sekhar wrote: >> On Tue, Apr 26, 2011 at 02:50:56, Greg KH wrote: >> > On Fri, Apr 22, 2011 at 05:38:26PM +0530, Subhasish Ghosh wrote: >> > > This patch adds support for the TTY compliant >> > > Soft-UART device emulated on PRUSS. >> > > >> > > This patch depends on: >> > > davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. >> > > https://patchwork.kernel.org/patch/615681/ >> >> This is already in mainline. Plus this patch >> doesn't really seem to depend on this commit. >> >> > > davinci: changed SRAM allocator to shared ram. >> > > https://patchwork.kernel.org/patch/549351/ >> >> There should be no build time dependency with this patch >> (the above patch just changes which pool of SRAM the >> allocation happens from) >> >> But, this brings out an important dependency of the patch >> calling platform specific sram allocator functions. There >> has been SRAM allocator consolidation work done by Russell >> and as a result the SRAM allocator API for DaVinci will >> actually change. The driver should probably just get sram >> space through platform data so that it doesn't depend on the >> platform specific sram allocation function. > > Ok, care to fix up the code then? > >> > Who is going to be applying these patches to the tree? >> > >> > Should this driver go through a davinci subtree because of these >> > dependancies? >> >> No, driver and platform changes can be merged separately >> if the above aspect is taken care of. Russell has been >> pushing back on merging driver patches through his tree >> unless absolutely required. > > That's fine, I'll take it through my tree then, care to resolve the > above issue and resend it? > > thanks, > > greg k-h From subhasish at mistralsolutions.com Wed Apr 27 01:39:47 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 12:09:47 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <4DB1A603.2090208@pengutronix.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> <4DB1A603.2090208@pengutronix.de> Message-ID: Hi Mark, - Is it ok to have u32 etc for __iomem cookie ? > + > +s32 pruss_disable(struct device *dev, u8 pruss_num) make it a int function SG -- Ok will do > + > + /* Reset PRU */ > + iowrite32(PRUCORE_CONTROL_RESETVAL, > + &h_pruss->control); > + spin_unlock(&pruss->lock); > + > + return 0; make it a void function? SG -- This should be int, in case of invalid pru num, we ret an error. > +} > +EXPORT_SYMBOL_GPL(pruss_disable); > + > +s32 pruss_enable(struct device *dev, u8 pruss_num) int? SG -- Ok will do > + > + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) > + return -EINVAL; > + > + h_pruss = &pruss_mmap->core[pruss_num]; > + > + /* Reset PRU */ > + spin_lock(&pruss->lock); > + iowrite32(PRUCORE_CONTROL_RESETVAL, &h_pruss->control); no need to lock the ram reset below? SG -- I don't think its required. We just reset the RAM during init and since each pru can only be attached to only one device, the access will be already serialized based upon the pru num. > + /* Reset any garbage in the ram */ > + if (pruss_num == PRUCORE_0) > + for (i = 0; i < PRUSS_PRU0_RAM_SZ; i++) > + iowrite32(0x0, &pruss_mmap->dram0[i]); > + else if (pruss_num == PRUCORE_1) > + for (i = 0; i < PRUSS_PRU1_RAM_SZ; i++) > + iowrite32(0x0, &pruss_mmap->dram1[i]); if you make a array for these +struct pruss_map { + u8 dram0[512]; + u8 res1[7680]; + u8 dram1[512]; + u8 res2[7680]; ..} you don't need the if..else.. SG - The dram/iram is not contiguous, there is a reserved space in between, how do I declare an array for it. > +/* Load the specified PRU with code */ > +s32 pruss_load(struct device *dev, u8 pruss_num, > + u32 *pruss_code, u32 code_size_in_words) > +{ > + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); > + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; > + u32 __iomem *pruss_iram; > + u32 i; > + > + if (pruss_num == PRUCORE_0) > + pruss_iram = (u32 __iomem *)&pruss_mmap->iram0; > + else if (pruss_num == PRUCORE_1) > + pruss_iram = (u32 __iomem *)&pruss_mmap->iram1; > + else same here SG - same here. > +s32 pruss_run(struct device *dev, u8 pruss_num) int? SG - Ok, Will do. > + while (cnt--) { > + temp_reg = ioread32(&h_pruss->control); > + if (((temp_reg & PRUCORE_CONTROL_RUNSTATE_MASK) >> > + PRUCORE_CONTROL_RUNSTATE_SHIFT) == > + PRUCORE_CONTROL_RUNSTATE_HALT) > + break; how long might this take? what about some delay, sleep, or reschedule? SG - This does not take more than 10 to 20 counts, > +s32 pruss_writeb(struct device *dev, u32 offset, u8 pdatatowrite) > +{ > + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); > + void __iomem *paddresstowrite; we usually don't use "p" variable names for pointers SG - Ok, will remove. > +s32 pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val) void function? SG - Ok, will do. > + > +s32 pruss_readb(struct device *dev, u32 offset, u8 *pdatatoread) void? SG - Ok, will do. > +{ > + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); > + void __iomem *paddresstoread; > + > + paddresstoread = pruss->ioaddr + offset ; > + *pdatatoread = ioread8(paddresstoread); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(pruss_readb); > + > +s32 pruss_readb_multi(struct device *dev, u32 offset, > + u8 *pdatatoread, u16 bytestoread) viod? SG - Ok, will do. > +{ > + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); > + u8 __iomem *paddresstoread; > + u16 i; int? SG - Ok, will do. > + return 0; > +} > +EXPORT_SYMBOL_GPL(pruss_readb_multi); > + > +s32 pruss_writel(struct device *dev, u32 offset, > + u32 pdatatowrite) void? SG - Ok, will do. > + return 0; > +} > +EXPORT_SYMBOL_GPL(pruss_writel); > + > +s32 pruss_writel_multi(struct device *dev, u32 offset, > + u32 *pdatatowrite, u16 wordstowrite) void? SG - Ok, will do. > +{ > + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); > + u32 __iomem *paddresstowrite; > + u16 i; > + > + paddresstowrite = pruss->ioaddr + offset; > + > + for (i = 0; i < wordstowrite; i++) > + iowrite32(*pdatatowrite++, paddresstowrite++); memcopy_to_iomem? SG -- I did not understand, could you please elaborate. > + > +s32 pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val) void? SG - Ok, will do. > +} > +EXPORT_SYMBOL_GPL(pruss_rmwl); > + > +s32 pruss_readl(struct device *dev, u32 offset, u32 *pdatatoread) void? or return the read value SG - Ok, will do. From subhasish at mistralsolutions.com Wed Apr 27 01:43:12 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 12:13:12 +0530 Subject: [PATCH v4 03/11] da850: pruss platform specific additions. In-Reply-To: <4DB6A736.3030609@mvista.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-4-git-send-email-subhasish@mistralsolutions.com> <4DB6A736.3030609@mvista.com> Message-ID: <0A4FA4CFE83C4DD4BA36491027E93552@subhasishg> >> #include >> @@ -73,6 +75,7 @@ extern unsigned int da850_max_speed; >> #define DA8XX_DDR2_CTL_BASE 0xb0000000 >> #define DA8XX_ARM_RAM_BASE 0xffff0000 >> #define DA8XX_SHARED_RAM_BASE 0x80000000 >> +#define DA8XX_PRUSS_MEM_BASE 0x01C30000 > > Keep the list sorted please. Also, this macro doesn't seem used outside > devices-da8xx.c, so should be moved there... SG - But would it not be better to have all device addresses at the same place. From subhasish at mistralsolutions.com Wed Apr 27 02:03:08 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 12:33:08 +0530 Subject: [PATCH v4 11/11] da850: pruss CAN board specific additions. In-Reply-To: <4DB6A509.2010009@mvista.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-12-git-send-email-subhasish@mistralsolutions.com> <4DB6A509.2010009@mvista.com> Message-ID: <1F65A5A7A9494B73A22BDA7D64355767@subhasishg> >> +static int __init da850_evm_pruss_can_setup(void) >> +{ >> + int ret, val = 0; >> + void __iomem *cfg_chip3_reg; >> + >> + ret = davinci_cfg_reg_list(da850_evm_pruss_can_pins); >> + if (ret) >> + pr_warning("%s: da850_evm_pruss_can_pins mux setup " >> + "failed:%d\n", __func__, ret); > > Yet you continue to initialize... you should stop here I think. SG - Ok, will ret error. > >> + /* value = 0 to enable the CAN transceiver */ >> + ret = gpio_request_one(DA850_PRUSS_CAN_TRX_PIN, >> + GPIOF_OUT_INIT_LOW, "pruss_can_en"); >> + if (ret) { >> + pr_warning("Cannot setup GPIO %d\n", DA850_PRUSS_CAN_TRX_PIN); >> + gpio_free(DA850_PRUSS_CAN_TRX_PIN); > > Doesn't gpio_request_one() handle freeing GPIO on error? SG - It does handle error, will remove. But I have another problem, Suppose the init failed due to some other reason during probe. How can I do a gpio_free, do I need some kind of deinit for the setup ? From linux at arm.linux.org.uk Wed Apr 27 04:12:52 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Wed, 27 Apr 2011 10:12:52 +0100 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <4DB7C5F7.3080103@pengutronix.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> <4DB1A603.2090208@pengutronix.de> <4DB7C5F7.3080103@pengutronix.de> Message-ID: <20110427091252.GP17290@n2100.arm.linux.org.uk> On Wed, Apr 27, 2011 at 09:29:59AM +0200, Marc Kleine-Budde wrote: > On 04/27/2011 08:39 AM, Subhasish Ghosh wrote: > > - Is it ok to have u32 etc for __iomem cookie ? > > no - "void __iomem *" is "void __iomem *" Actually, it is _provided_ you don't directly dereference it. You can then do pointer arithmetic on it in the usual way - which is about the only valid thing to do with an __iomem pointer. The voidness just acts as an additional check against direct dereferences of this. The important thing though is that the code passes sparse checks. From sshtylyov at mvista.com Wed Apr 27 05:05:18 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 27 Apr 2011 14:05:18 +0400 Subject: [PATCH v4 03/11] da850: pruss platform specific additions. In-Reply-To: <0A4FA4CFE83C4DD4BA36491027E93552@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-4-git-send-email-subhasish@mistralsolutions.com> <4DB6A736.3030609@mvista.com> <0A4FA4CFE83C4DD4BA36491027E93552@subhasishg> Message-ID: <4DB7EA5E.7000507@mvista.com> Hello. On 27-04-2011 10:43, Subhasish Ghosh wrote: >>> #include >>> @@ -73,6 +75,7 @@ extern unsigned int da850_max_speed; >>> #define DA8XX_DDR2_CTL_BASE 0xb0000000 >>> #define DA8XX_ARM_RAM_BASE 0xffff0000 >>> #define DA8XX_SHARED_RAM_BASE 0x80000000 >>> +#define DA8XX_PRUSS_MEM_BASE 0x01C30000 >> Keep the list sorted please. Also, this macro doesn't seem used outside >> devices-da8xx.c, so should be moved there... > SG - But would it not be better to have all device addresses at the same place. They are not at the same place already -- which is intentional. The ones used locally in devices-da8xx.c should be #define'd there. WBR, Sergei From subhasish at mistralsolutions.com Wed Apr 27 05:19:52 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 15:49:52 +0530 Subject: [PATCH v4 03/11] da850: pruss platform specific additions. In-Reply-To: <4DB7EA5E.7000507@mvista.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-4-git-send-email-subhasish@mistralsolutions.com> <4DB6A736.3030609@mvista.com> <0A4FA4CFE83C4DD4BA36491027E93552@subhasishg> <4DB7EA5E.7000507@mvista.com> Message-ID: >>>> #include >>>> @@ -73,6 +75,7 @@ extern unsigned int da850_max_speed; >>>> #define DA8XX_DDR2_CTL_BASE 0xb0000000 >>>> #define DA8XX_ARM_RAM_BASE 0xffff0000 >>>> #define DA8XX_SHARED_RAM_BASE 0x80000000 >>>> +#define DA8XX_PRUSS_MEM_BASE 0x01C30000 > >>> Keep the list sorted please. Also, this macro doesn't seem used outside >>> devices-da8xx.c, so should be moved there... > >> SG - But would it not be better to have all device addresses at the same >> place. > > They are not at the same place already -- which is intentional. The > ones used locally in devices-da8xx.c should be #define'd there. SG -- Ok, Will do From nsekhar at ti.com Wed Apr 27 06:19:02 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 27 Apr 2011 16:49:02 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <35F38DB5B5C4408EA80AF0DB8A6FA178@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <20110425212056.GA29313@kroah.com> <20110426124519.GC5977@suse.de> <35F38DB5B5C4408EA80AF0DB8A6FA178@subhasishg> Message-ID: On Wed, Apr 27, 2011 at 10:53:38, Subhasish Ghosh wrote: > >> There should be no build time dependency with this patch > >> (the above patch just changes which pool of SRAM the > >> allocation happens from) > >> > >> But, this brings out an important dependency of the patch > >> calling platform specific sram allocator functions. There > >> has been SRAM allocator consolidation work done by Russell > >> and as a result the SRAM allocator API for DaVinci will > >> actually change. > > I earlier had an implementation where I would get the sram memory addresses > through the .resource structure and ioremap it in the driver. This is wrong since it assumes the whole SRAM is available for usage by your driver. We already have an allocator for SRAM. > > >>The driver should probably just get sram > >> space through platform data so that it doesn't depend on the > >> platform specific sram allocation function. > > Are you suggesting that I go back to that implementation. No, the platform code should use the SRAM allocator and pass on the allocated memory to the driver. Thanks, Sekhar From subhasish at mistralsolutions.com Wed Apr 27 08:08:19 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 18:38:19 +0530 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <4DB5D452.9050500@grandegger.com> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <1303474267-6344-2-git-send-email-subhasish@mistralsolutions.com> <4DB1A3B7.7060300@pengutronix.de> <4DB5D452.9050500@grandegger.com> Message-ID: <46D523E49EFF489F9B088AE7B9CD7286@subhasishg> > > - Use just *one* value per sysfs file SG - I felt adding entry for each mbx_id will clutter the sysfs. Is it ok to do that. >>> +static u32 pruss_intc_init[19][3] = { >>> + {PRUSS_INTC_POLARITY0, PRU_INTC_REGMAP_MASK, 0xFFFFFFFF}, >>> + {PRUSS_INTC_POLARITY1, PRU_INTC_REGMAP_MASK, 0xFFFFFFFF}, >>> + {PRUSS_INTC_TYPE0, PRU_INTC_REGMAP_MASK, 0x1C000000}, >>> + {PRUSS_INTC_TYPE1, PRU_INTC_REGMAP_MASK, 0}, >>> + {PRUSS_INTC_GLBLEN, 0, 1}, >>> + {PRUSS_INTC_HOSTMAP0, PRU_INTC_REGMAP_MASK, 0x03020100}, >>> + {PRUSS_INTC_HOSTMAP1, PRU_INTC_REGMAP_MASK, 0x07060504}, >>> + {PRUSS_INTC_HOSTMAP2, PRU_INTC_REGMAP_MASK, 0x0000908}, >>> + {PRUSS_INTC_CHANMAP0, PRU_INTC_REGMAP_MASK, 0}, >>> + {PRUSS_INTC_CHANMAP8, PRU_INTC_REGMAP_MASK, 0x00020200}, >>> + {PRUSS_INTC_STATIDXCLR, 0, 32}, >>> + {PRUSS_INTC_STATIDXCLR, 0, 19}, >>> + {PRUSS_INTC_ENIDXSET, 0, 19}, >>> + {PRUSS_INTC_STATIDXCLR, 0, 18}, >>> + {PRUSS_INTC_ENIDXSET, 0, 18}, >>> + {PRUSS_INTC_STATIDXCLR, 0, 34}, >>> + {PRUSS_INTC_ENIDXSET, 0, 34}, >>> + {PRUSS_INTC_ENIDXSET, 0, 32}, >>> + {PRUSS_INTC_HOSTINTEN, 0, 5} >> >> please add "," > > Also a struct to describe each entry would improve readability. > Then you could also use ARRAY_SIZE. SG _ I could not follow this, are you recommending that I create a structure with three variables and then create an array for it. something like: const static struct [] = { { unsigned int reg_base; unsigned int reg_mask; unsigned int reg_val; }, ... }; >>> + value = (PRUSS_CAN_GPIO_SETUP_DELAY * >>> + (priv->clk_freq_pru / 1000000) / 1000) / >>> + PRUSS_CAN_DELAY_LOOP_LENGTH; > > This calculation looks delicate. 64-bit math would be safer. SG - This one works fine. I am dividing it twice to avoid the problem. >>> + pru_can_mask_ints(priv->dev, PRUSS_CAN_TX_PRU_1, false); >>> + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, false); >>> + can_bus_off(ndev); >>> + dev_dbg(priv->ndev->dev.parent, "Bus off mode\n"); >>> + } >>> + >>> + netif_rx(skb); > > You should use netif_receive_skb(skb) here as well. SG - Ok, Will do. > > if (PRUSS_CAN_ISR_BIT_ESI & > priv->can_rx_cntx.intr_stat) { > > Is more readable. SG - Ok, Will do. > > >>> + pru_can_gbl_stat_get(priv->dev, >>> + &priv->can_rx_cntx); >>> + pru_can_err(ndev, >>> + priv->can_rx_cntx.intr_stat, >>> + priv->can_rx_cntx.gbl_stat); > > Please fix bogous indention. SG - Ok, Will do. >>> + >>> + pdata = dev->platform_data; >>> + if (!pdata) { >>> + dev_err(&pdev->dev, "platform data not found\n"); >>> + return -EINVAL; >>> + } >>> + (pdata->setup)(); >> >> no need fot the ( ) SG - Ok, Will do. >>> + } >>> + >>> + priv->ndev = ndev; >>> + priv->dev = dev; >>> + >>> + priv->can.bittiming_const = &pru_can_bittiming_const; >>> + priv->can.do_set_bittiming = pru_can_set_bittiming; >>> + priv->can.do_set_mode = pru_can_set_mode; >>> + priv->can.do_get_state = pru_can_get_state; > > Please remove that callback. It's not needed as state changes are > handled properly. > SG -- Ok, Will do From subhasish at mistralsolutions.com Wed Apr 27 08:15:06 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 18:45:06 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <20110425212056.GA29313@kroah.com> <20110426124519.GC5977@suse.de> <35F38DB5B5C4408EA80AF0DB8A6FA178@subhasishg> Message-ID: >> >>The driver should probably just get sram >> >> space through platform data so that it doesn't depend on the >> >> platform specific sram allocation function. >> >> Are you suggesting that I go back to that implementation. > > No, the platform code should use the SRAM allocator and > pass on the allocated memory to the driver. SG - So, should I call the sram_alloc() in the platform setup function. Something like this: suart_probe() { ... pdata.setup(&sram_aadr); ...use sram data; } suart_remove() { ... pdata.free(&sram_aadr); ... } From arnd at arndb.de Wed Apr 27 08:16:30 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 27 Apr 2011 15:16:30 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> Message-ID: <201104271516.30951.arnd@arndb.de> On Friday 22 April 2011, Subhasish Ghosh wrote: > This patch adds the pruss MFD driver and associated include files. > For details regarding the PRUSS please refer the folowing link: > http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem > > The rational behind the MFD driver being the fact that multiple devices can > be implemented on the cores independently. This is determined by the nature > of the program which is loaded into the PRU's instruction memory. > A device may be de-initialized and another loaded or two different devices > can be run simultaneously on the two cores. > It's also possible, as in our case, to implement a single device on both > the PRU's resulting in improved load sharing. > > Signed-off-by: Subhasish Ghosh Hi Subhasish, This looks like great progress since the last time I looked at the pruss mfd driver, good work there! Thanks to your explanations and the documentation link, I now have a better understanding of what is actually going on here, but I'd still like to understandhow the decision is made regarding what programs are loaded into each PRU and how the MFD cells are set up. Is this a fixed setting for each board that strictly depends on how the external pins are connected, or is it possible to use the same hardware for different purposes based on the program? If I read your code correctly, you hardwire the usage of the two PRUs in the da850 board code, which makes it impossible to use them in different ways even if the hardware supports it. If this is indeed the case, using an MFD device might not be the best option and we should try to come up with a way to dynamically repurpose the PRU with some user interface. Arnd From subhasish at mistralsolutions.com Wed Apr 27 08:18:54 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 18:48:54 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <20110427091252.GP17290@n2100.arm.linux.org.uk> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> <4DB1A603.2090208@pengutronix.de> <4DB7C5F7.3080103@pengutronix.de> <20110427091252.GP17290@n2100.arm.linux.org.uk> Message-ID: <5BAE2D6F9E6047C19B6BE8FF26F7270B@subhasishg> My problem is, I am doing something like this: s32 pruss_writel_multi(struct device *dev, u32 offset, u32 *pdatatowrite, u16 wordstowrite) { struct pruss_priv *pruss = dev_get_drvdata(dev->parent); u32 __iomem *paddresstowrite; u16 i; paddresstowrite = pruss->ioaddr + offset; for (i = 0; i < wordstowrite; i++) iowrite32(*pdatatowrite++, paddresstowrite++); return 0; } So, if I make paddresstowrite as void, it will not work. The above implementation does not generate any sparse errors though. > On Wed, Apr 27, 2011 at 09:29:59AM +0200, Marc Kleine-Budde wrote: >> On 04/27/2011 08:39 AM, Subhasish Ghosh wrote: >> > - Is it ok to have u32 etc for __iomem cookie ? >> >> no - "void __iomem *" is "void __iomem *" > > Actually, it is _provided_ you don't directly dereference it. You can > then do pointer arithmetic on it in the usual way - which is about the > only valid thing to do with an __iomem pointer. The voidness just acts > as an additional check against direct dereferences of this. > > The important thing though is that the code passes sparse checks. From arnd at arndb.de Wed Apr 27 08:25:28 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 27 Apr 2011 15:25:28 +0200 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <46D523E49EFF489F9B088AE7B9CD7286@subhasishg> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <4DB5D452.9050500@grandegger.com> <46D523E49EFF489F9B088AE7B9CD7286@subhasishg> Message-ID: <201104271525.28512.arnd@arndb.de> On Wednesday 27 April 2011, Subhasish Ghosh wrote: > > > > - Use just one value per sysfs file > > SG - I felt adding entry for each mbx_id will clutter the sysfs. > Is it ok to do that. That is probably not much better either. Note also that every sysfs file needs to come with associated documentation in Documentation/ABI/*/ to make sure that users will know exactly how the file is meant to work. Why do you need to export these values in the first place? Is it just for debugging or do you expect all CAN user space to look at this? If it's for debugging, please don't export the files through sysfs. Depending on how useful the data is to regular users, you can still export it through a debugfs file in that case, which has much less strict rules. If the file is instead meant as part of the regular operation of the device, it should not be in debugfs but probably be integrated into the CAN socket interface, so that users don't need to work with two different ways of getting to the device (socket and sysfs). Arnd From subhasish at mistralsolutions.com Wed Apr 27 08:38:03 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 27 Apr 2011 19:08:03 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201104271516.30951.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> <201104271516.30951.arnd@arndb.de> Message-ID: PRU's resulting in improved load sharing. >> >> Signed-off-by: Subhasish Ghosh > > Hi Subhasish, > > This looks like great progress since the last time I looked at the > pruss mfd driver, good work there! > > Thanks to your explanations and the documentation link, I now have > a better understanding of what is actually going on here, but I'd > still like to understandhow the decision is made regarding what programs > are loaded into each PRU and how the MFD cells are set up. > > Is this a fixed setting for each board that strictly depends on how > the external pins are connected, or is it possible to use the same > hardware for different purposes based on the program? SG -- The PRU pins are exposed as is on the main board, we have separate add on daughter cards specific to the protocol implemented on it. > > If I read your code correctly, you hardwire the usage of the two > PRUs in the da850 board code, which makes it impossible to use > them in different ways even if the hardware supports it. If this is > indeed the case, using an MFD device might not be the best option > and we should try to come up with a way to dynamically repurpose > the PRU with some user interface. SG -- It depends upon how the firmware is implemented. If another firmware is downloaded on it, it will emulate another device. Also, if a firmware emulated on it supports switching between devices, that too is possible. Its just a microcontroller, we can do whatever we feel like with it. Both the PRUs have separate instruction/data ram, so both can be used to implement two different devices. From arnd at arndb.de Wed Apr 27 09:05:09 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 27 Apr 2011 16:05:09 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201104271516.30951.arnd@arndb.de> Message-ID: <201104271605.09537.arnd@arndb.de> On Wednesday 27 April 2011, Subhasish Ghosh wrote: > > > > If I read your code correctly, you hardwire the usage of the two > > PRUs in the da850 board code, which makes it impossible to use > > them in different ways even if the hardware supports it. If this is > > indeed the case, using an MFD device might not be the best option > > and we should try to come up with a way to dynamically repurpose > > the PRU with some user interface. > > SG -- It depends upon how the firmware is implemented. If another > firmware is downloaded on it, it will emulate another device. > Also, if a firmware emulated on it supports switching between > devices, > that too is possible. Its just a microcontroller, we can do > whatever we feel like > with it. Both the PRUs have separate instruction/data ram, so > both can be used > to implement two different devices. I see. So the problem that I see with the current code is that you force the system to provide a set of devices from the MFD, which then get passed to the individual drivers (uart and can) that load the firmware they need. Please correct me if I am reading your code wrong. What I suggest you do instead is to have the request_firmware call in the low-level MFD driver, so the user can provide the firmware that he/she wants to use, and then the MFD driver will create the devices that match the firmware loaded into the device. You can easily do that by adding a small header to the firmware format and interpret that header by the MFD driver. When the name of the subdevice is part of that header, the MFD driver does not need to understand the difference, it can simply pass that on when creating its child devices. Arnd From manjunath.hadli at ti.com Wed Apr 27 09:14:03 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 27 Apr 2011 19:44:03 +0530 Subject: [RFC] Media Controller Capture driver for DM365 Message-ID: Introduction ------------ This is the proposal of the initial version of design and implementation of the DM365 VPFE (Video Port Front End) drivers using Media Controloler , the initial version which supports the following: 1) dm365 vpfe 2) ccdc,previewer,resizer,h3a,af blocks 3) supports both continuous and single-shot modes 4) supports user pointer exchange and memory mapped modes for buffer allocation This driver bases its design on Laurent Pinchart's Media Controller Design whose patches for Media Controller and subdev enhancements form the base. The driver also takes copious elements taken from Laurent Pinchart and others' OMAP ISP driver based on Media Controller. So thank you all the people who are responsible for the Media Controller and the OMAP ISP driver. Also, the core functionality of the driver comes from the arago vpfe capture driver of which the CCDC capture was based on V4L2, with other drivers like Previwer, and Resizer. The specification for DM365 can be found here: dm365 vpfe: http://www.ti.com/litv/pdf/sprufg8c The initial version of the driver implementation can be found here: http://git.linuxtv.org/mhadli/v4l-dvb-davinci_devices.git?a=shortlog;h=refs/heads/mc_release Driver Design: Main entities ---------------------------- The hardware modules for dm355,dm365 are mainly ipipe, ipipeif,isif. These hardware modules are generically exposed to the user level in the for of dm6446 style modules. Mainly - ccdc, previewer, resizer in addition to the other histogram and auto color/white balance correction and auto focus modules. 1)MT9P031 sensor module for RAW capture 2)TVP7002 decoder module for HD inputs 3)TVP514x decoder module for SD inputs 4)CCDC capture module 5)Previewer Module for Bayer to YUV conversion 6)Resizer Module for scaling Connection for on-the-fly capture --------------------------------- Mt9P031 ------>CCDC--->Previewer(optional)--->Resizer(optional)--->Video | TVP7002 --- | TV514x --- File Organisation ----------------- main driver files ---------------- drivers/media/video/davinci/vpfe_capture.c include/media/davinci/vpfe_capture.h Instantiatiation of the v4l2 device, media device and all subdevs from here. video Interface files --------------------- drivers/media/video/davinci/vpfe_video.c include/media/davinci/vpfe_video.h Implements all the v4l2 video operations with a generic implementation for continuous and one shot mode. subdev interface files ---------------------- These file implement the subdev interface functionality for each of the subdev entities - mainly the entry points and their implementations in a IP generic way. drivers/media/video/davinci/vpfe_ccdc.c drivers/media/video/davinci/vpfe_previewer.c drivers/media/video/davinci/vpfe_resizer.c drivers/media/video/davinci/vpfe_af.c drivers/media/video/davinci/vpfe_aew.c drivers/media/video/mt9p031.c drivers/media/video/tvp514x.c drivers/media/video/tvp7002.c drivers/media/video/ths7353.c include/media/davinci/vpfe_ccdc.h include/media/davinci/vpfe_previewer.h include/media/davinci/vpfe_resizer.h include/media/davinci/vpfe_af.h include/media/davinci/vpfe_aew.h include/media/tvp514x.h drivers/media/video/tvp514x_regs.h include/media/tvp7002.h drivers/media/video/tvp7002_reg.h core implementation files ------------------------- These provide a core implementation routines for ccdc, ipipeif, ipipe,aew, af, resizer hardware modules. drivers/media/video/davinci/dm365_ccdc.c drivers/media/video/davinci/dm365_ipipe.c drivers/media/video/davinci/dm365_def_para.c drivers/media/video/davinci/dm365_ipipe_hw.c drivers/media/video/davinci/dm3xx_ipipe.c drivers/media/video/davinci/dm365_aew.c drivers/media/video/davinci/dm365_af.c drivers/media/video/davinci/dm365_a3_hw.c include/media/davinci/imp_common.h include/media/davinci/dm365_ccdc.h include/media/davinci/dm365_ipipe.h include/media/davinci/imp_hw_if.h include/media/davinci/dm3xx_ipipe.h include/media/davinci/dm365_aew.h include/media/davinci/dm365_af.h include/media/davinci/dm365_a3_hw.h include/media/davinci/vpfe_types.h drivers/media/video/davinci/dm365_ccdc_regs.h drivers/media/video/davinci/ccdc_hw_device.h drivers/media/video/davinci/dm365_def_para.h drivers/media/video/davinci/dm365_ipipe_hw.h TODOs: ====== 1. Support NV12/YUYV format 2. Support more than 1 buffer for single-shot mode 3. Enable Resizer-B 4. Remove duplicate format setting in ipipe 5. Remove function declarations in dm365_ipipe.c by removing function pointer table 6. Remove function declarations in dm365_ccdc.c by removing function pointer table 7. Make multilevel previewer module ioctl into single level 8. Move kernel only headers into drivers/media/video/davinci from include/media/davinci From nsekhar at ti.com Wed Apr 27 12:50:18 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 27 Apr 2011 23:20:18 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <20110425212056.GA29313@kroah.com> <20110426124519.GC5977@suse.de> <35F38DB5B5C4408EA80AF0DB8A6FA178@subhasishg> Message-ID: Hi Subhasish, On Wed, Apr 27, 2011 at 18:45:06, Subhasish Ghosh wrote: > >> >>The driver should probably just get sram > >> >> space through platform data so that it doesn't depend on the > >> >> platform specific sram allocation function. > >> > >> Are you suggesting that I go back to that implementation. > > > > No, the platform code should use the SRAM allocator and > > pass on the allocated memory to the driver. > > SG - So, should I call the sram_alloc() in the platform setup function. Can you please shed some light on how SRAM is being used in the driver? Looking at the driver, it looks like it is used as a shared buffer between the PRU firmware and kernel. If yes, how do you cope with dynamic allocation of SRAM? That is, how do you inform the firmware what portion of SRAM has been allocated to the driver? Also, usage of SRAM is not required for basic driver function, correct? So, a platform which does not have SRAM to spare for this driver could still have a portion of SDRAM/DDR allocated to be used as the shared buffer? I guess SRAM was used only for lower access times. But it should still be possible to sustain lower baudrates with SDRAM/DDR? Thanks, Sekhar From nsekhar at ti.com Wed Apr 27 13:48:14 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 28 Apr 2011 00:18:14 +0530 Subject: [PATCH] [RFC] davinci_emac: don't WARN_ON cpdma_chan_submit -ENOMEM In-Reply-To: <1303407032-7526-1-git-send-email-bengardiner@nanometrics.ca> References: <1303407032-7526-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: Hi Ben, [CCing netdev maintainer] On Thu, Apr 21, 2011 at 23:00:32, Ben Gardiner wrote: > The current implementation of emac_rx_handler, when the host is > flooded, will result in a great deal of WARNs on the console; due to > the return value of cpdma_chan_submit. This function can error with > EINVAL and ENOMEM; the former when the channel is in an invalid > state, in which case the caller is in error. The latter when a cpdma > descriptor cannot be allocated. > > When flooded, cpdma_chan_submit will return -ENOMEM; > treat the inability to allocate a cpdma descriptor as an rx error > similar in behaviour to when emac_rx_alloc returns NULL. > > No Signed-off-by yet; not complete fix (see below). > CC: Sriramakrishnan A G > > --- > I'm new to network drivers -- and kernel development, really. I'd be > happy to receive feedback on this approach of resolving the -ENOMEM > when flooded. Is there a more conventional approach? Shoud these frames > be recorded as 'dropped'? Yes, that seems to be the right approach. You can look at 8139too.c as an example. dev.stats.rx_dropped is incremented in this case. The same stat needs to be incremented when emac_rx_alloc() fails. > > Testing was performed on da850evm both with and without "net: > davinci_emac: fix spinlock bug with dma channel cleanup" from > Sriramakrishnan A G applied. The behaviour was the same: the emac is > not able to receive any frames after being flooded -- but it can still > send. I would appreciate any insight into the potential causes of the > lockup. This is most likely due to the Rx channel hitting an "End-of-Queue (EOQ)" and the driver failing to restart the DMA on the channel by reprogramming the "Head Descriptor Pointer (HDP)". You can verify that the HDP is indeed NULL for Rx channel 0 (the channel used for Rx in the driver). I can see that there is logic in place to restart the DMA when EOQ is hit in __cpdma_chan_submit(), but there could be some corner case lurking somewhere. > > Best Regards, > Ben Gardiner > > Nanometrics Inc. > http://www.nanometrics.ca > > --- > drivers/net/davinci_emac.c | 6 +++++- > 1 files changed, 5 insertions(+), 1 deletions(-) > > diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c > index 7018bfe..17c48d6 100644 > --- a/drivers/net/davinci_emac.c > +++ b/drivers/net/davinci_emac.c > @@ -1037,8 +1037,12 @@ static void emac_rx_handler(void *token, int len, int status) > recycle: > ret = cpdma_chan_submit(priv->rxchan, skb, skb->data, > skb_tailroom(skb), GFP_KERNEL); > - if (WARN_ON(ret < 0)) > + WARN_ON(ret == -EINVAL); I think we are better off shifting the WARN_ON to cpdma_chan_submit() itself. Let that function decide which errors to warn on and which ones to be just silent on. > + if (ret < 0) { > + if (netif_msg_rx_err(priv) && net_ratelimit()) > + dev_err(emac_dev, "failed cpdma submit\n"); Or rather drop the WARN_ON() altogether since you are anyway printing this rate limited error message. Thanks, Sekhar > dev_kfree_skb_any(skb); > + } > } > > static void emac_tx_handler(void *token, int len, int status) > -- > 1.7.1 > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > From subhasish at mistralsolutions.com Thu Apr 28 02:17:21 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Thu, 28 Apr 2011 12:47:21 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201104271605.09537.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201104271516.30951.arnd@arndb.de> <201104271605.09537.arnd@arndb.de> Message-ID: Hi, >> SG -- It depends upon how the firmware is implemented. If another >> firmware is downloaded on it, it will emulate another device. >> Also, if a firmware emulated on it supports switching between >> devices, >> that too is possible. Its just a microcontroller, we can do >> whatever we feel like >> with it. Both the PRUs have separate instruction/data ram, so >> both can be used >> to implement two different devices. > > I see. So the problem that I see with the current code is that you > force the system to provide a set of devices from the MFD, which > then get passed to the individual drivers (uart and can) that load > the firmware they need. Please correct me if I am reading your code > wrong. > > What I suggest you do instead is to have the request_firmware > call in the low-level MFD driver, so the user can provide the > firmware that he/she wants to use, and then the MFD driver will > create the devices that match the firmware loaded into the device. > > You can easily do that by adding a small header to the firmware > format and interpret that header by the MFD driver. When the name > of the subdevice is part of that header, the MFD driver does not > need to understand the difference, it can simply pass that on > when creating its child devices. I don't understand why loading the firmware should be done at the MFD driver. The user already specifies the device he/she wants to start on the PRU via modprobe. A driver can be inserted, which can download a printer firmware on one PRU and a scanner firmware on the other. This way both cores can be used for separate purposes. I mean, say in a real MFD controller, that will also have two separate cores running on it, just that, the firmware on it would not be downloaded runtime but fused in some non volatile memory. From subhasish at mistralsolutions.com Thu Apr 28 02:22:49 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Thu, 28 Apr 2011 12:52:49 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <4DB81B9E.3000401@pengutronix.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> <4DB1A603.2090208@pengutronix.de> <4DB7C5F7.3080103@pengutronix.de> <20110427091252.GP17290@n2100.arm.linux.org.uk> <5BAE2D6F9E6047C19B6BE8FF26F7270B@subhasishg> <4DB81B9E.3000401@pengutronix.de> Message-ID: <8430D2C750334C98B8E8D1469F2023C7@subhasishg> Hi, On 04/27/2011 03:18 PM, Subhasish Ghosh wrote: > My problem is, I am doing something like this: > > s32 pruss_writel_multi(struct device *dev, u32 offset, > u32 *pdatatowrite, u16 wordstowrite) > { > struct pruss_priv *pruss = dev_get_drvdata(dev->parent); > u32 __iomem *paddresstowrite; > u16 i; > > paddresstowrite = pruss->ioaddr + offset; > > for (i = 0; i < wordstowrite; i++) > iowrite32(*pdatatowrite++, paddresstowrite++); > > return 0; > } > > So, if I make paddresstowrite as void, it will not work. The above > implementation does not generate any sparse errors though. Yes, that why I can work with readb_multi even if I have void __iomen *. But, how do I solve this problem. In the above function, I must use u32 __iomem * From arnd at arndb.de Thu Apr 28 02:35:38 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Thu, 28 Apr 2011 09:35:38 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201104271605.09537.arnd@arndb.de> Message-ID: <201104280935.38787.arnd@arndb.de> On Thursday 28 April 2011 09:17:21 Subhasish Ghosh wrote: > > > > You can easily do that by adding a small header to the firmware > > format and interpret that header by the MFD driver. When the name > > of the subdevice is part of that header, the MFD driver does not > > need to understand the difference, it can simply pass that on > > when creating its child devices. > > I don't understand why loading the firmware should be done at the MFD > driver. > The user already specifies the device he/she wants to start on the PRU via > modprobe. > A driver can be inserted, which can download a printer firmware on one PRU > and a > scanner firmware on the other. This way both cores can be used for separate > purposes. > I mean, say in a real MFD controller, that will also have two separate cores > running on it, > just that, the firmware on it would not be downloaded runtime but fused in > some non volatile memory. Then I must be misreading what your code currently does, because it does not match your explanations. What I see in the platform code is that you create MFD cells for specific devices that get automatically created by the MFD driver. This will cause udev to load the drivers for these devices, which then load the firmware they need. Also, I cannot see how the method you describe would make it possible to the same driver into both units, e.g. when you want to have two serial ports. The reason is that you currently hardcode the PRU number in the driver and that you cannot load a single driver twice. Finally, I'm trying to make sure that whatever solution you come up with will still work when we migrate the code to using a flattened device tree. In that case, you would ideally put the device firmware into the device tree as a property that matches whatever you have connected on the specific board (at least as an option, you can still fall back to request_firmware). You definitely want automatic module loading in that case. Note that using module loading with specific parameters in order to match the hardware is not a recommended procedure any more. The code really needs to work the same way when all drivers are built into the kernel. It should not be hard to use the firmware loading mechanism in the MFD driver to both load the firmware and configure the devices appropriately so we always use the right driver for the currently active devices. Arnd BTW, something is wrong with your email client line wrapping. I've fixed this up manually before when replying, but please find a way to get this right in the future. From arnd at arndb.de Thu Apr 28 02:46:59 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Thu, 28 Apr 2011 09:46:59 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <8430D2C750334C98B8E8D1469F2023C7@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <4DB81B9E.3000401@pengutronix.de> <8430D2C750334C98B8E8D1469F2023C7@subhasishg> Message-ID: <201104280946.59789.arnd@arndb.de> On Thursday 28 April 2011 09:22:49 Subhasish Ghosh wrote: > On 04/27/2011 03:18 PM, Subhasish Ghosh wrote: > > My problem is, I am doing something like this: > > > > s32 pruss_writel_multi(struct device *dev, u32 offset, > > u32 *pdatatowrite, u16 wordstowrite) > > { > > struct pruss_priv *pruss = dev_get_drvdata(dev->parent); > > u32 __iomem *paddresstowrite; > > u16 i; > > > > paddresstowrite = pruss->ioaddr + offset; > > > > for (i = 0; i < wordstowrite; i++) > > iowrite32(*pdatatowrite++, paddresstowrite++); > > > > return 0; > > } > > > > So, if I make paddresstowrite as void, it will not work. The above > > implementation does not generate any sparse errors though. > > Yes, that why I can work with readb_multi even if I have void __iomen *. > > But, how do I solve this problem. In the above function, I must use u32 > __iomem * > I believe you were talking about different things. The code you cited above looks correct to me. For clarity, I would write the loop as for (i = 0; i < wordstowrite; i++) iowrite32(pdatatowrite[i], &paddresstowrite[i]); but your version is just as correct, and I would not complain about it. It is absolutely valid to pass either a 'void __iomem *' or a 'u32 __iomem *' into iowrite32(). What is not valid is to cast between a 'void __iomem *' and a plain 'u32' (no pointer). While that may work in most cases, there are a lot of reasons why that is considered bad style and you should never write code like that. I believe that is what Marc was referring to, but you don't do it in your code. The initial comments that Marc made were about the return value of the accessor functions that always return success. Just make those return void instead. Again, this is unrelated to the pointer types. Arnd