From laurent.pinchart at ideasonboard.com Sun May 1 17:06:23 2011 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Mon, 2 May 2011 00:06:23 +0200 Subject: [RFC] Media Controller Capture driver for DM365 In-Reply-To: References: Message-ID: <201105020006.24338.laurent.pinchart@ideasonboard.com> Hi Manjunath, On Wednesday 27 April 2011 16:14:03 Hadli, Manjunath wrote: > 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. You're welcome. > 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 I'll try to review the code, but I got the DM644x V4L2 display driver on my todo-list first :-) > 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. I'm curious, why do you name the modules CCDC and previewer in the code, when they're called ISIF, IPIPEIF and IPIPE in the documentation ? That's quite confusing. > 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 Why don't you combine the davinci/vpfe_*.c files with the davinci/dm365_*.c files ? What's the reason for splitting functionality between vpfe_ccdc.c and dm365_ccdc.c for instance ? > 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 -- Regards, Laurent Pinchart From Jon.Povey at racelogic.co.uk Sun May 1 20:28:26 2011 From: Jon.Povey at racelogic.co.uk (Jon Povey) Date: Mon, 2 May 2011 02:28:26 +0100 Subject: Davinci DM365 NAND Flash SYNDROME Support In-Reply-To: <201104261242.12174.gasparini@imavis.com> Message-ID: <70E876B0EA86DD4BAF101844BC814DFE093FB7BD25@Cloud.RL.local> Andrea Gasparini wrote: > Hi Michael, hi jon! > I'm resurrecting this thread just to know if anyone landed on > a practical > solution. Sorry, without dates or more reference I can't remember what the problem is that you're looking for a solution for. -- 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 subhasish at mistralsolutions.com Mon May 2 03:34:11 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 2 May 2011 14:04:11 +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? The sram is allocated dynamically in the driver. After allocation, we write the pointer into the PRU, so in case the driver allocates memory form the DDR, it will write this info into the PRU and it will work. But, because of DDR access latencies, the UART will work only for lower baud rates. From ben-i2c at fluff.org Mon May 2 04:36:01 2011 From: ben-i2c at fluff.org (Ben Dooks) Date: Mon, 2 May 2011 10:36:01 +0100 Subject: [PATCH] i2c: davinci: Fix null dereference bug in i2c_davinci_calc_clk_dividers In-Reply-To: <1303579895-9441-1-git-send-email-michael.williamson@criticallink.com> References: <1303579895-9441-1-git-send-email-michael.williamson@criticallink.com> Message-ID: <20110502093601.GW15795@trinity.fluff.org> On Sat, Apr 23, 2011 at 01:31:35PM -0400, Michael Williamson wrote: > 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); This looks like a change in the behaviour of the code, since if there was no pdata supplied we now can try generic_i2c_clock_pulse() with what is probably gpio pin 0. > /* 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; Hmm, not going to work well if the driver is a module, as you'll unload the module, leave the default pointer in and then re-load later at a possibly different address with the old pointer in. > /* 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 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-i2c" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Ben Dooks, ben at fluff.org, http://www.fluff.org/ben/ Large Hadron Colada: A large Pina Colada that makes the universe disappear. From laurent.pinchart at ideasonboard.com Mon May 2 04:58:23 2011 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Mon, 2 May 2011 11:58:23 +0200 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: References: Message-ID: <201105021158.23956.laurent.pinchart@ideasonboard.com> Hi Manjunath, On Tuesday 26 April 2011 16:47:45 Hadli, Manjunath wrote: > 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=69 > f60ed7577ab9184ceabd7efbe5bb3453bf7ef1;hp=a400604f47c339831880c50eda6f6b032 > 21579e3 I've reviewed the same patch, here are my comments. > +/* > + * 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, struct vpbe_display *disp_obj) > +{ > + struct osd_state *osd_device = disp_obj->osd_device; > + struct timespec timevalue; > + struct vpbe_layer *layer; > + unsigned long addr; > + int fid; > + int i; > + > + ktime_get_ts(&timevalue); > + > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > + layer = disp_obj->dev[i]; > + /* If streaming is started in this layer */ > + if (!layer->started) > + continue; What about moving everything above to venc_isr(), and having this function handle a single layer only ? It will lower the max indentation level. I also wonder whether you couldn't share some code between the non-interlaced and the interlaced cases by reorganizing the function body (the fid == 1 code looks quite similar to the non-interlaced code). [snip] > +/** > + * 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 This still sounds a bit cryptic to me. vpbe_try_format() should return a format closest to what the user requested: - If the pixel format is invalid, select a default value (done) - If the field is invalid or not specified, select a default value (partly done, you don't check for default values) - If width and/or height are invalid (including being set to 0), select default values (partly done, you compute width/height based on bytesperline and sizeimage when they're set to 0, and I don't understand why) - If bytesperline is invalid (smaller than the minimum value according to the selected width, or larger than the maximum allowable value), fix it - If sizeimage is invalid (smaller than the minimum value according the the selected height and bytesperline), fix it Is there a need to allow sizeimage values different than height * bytesperline ? > + */ [snip] > +static int vpbe_display_querycap(struct file *file, void *priv, > + struct v4l2_capability *cap) > +{ > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > + > + cap->version = VPBE_DISPLAY_VERSION_CODE; > + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; > + strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver)); > + strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); > + /* check the name of davinci device */ > + if (vpbe_dev->cfg->module_name != NULL) module_name can't be NULL, as it's declared as a char[32]. > + strlcpy(cap->card, vpbe_dev->cfg->module_name, > + sizeof(cap->card)); > + > + 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_layer *layer = fh->layer; > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > + > + 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) { > + /* Fill in the information about format */ > + fmt->fmt.pix = layer->pix_fmt; > + } else { > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > + return -EINVAL; > + } You should do it the other way around. Return -EINVAL when the type isn't OUTPUT, and remove the else. This will increase code readability by decreasing the max indentation level in the common case. > + > + return 0; > +} [snip] > +/** > + * 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) > +{ > + struct vpbe_fh *fh = priv; > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > + int ret; > + > + 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; > + } > + } else { > + return -EINVAL; > + } Other way around here too please. > + > + 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_layer *layer = fh->layer; > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > + int ret; > + > + 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; > + } > + } else { > + return -EINVAL; > + } And here too. > + > + return 0; > +} [snip] > +static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, > + struct platform_device *pdev) > +{ > + struct vpbe_layer *vpbe_display_layer = NULL; > + struct video_device *vbd = NULL; > + int k; > + int err; > + > + /* Allocate memory for four plane display objects */ > + > + disp_dev->dev[i] = > + kmalloc(sizeof(struct vpbe_layer), GFP_KERNEL); You can use kzalloc() and avoid several initializations to 0 below. > + > + /* If memory allocation fails, return error */ > + if (!disp_dev->dev[i]) { > + printk(KERN_ERR "ran out of memory\n"); > + err = -ENOMEM; > + goto free_mem; > + } > + spin_lock_init(&disp_dev->dev[i]->irqlock); > + mutex_init(&disp_dev->dev[i]->opslock); > + > + /* Get the pointer to the layer object */ > + vpbe_display_layer = disp_dev->dev[i]; > + /* Allocate memory for video device */ > + vbd = video_device_alloc(); There's no need to allocate the device dynamically, you can embed struct video_device into struct vpbe_layer (i.e. replace struct video_device *video_dev with struct video_device video_dev inside struct vpbe_layer) (and feel free to rename video_dev to something shorter if needed) > + if (vbd == NULL) { > + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, > + "ran out of memory\n"); > + err = -ENOMEM; > + goto free_mem; > + } > + /* Initialize field of video device */ > + vbd->release = video_device_release; You should then use video_device_release_empty instead of video_device_release. > + vbd->fops = &vpbe_fops; > + vbd->ioctl_ops = &vpbe_ioctl_ops; > + vbd->minor = -1; > + vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; > + vbd->lock = &vpbe_display_layer->opslock; > + > + if (disp_dev->vpbe_dev->current_timings.timings_type & > + VPBE_ENC_STD) { > + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); > + vbd->current_norm = > + disp_dev->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); > + > + /* 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); > + > + return 0; > + > +free_mem: > + for (k = 0; k < i-1; k++) { > + /* Get the pointer to the layer object */ > + vpbe_display_layer = disp_dev->dev[k]; > + /* Release video device */ > + video_device_release(vpbe_display_layer->video_dev); > + vpbe_display_layer->video_dev = NULL; > + /* free layer memory */ > + kfree(disp_dev->dev[k]); > + } This should be moved to the error cleanup part of vpbe_display_probe(). A function that registers a single device shouldn't clean other devices up in case of error. > + > + return -ENODEV; > +} > + > +static __devinit int register_devices(int i, struct vpbe_display *disp_dev, > + struct platform_device *pdev) { This registers a single device, so I would call it vpbe_register_device. > + struct vpbe_layer *vpbe_display_layer = NULL; > + int err; > + int k; > + > + vpbe_display_layer = disp_dev->dev[i]; Please pass disp_dev->dev[i] to the function instead of i. > + v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, > + "Trying to register VPBE display device.\n"); > + v4l2_info(&disp_dev->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, > + -1); > + if (err) > + goto video_register_failed; > + > + vpbe_display_layer->disp_dev = disp_dev; > + /* set the driver data in platform device */ > + platform_set_drvdata(pdev, disp_dev); > + video_set_drvdata(vpbe_display_layer->video_dev, > + vpbe_display_layer); > + > + return 0; > + > +video_register_failed: > + for (k = 0; k < i-1; k++) > + video_unregister_device(vpbe_display_layer->video_dev); > + > + for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { > + /* Get the pointer to the layer object */ > + vpbe_display_layer = disp_dev->dev[k]; > + /* Release video device */ > + video_device_release(vpbe_display_layer->video_dev); > + /* Unregister video device */ > + video_unregister_device(vpbe_display_layer->video_dev); > + vpbe_display_layer->video_dev = NULL; > + /* free layer memory */ > + kfree(disp_dev->dev[k]); > + } This should be moved to the error cleanup part of vpbe_display_probe() as well. > + return -ENODEV; > +} > + > + > + > +/* > + * 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) > +{ > + struct vpbe_display *disp_dev; > + struct resource *res; > + int i; > + int err; > + 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; > + } > + > + spin_lock_init(&disp_dev->dma_queue_lock); > + /* > + * 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, disp_dev, > + vpbe_device_get); > + if (err < 0) > + return err; > + /* Initialize the vpbe display controller */ > + if (NULL != disp_dev->vpbe_dev->ops.initialize) { > + err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, > + disp_dev->vpbe_dev); > + if (err) { > + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, > + "Error initing vpbe\n"); > + err = -ENOMEM; > + goto probe_out; > + } > + } > + > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > + if (init_vpbe_layer(i, disp_dev, pdev)) { > + err = -ENODEV; > + goto probe_out; > + } > + } > + > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > + if (!res) { > + v4l2_err(&disp_dev->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(&disp_dev->vpbe_dev->v4l2_dev, > + "Unable to request interrupt\n"); > + err = -ENODEV; > + goto probe_out; > + } > + > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > + if (register_devices(i, disp_dev, pdev)) { > + err = -ENODEV; > + goto probe_out; > + } > + } > + > + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); > + return 0; > + > +probe_out: You need to unregister the IRQ handler (and move the cleanup code from the two previous functions here). > + kfree(disp_dev); > + return err; > +} [snip] > +/* Function for module initialization and cleanup */ > +module_init(vpbe_display_init); > +module_exit(vpbe_display_cleanup); > + > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); What about "TI DM644x/DM355/DM365" then ? DMXXX makes it look like it supports all DaVinci chips. > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Texas Instruments"); -- Regards, Laurent Pinchart From michael.williamson at criticallink.com Mon May 2 06:38:16 2011 From: michael.williamson at criticallink.com (Michael Williamson) Date: Mon, 02 May 2011 07:38:16 -0400 Subject: [PATCH] i2c: davinci: Fix null dereference bug in i2c_davinci_calc_clk_dividers In-Reply-To: <20110502093601.GW15795@trinity.fluff.org> References: <1303579895-9441-1-git-send-email-michael.williamson@criticallink.com> <20110502093601.GW15795@trinity.fluff.org> Message-ID: <4DBE97A8.4010302@criticallink.com> Hi Mr. Dooks, On 5/2/2011 5:36 AM, Ben Dooks wrote: > On Sat, Apr 23, 2011 at 01:31:35PM -0400, Michael Williamson wrote: >> 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); > > This looks like a change in the behaviour of the code, since if there was > no pdata supplied we now can try generic_i2c_clock_pulse() with what is > probably gpio pin 0. > - There is a check in generic_i2c_clock_pulse() that does not allow for scl_pin pin = 0. I didn't realize gpio pin 0 was a valid gpio when looking at it. Seems like this is an issue as well? - I think this code is always getting called. I don't think there are any platforms that would work using a NULL pdata as i2c_davinci_calc_clk_dividers would bomb. I don't see any platforms in mach-davinci that provide NULL i2c platform_data. Perhaps I'm not looking hard enough. >> /* 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; > > Hmm, not going to work well if the driver is a module, as you'll unload > the module, leave the default pointer in and then re-load later at a > possibly different address with the old pointer in. > I hadn't thought of (or tested) the load->unload->reload scenario. Subtle (well, to me anyway). Thanks for pointing that out. Appreciate your cycles on review. >> /* 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 >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in >> the body of a message to majordomo at vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > From nsekhar at ti.com Mon May 2 12:15:53 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 2 May 2011 22:45:53 +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: On Mon, May 02, 2011 at 14:04:11, Subhasish Ghosh wrote: > >> 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? > > The sram is allocated dynamically in the driver. > After allocation, we write the pointer into the PRU, so in case the > driver allocates memory form the DDR, it will write this info into the > PRU and it will work. But, because of DDR access latencies, the UART > will work only for lower baud rates. Thanks for the clarification. In this case, the driver should use platform callbacks to get/put fast fifo space. In case this callback is not populated by the platform or returns an error, the driver should fall back to allocating from DDR. Thanks, Sekhar From subhasish at mistralsolutions.com Wed May 4 02:13:08 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 4 May 2011 12:43:08 +0530 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <201104271525.28512.arnd@arndb.de> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <4DB5D452.9050500@grandegger.com> <46D523E49EFF489F9B088AE7B9CD7286@subhasishg> <201104271525.28512.arnd@arndb.de> Message-ID: <15AD189F851849F69A011B6F4D1DDB6C@subhasishg> > 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). > CAN requires mail box IDs to be programmed in. But, the socket CAN subsystem supports only software filtering of the mail box IDs. So, the mail box IDs programmed into socket CAN during initialization does not propagate into the hardware. This is planned to be a future implementation in Socket CAN. In our case, we support hardware filtering, to work around with this, Wolfgang (Socket CAN owner) suggested that we implement this using sysfs. These setting are not for debugging, but to program the mail box IDs into the hardware. From subhasish at mistralsolutions.com Wed May 4 02:18:43 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 4 May 2011 12:48:43 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201104280935.38787.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201104271605.09537.arnd@arndb.de> <201104280935.38787.arnd@arndb.de> Message-ID: <2543A75FDDB244D2A148C14BCAA5FD88@subhasishg> Hi Arnd, How about just doing something like: #> echo da8xx_pruss_uart >> firmware.bin i.e just append the device name (from the board file) into the firmware file. In the driver probe, we can parse from the bottom, when it reaches "da8xx_pruss", the rest of the upper data is the firmware and from the full name, we can determine if it's a CAN, UART or any other peripheral. So, based on the platform_data, which the MFD driver received, it can find out which device to initialize. Also, does the line wrapping look any better ? > 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 Wed May 4 08:11:53 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 4 May 2011 15:11:53 +0200 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <15AD189F851849F69A011B6F4D1DDB6C@subhasishg> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <201104271525.28512.arnd@arndb.de> <15AD189F851849F69A011B6F4D1DDB6C@subhasishg> Message-ID: <201105041511.54095.arnd@arndb.de> On Wednesday 04 May 2011, Subhasish Ghosh wrote: > CAN requires mail box IDs to be programmed in. But, the socket > CAN subsystem supports only software filtering of the mail box IDs. > > So, the mail box IDs programmed into socket CAN during initialization > does not propagate into the hardware. This is planned to be a future > implementation in Socket CAN. > > In our case, we support hardware filtering, to work around with this, > Wolfgang (Socket CAN owner) suggested that we implement > this using sysfs. > > These setting are not for debugging, but to program the mail box IDs > into the hardware. Ok, I see. Can you point me to that discussion? Wolfgang, I'm a bit worried by the API being split between sockets and sysfs. The problem is that once the sysfs API is established, users will start relying on it, and you can no longer migrate away from it, even when a later version of the Socket CAN also supports setting through a different interface. What is the current interface to set mail box IDs in software? How hard would it be to implement that feature in Socket CAN? Is that something that Subhasish or someone else could to as a prerequisite to merging the driver? Arnd From arnd at arndb.de Wed May 4 08:44:19 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 4 May 2011 15:44:19 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <2543A75FDDB244D2A148C14BCAA5FD88@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201104280935.38787.arnd@arndb.de> <2543A75FDDB244D2A148C14BCAA5FD88@subhasishg> Message-ID: <201105041544.19726.arnd@arndb.de> On Wednesday 04 May 2011, Subhasish Ghosh wrote: > How about just doing something like: > > #> echo da8xx_pruss_uart >> firmware.bin > > i.e just append the device name (from the board file) into the firmware > file. That sounds fine to me. I would put a header in the beginning, but feel free to use whatever format you find useful there. > In the driver probe, we can parse from the bottom, when it reaches > "da8xx_pruss", the rest of the upper data is the firmware and from > the full name, we can determine if it's a CAN, UART or any other > peripheral. > > So, based on the platform_data, which the MFD driver received, > it can find out which device to initialize. In my opinion, the MFD driver should not know the possible values at all, or look at platform_data for the children, but take all that information from the firmware file. The only thing that the MFD driver needs to know is how many PRUs there are and how they are connected to the host bus (memory regions, irqs, clocks, ...). It can then load firmware files, either one for the entire MFD or one per PRU core (whatever works best for you) and create child devices with the information it finds in there about what code to load into each PRU and what drivers to load for them. When you prepend the "da8xx_pruss_" string to the name you find in the firmware file, the pruss driver can be sure that it does not accidentally load a different module, e.g. when a malicious user was able to exchange the firmware file. > Also, does the line wrapping look any better ? Looks good now, yes. Arnd From nsekhar at ti.com Wed May 4 09:38:30 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 4 May 2011 20:08:30 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <2543A75FDDB244D2A148C14BCAA5FD88@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201104271605.09537.arnd@arndb.de> <201104280935.38787.arnd@arndb.de> <2543A75FDDB244D2A148C14BCAA5FD88@subhasishg> Message-ID: Hi Subhasish, On Wed, May 04, 2011 at 12:48:43, Subhasish Ghosh wrote: > Hi Arnd, > > How about just doing something like: > > #> echo da8xx_pruss_uart >> firmware.bin > > i.e just append the device name (from the board file) into the firmware > file. > > In the driver probe, we can parse from the bottom, when it reaches > "da8xx_pruss", the rest of the upper data is the firmware and from > the full name, we can determine if it's a CAN, UART or any other > peripheral. > > So, based on the platform_data, which the MFD driver received, > it can find out which device to initialize. If you are defining a firmware header, it may be worthwhile making sure you include information on features of the device being supported (I assume there will be differences in CAN features if the firmware uses both PRUs to support CAN versus UART on one PRU and CAN on another PRU). The MFD driver can parse this information and pass this on to the UART or CAN drivers. Thanks, Sekhar > > Also, does the line wrapping look any better ? > > > > 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 Wed May 4 09:48:36 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 4 May 2011 16:48:36 +0200 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <4DC163D7.9010309@grandegger.com> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <201105041511.54095.arnd@arndb.de> <4DC163D7.9010309@grandegger.com> Message-ID: <201105041648.37199.arnd@arndb.de> On Wednesday 04 May 2011, Wolfgang Grandegger wrote: > On 05/04/2011 03:11 PM, Arnd Bergmann wrote: > > Wolfgang, I'm a bit worried by the API being split between sockets and sysfs. > > The problem is that once the sysfs API is established, users will start > > relying on it, and you can no longer migrate away from it, even when > > a later version of the Socket CAN also supports setting through a different > > interface. What is the current interface to set mail box IDs in software? > > Note that this CAN controller is *very* special. It cannot handle all > CAN id's due to a lack or resources. The PRUSS firmware is able to > manage just up to 8 different CAN identifiers out of the usual 4096 > (12-bit) or even more for the extended CAN ids using 29 bits. So for other controllers, they can simply access every ID within the range (12 or 29 bits), but there is no need to configure? What are these IDs for? Do they identify a local port, a remote address, a connection, or something else? > There is > no other CAN controller with such rather serious limitations and > therefore there exists also no appropriate interface. I think using > sysfs is OK for such device-specific parameters, at least for the time > being. It sounds like it's not very scalable, especially since the implementation is done completely in firmware. Imagine a new firmware version suddenly supporting 256 ids instead of 8 -- you'd then have to create 256 sysfs files to be compatible if I understand you correctly. > > How hard would it be to implement that feature in Socket CAN? > > CAN controllers usually provide some kind of hardware CAN id filtering, > but in a very hardware dependent way. A generic interface may be able to > handle the PRUSS restrictions as well. CAN devices are usually > configured through the netlink interface. e.g. > > $ ip link set can0 up type can bitrate 125000 > > and such a common interface would be netlink based as well. Agreed. > > Is that something that Subhasish or someone else could to as a prerequisite > > to merging the driver? > > Any ideas on how to handle hardware filtering in a generic way are > welcome. I will try to come up with a proposal sooner than later. It sounds to me like the best solution would be change the firmware to lift that restriction and simply allow all IDs, in case it's not actually a hardware limitation (which sounds unlikely). If that's not possible, maybe it's possible to define a generic filtering interface using netlink, and then either do it completely in the kernel, or using the hardware support. Arnd From subhasish at mistralsolutions.com Thu May 5 08:25:27 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Thu, 5 May 2011 18:55:27 +0530 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> <201104280935.38787.arnd@arndb.de> <2543A75FDDB244D2A148C14BCAA5FD88@subhasishg> Message-ID: <613E2AF627DE4ABE92AD42D5D9E1BFE5@subhasishg> I am a bit lost, If I understand correctly, there are two problems 1. In case of udev based driver loading, we will end up loading both the drivers and the associated firmware. This is not acceptable because both UART & CAN use PRU0 & PRU1. 2. How do we switch between devices at run time. This is required because MFD devices should be capable of doing that. Ok, if that understanding is somewhat correct, then, what we are trying to do is load the firmware from the MFD driver itself. So, in case of a switch between device, say from UART to CAN, all the user has to do is copy a new firmware. This firmware should contain some info regarding the device, like say, device name & prunum. So, in case a device requires both PRUs, then the firmware would download for both PRUs. In case a device requires only one PRU, then it can be downloaded only into the requested PRU. So, the other PRU remains open for any other firmware/device. After loading the firmware, the MFD device should create the platform devices based upon the firmware. So, if only one PRU is used for UART, then the other PRU can be used for CAN and the MFD should create two platform_devices. If the UART uses both the PRUs, then the MFD should download the firmware into both the PRUs but create only one platform_device. But, in this case how do we download two different firmware on two different PRUs. One way is to keep the firmware files as its now, but add another file say firmware-config.txt, consisting a list of parameters like, START MFD_ID0#0 MFD_ID0_PRU_NUM#PRU0 MFD_ID1#1 MFD_ID1_PRU_NUM#PRU1 END OR START MFD_ID0#0 MFD_ID0_PRU_NUM#PRU0:PRU1 END So, the MFD driver can do a request_firmware on this file first, parse the device info and decide to request the main firmware. This becomes something like the devices table that we have on PPC. Also, if PRU0 is loaded, we need some way of maintaining the usage_count, So that the PRU0 is not loaded again accidently. We can increase the usage_count from the MFD driver. But, to decrement the usage_count, we can add an API which will be called when the CAN or UART driver is rmmod. Now how do we handle switch between devices. We can either do a rmmod of the MFD driver, then again modprobe it. So, the request_firmware is run again and the appropriate device is loaded based upon the changed firmware-config.txt. But, this does not look good, say what if the MFD driver is in-build. Another way is that we can add a sysfs entry, which when written with anything can re-evaluate the firmware_config. What the hell, does anything make any sense ? > Hi Subhasish, > > On Wed, May 04, 2011 at 12:48:43, Subhasish Ghosh wrote: >> Hi Arnd, >> >> How about just doing something like: >> >> #> echo da8xx_pruss_uart >> firmware.bin >> >> i.e just append the device name (from the board file) into the firmware >> file. >> >> In the driver probe, we can parse from the bottom, when it reaches >> "da8xx_pruss", the rest of the upper data is the firmware and from >> the full name, we can determine if it's a CAN, UART or any other >> peripheral. >> >> So, based on the platform_data, which the MFD driver received, >> it can find out which device to initialize. > > If you are defining a firmware header, it may be > worthwhile making sure you include information > on features of the device being supported (I > assume there will be differences in CAN features if > the firmware uses both PRUs to support CAN versus > UART on one PRU and CAN on another PRU). > > The MFD driver can parse this information and > pass this on to the UART or CAN drivers. > > Thanks, > Sekhar > >> >> Also, does the line wrapping look any better ? >> >> >> > 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 May 5 09:12:18 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Thu, 5 May 2011 16:12:18 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <613E2AF627DE4ABE92AD42D5D9E1BFE5@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <613E2AF627DE4ABE92AD42D5D9E1BFE5@subhasishg> Message-ID: <201105051612.18522.arnd@arndb.de> On Thursday 05 May 2011, Subhasish Ghosh wrote: > I am a bit lost, > > If I understand correctly, there are two problems > > 1. In case of udev based driver loading, we will end up > loading both the drivers and the associated firmware. > This is not acceptable because both UART & CAN > use PRU0 & PRU1. > > 2. How do we switch between devices at run time. > This is required because MFD devices should be > capable of doing that. > > > Ok, if that understanding is somewhat correct, then, what we are > trying to do is load the firmware from the MFD driver itself. I would describe the problem differently: You hardcode the usage of the pruss in the platform code, even though it is completely defined by the software you load into it. I think it is essential that all the configuration of the pruss is dynamic and not hardcoded in the platform code, so that it is at all possible to load other firmware into it. Describing the device in the firmware file instead of the platform code is one way to get there, but there may be other ways that you could come up with. > So, in case of a switch between device, say from UART to CAN, > all the user has to do is copy a new firmware. > > This firmware should contain some info regarding the device, like > say, device name & prunum. Right. > So, in case a device requires both PRUs, then the firmware would > download for both PRUs. > > In case a device requires only one PRU, then it can be downloaded > only into the requested PRU. So, the other PRU remains open for > any other firmware/device. > > After loading the firmware, the MFD device should create the > platform devices based upon the firmware. > > So, if only one PRU is used for UART, then the other PRU can be > used for CAN and the MFD should create two platform_devices. > > If the UART uses both the PRUs, then the MFD should download > the firmware into both the PRUs but create only one platform_device. As I mentioned, I did not have a good idea for how to abstract the fact that there may be either one or two child devices, depending on the firmware. A possible way to deal with this would be to have only a single firmware image files that contains the code for both PRUs, with a header indicating how many devices there should be and what they are called. > But, in this case how do we download two different firmware > on two different PRUs. One way is to keep the firmware files > as its now, but add another file say firmware-config.txt, > consisting a list of parameters like, > > START > MFD_ID0#0 > MFD_ID0_PRU_NUM#PRU0 > MFD_ID1#1 > MFD_ID1_PRU_NUM#PRU1 > END > > OR > > START > MFD_ID0#0 > MFD_ID0_PRU_NUM#PRU0:PRU1 > END > > So, the MFD driver can do a request_firmware on this file first, > parse the device info and decide to request the main firmware. > This becomes something like the devices table that we have on > PPC. Yes, this would be another option. I generally don't like parsing data in the kernel, but it's not completely unrealistic. > Also, if PRU0 is loaded, we need some way of maintaining the > usage_count, So that the PRU0 is not loaded again accidently. > We can increase the usage_count from the MFD driver. > But, to decrement the usage_count, we can add an API > which will be called when the CAN or UART driver is rmmod. Not necesarily. You can make the interface so that the child device gets registered after the firmware got loaded, but unregistered if you start loading new firmware. We have hotplug support for a lot of devices, and it's not hard to write the serial/can/... drivers in a way that allows the underlying device to go away. You should not actually need to unload the device driver. E.g. when the drivers are built into the kernel, you can still remove the device and put it back in there using the ->probe and ->remove callbacks of the platform driver. > Now how do we handle switch between devices. > > We can either do a rmmod of the MFD driver, then again modprobe it. > So, the request_firmware is run again and the appropriate device is > loaded based upon the changed firmware-config.txt. > > But, this does not look good, say what if the MFD driver is in-build. > Another way is that we can add a sysfs entry, which when written > with anything can re-evaluate the firmware_config. Yes, I think that a sysfs entry would be good here, basically to reset the entire unit and remove the child devices, followed by a new call to request_firmware. Instead of passing a configuration file into the MFD driver and calling it a firmware, I can see three other options that I believe would be nicer: 1. have a single firmware blob that describes the child devices and the code that is to be loaded into both units. If they are acting as one device, either make sure you only create one child node, or create one with a name that no driver binds to. 2. Call request_firmware separately for the two child devices, and configure them separately based on the information in the firmware files. In the case where they should act as a single device, call the children e.g. "pruss-uart-a" and "pruss-uart-b", then bind to both names in the pruss-uart drivers and only start using the device when you have both nodes for the same parent. 3. Do the configuration through sysfs files in the MFD device node, which then cause the creation of the child devices. This means you need extra user space scripts to write the addititonal files, but is also the most flexible way. Arnd From subhasish at mistralsolutions.com Mon May 9 08:39:37 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 9 May 2011 19:09:37 +0530 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: <034A6447E0D54737B5CEE009A4D20B37@subhasishg> Hi Greg, I am observing a RX FIFO sized data loss problem with my driver. I observed that if I start the TX before the RX, then there are no errors, but, if I start a TX after a RX, then I observe the error (FIFO sized data chunks missing). Say, for example, if I use all three UARTS and on all three I start RX, then for every TX started I would observe one error on all the RX data buffer. So, if I start only two TX, then I will see only two errors on all the three RX UARTS. >From this observation I concluded that somehow the TX start was effecting the RX. So, I disabled the complete TX section in the driver, but even when the complete TX was disabled I observed the same errors in RX. >From here I concluded that the error was happening before my drivers TX routine was getting called. I traced it down to the below function, this is a sub-system specific function in the file serial-core.c static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; int mctrl; spin_lock_irq(&uport->lock); uport->ops->enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); if (mctrl & TIOCM_CAR) { return 1; } return 0; } In this function I moved the "return 1" to the beginning of the function, this solved the bug that we are having. There was no data loss. I think, the two spin locks used in this function is somehow effecting the RX. I then modified this function to as follows and the error is not observed anymore. static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; int mctrl; unsigned long flags = 0; spin_lock_irqsave(&uport->lock, flags); uport->ops->enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irqrestore(&uport->lock, flags); if (mctrl & TIOCM_CAR) { return 1; } return 0; } Is this a BUG in the TTY sub-system or am I doing something wrong in my driver. I also changed one driver function to as follows: static u32 pruss_suart_get_mctrl(struct uart_port *port) { return TIOCM_CAR; } Best Regards, Subhasish Ghosh -------------------------------------------------- From: "Subhasish Ghosh" Sent: Friday, April 22, 2011 5:38 PM To: Cc: ; ; ; ; "Subhasish Ghosh" ; "Greg Kroah-Hartman (maintainer:TTY LAYER,commit_signer:2/4=50%,commit_signer:1/2=50%)" ; "Andrew Morton (commit_signer:1/4=25%)" ; "Randy Dunlap (commit_signer:1/4=25%)" ; "open list" Subject: [PATCH v4 08/11] tty: add pruss SUART driver > 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< + OMAPL_MCASP_PDIR_AHCLKR_OUTPUT< + OMAPL_MCASP_PDIR_ACLKR_OUTPUT< + OMAPL_MCASP_PDIR_AFSX_OUTPUT< + OMAPL_MCASP_PDIR_AHCLKX_OUTPUT< + OMAPL_MCASP_PDIR_ACLKX_OUTPUT< + > +/* > + * This enum is used to specify the direction of the channel in UART > + */ > +enum SUART_CHN_DIR { > + SUART_CHN_TX = 1, > + SUART_CHN_RX = 2 > +}; > + > +/* > + * This enum is used to specify the state of the channel in UART. It > + * is either enabled or disabled. > + */ > +enum SUART_CHN_STATE { > + SUART_CHN_DISABLED = 0, > + SUART_CHN_ENABLED = 1 > +}; > + > +enum SUART_EN_BITSPERCHAR { > + ePRU_SUART_DATA_BITS6 = 8, > + ePRU_SUART_DATA_BITS7, > + ePRU_SUART_DATA_BITS8, > + ePRU_SUART_DATA_BITS9, > + ePRU_SUART_DATA_BITS10, > + ePRU_SUART_DATA_BITS11, > + ePRU_SUART_DATA_BITS12 > +}; > + > +enum SUART_EN_UARTNUM { > + ePRU_SUART_NUM_1 = 1, > + ePRU_SUART_NUM_2, > + ePRU_SUART_NUM_3, > + ePRU_SUART_NUM_4, > + ePRU_SUART_NUM_5, > + ePRU_SUART_NUM_6, > + ePRU_SUART_NUM_7, > + ePRU_SUART_NUM_8 > +}; > + > +enum SUART_EN_UARTTYPE { > + ePRU_SUART_HALF_TX = 1, > + ePRU_SUART_HALF_RX, > + ePRU_SUART_FULL_TX_RX, > + ePRU_SUART_HALF_TX_DISABLED = 4, > + ePRU_SUART_HALF_RX_DISABLED = 8 > +}; > + > +enum SUART_EN_TXCHANNEL { > + ePRU_SUART_TX_CH0 = 0, > + ePRU_SUART_TX_CH1, > + ePRU_SUART_TX_CH2, > + ePRU_SUART_TX_CH3, > + ePRU_SUART_TX_CH4, > + ePRU_SUART_TX_CH5, > + ePRU_SUART_TX_CH6, > + ePRU_SUART_TX_CH7 > +}; > + > +enum SUART_EN_RXCHANNEL { > + ePRU_SUART_RX_CH0 = 0, > + ePRU_SUART_RX_CH1, > + ePRU_SUART_RX_CH2, > + ePRU_SUART_RX_CH3, > + ePRU_SUART_RX_CH4, > + ePRU_SUART_RX_CH5, > + ePRU_SUART_RX_CH6, > + ePRU_SUART_RX_CH7 > +}; > + > +enum SUART_EN_UART_STATUS { > + ePRU_SUART_UART_FREE = 0, > + ePRU_SUART_UART_IN_USE > +}; > + > +struct pru_suart_cnh_cntrl_config1 { > + u32 mode:2; > + u32 service_req:1; > + u32 asp_id:2; > + u32 reserved1:3; > + u32 serializer_num:4; > + u32 reserved2:4; > + u32 presacler:10; > + u32 over_sampling:2; > + u32 framing_mask:1; > + u32 break_mask:1; > + u32 timeout_mask:1; > + u32 reserved3:1; > +}; > + > +struct pru_suart_chn_config2_status { > + u32 bits_per_char:4; > + u32 reserved1:4; > + u32 data_len:4; > + u32 reserved2:4; > + u32 txrx_ready:1; > + u32 txrx_complete:1; > + u32 txrx_error:1; > + u32 txrx_underrun:1; > + u32 framing_error:1; > + u32 break_error:1; > + u32 timeout_error:1; > + u32 reserved3:8; > + u32 chn_state:1; > +}; > + > +struct pru_suart_regs_ovly { > + struct pru_suart_cnh_cntrl_config1 ch_ctrl_config1; > + struct pru_suart_chn_config2_status ch_config2_txrx_status; > + u32 ch_txrx_data; > + u32 reserved1; > +}; > + > +struct pru_suart_tx_cntx_priv { > + u32 asp_xsrctl_base; > + u32 asp_xbuf_base; > + u16 buff_addr; > + u8 buff_size; > + u8 bits_loaded; > +}; > + > +struct pru_suart_rx_cntx_priv { > + u32 asp_rbuf_base; > + u32 asp_rsrctl_base; > + u32 reserved1; > + u32 reserved2; > + u32 reserved3; > + u32 reserved4; > +}; > + > +struct suart_config { > + u8 tx_serializer; > + u8 rx_serializer; > + u16 tx_clk_divisor; > + u16 rx_clk_divisor; > + u8 tx_bits_per_char; > + u8 rx_bits_per_char; > + u8 oversampling; > + u8 bi_inter_mask; > + u8 fe_intr_mask; > +}; > + > +struct suart_handle { > + u16 uart_num; > + u16 uart_type; > + u16 uart_tx_channel; > + u16 uart_rx_channel; > + u16 uart_status; > +}; > + > +struct pruss_suart_iomap { > + void __iomem *mcasp_io_addr; > + void *p_fifo_buff_phys_base; > + void *p_fifo_buff_virt_base; > +}; > + > +struct pruss_suart_initparams { > + u32 tx_baud_value; > + u32 rx_baud_value; > + u32 oversampling; > +}; > + > +/* MCASP */ > +struct omapl_mcasp_regs_ovly { > + u32 revid; > + u32 rsvd0[3]; > + u32 pfunc; > + u32 pdir; > + u32 pdout; > + u32 pdin; > + u32 pdclr; > + u32 rsvd1[8]; > + u32 gblctl; > + u32 amute; > + u32 dlbctl; > + u32 ditctl; > + u32 rsvd2[3]; > + u32 rgblctl; > + u32 rmask; > + u32 rfmt; > + u32 afsrctl; > + u32 aclkrctl; > + u32 ahclkrctl; > + u32 rtdm; > + u32 rintctl; > + u32 rstat; > + u32 rslot; > + u32 rclkchk; > + u32 revtctl; > + u32 rsvd3[4]; > + u32 xgblctl; > + u32 xmask; > + u32 xfmt; > + u32 afsxctl; > + u32 aclkxctl; > + u32 ahclkxctl; > + u32 xtdm; > + u32 xintctl; > + u32 xstat; > + u32 xslot; > + u32 xclkchk; > + u32 xevtctl; > + u32 rsvd4[12]; > + u32 ditcsra[6]; > + u32 ditcsrb[6]; > + u32 ditudra[6]; > + u32 ditudrb[6]; > + u32 rsvd5[8]; > + u32 srctl0; > + u32 srctl1; > + u32 srctl2; > + u32 srctl3; > + u32 srctl4; > + u32 srctl5; > + u32 srctl6; > + u32 srctl7; > + u32 srctl8; > + u32 srctl9; > + u32 srctl10; > + u32 srctl11; > + u32 srctl12; > + u32 srctl13; > + u32 srctl14; > + u32 srctl15; > + u32 rsvd6[16]; > + u32 xbuf[16]; > + u32 rsvd7[16]; > + u32 rbuf[16]; > +}; > + > +/* > + * SUART Config regs > + */ > +struct suart_struct_pru_regs { > + u16 chn_ctrl; > + u16 chn_config1; > + u16 chn_config2; > + u16 chn_txrx_status; > + u32 chn_txrx_data; > +}; > + > +extern s32 pru_softuart_init(struct device *dev, > + struct pruss_suart_initparams *, > + const u8 *pru_suart_emu_code, u32 fw_size, > + u32 clk_rate_pruss, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern s32 pru_softuart_open(struct suart_handle *h_suart); > + > +extern s32 pru_softuart_close(struct suart_handle *h_uart); > + > +extern s32 pru_softuart_setbaud(struct device *dev, > + struct suart_handle *h_uart, > + u16 tx_clk_divisor, u16 rx_clk_divisor); > + > +extern s32 pru_softuart_setdatabits(struct device *dev, > + struct suart_handle *h_uart, > + u16 tx_data_bits, u16 rx_data_bits); > + > +extern s32 pru_softuart_setconfig(struct device *dev, > + struct suart_handle *h_uart, > + struct suart_config *config_uart); > + > +extern s32 pru_softuart_getconfig(struct device *dev, > + struct suart_handle *h_uart, > + struct suart_config *config_uart); > + > +extern s32 pru_softuart_pending_tx_request(struct device *dev); > + > +extern s32 pru_softuart_write(struct device *dev, > + struct suart_handle *h_uart, > + u32 *pt_tx_data_buf, u16 data_len); > + > +extern s32 pru_softuart_read(struct device *dev, > + struct suart_handle *h_uart, > + u32 *pt_data_buf, u16 data_len); > + > +extern s32 suart_intr_clrmask(struct device *dev, u16 uart_num, > + u32 txrxmode, > + u32 intrmask); > + > +extern s32 pru_softuart_clr_tx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_tx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_clr_rx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_rx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, > + u16 *txrx_flag); > + > +extern s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, > + u32 txrxmode); > + > +extern s32 suart_intr_getmask(struct device *dev, u16 uart_num, > + u32 txrxmode, > + u32 intrmask); > + > +extern s32 suart_intr_setmask(struct device *dev, u16 uart_num, > + u32 txrxmode, u32 intrmask); > + > +extern s32 pru_softuart_get_tx_data_len(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_rx_data_len(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 suart_arm_to_pru_intr(struct device *dev, u16 uart_num); > + > +extern void pru_mcasp_deinit(void); > + > +extern s32 pru_softuart_read_data(struct device *dev, > + struct suart_handle *h_uart, > + u8 *p_data_buffer, s32 max_len, > + u32 *pdata_read); > + > +extern s32 pru_softuart_stop_receive(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 suart_pru_to_host_intr_enable(struct device *dev, > + u16 uart_num, > + u32 txrxmode, s32 flag); > + > +extern void pru_set_fifo_timeout(struct device *dev, s16 timeout); > + > +extern void suart_mcasp_config(u32 tx_baud_value, > + u32 rx_baud_value, u32 oversampling, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern void suart_mcasp_reset(struct pruss_suart_iomap *pruss_ioaddr); > + > +extern short suart_asp_baud_set(u32 tx_baud_value, > + u32 rx_baud_value, u32 oversampling, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern short suart_asp_serializer_deactivate(u16 sr_num, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern void suart_mcasp_tx_serialzier_set(u32 serializer_num, > + struct pruss_suart_iomap *pruss_ioaddr); > +#endif > diff --git a/drivers/tty/serial/pruss_suart_api.c > b/drivers/tty/serial/pruss_suart_api.c > new file mode 100644 > index 0000000..15178f5 > --- /dev/null > +++ b/drivers/tty/serial/pruss_suart_api.c > @@ -0,0 +1,1710 @@ > +/* > + * 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 > +#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 alan at lxorguk.ukuu.org.uk Mon May 9 08:46:10 2011 From: alan at lxorguk.ukuu.org.uk (Alan Cox) Date: Mon, 9 May 2011 14:46:10 +0100 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <034A6447E0D54737B5CEE009A4D20B37@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <034A6447E0D54737B5CEE009A4D20B37@subhasishg> Message-ID: <20110509144610.6b4f7090@lxorguk.ukuu.org.uk> > I then modified this function to as follows and the error is not observed > anymore. That looks like you are somehow calling uart_carrier_raised somewhere with interrupts disabled ? Alan From subhasish at mistralsolutions.com Mon May 9 08:50:17 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 9 May 2011 19:20:17 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <20110509144610.6b4f7090@lxorguk.ukuu.org.uk> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com><1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com><034A6447E0D54737B5CEE009A4D20B37@subhasishg> <20110509144610.6b4f7090@lxorguk.ukuu.org.uk> Message-ID: <3A9C5CD56396439DBF825BB4E5779DB9@subhasishg> >> I then modified this function to as follows and the error is not observed >> anymore. > > That looks like you are somehow calling uart_carrier_raised somewhere > with interrupts disabled ? > I am not calling this function in my driver atall, this is getting called by tty_port.c int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp) { /* Probe the carrier. For devices with no carrier detect this will always return true */ cd = tty_port_carrier_raised(port); if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } From alan at lxorguk.ukuu.org.uk Mon May 9 08:55:34 2011 From: alan at lxorguk.ukuu.org.uk (Alan Cox) Date: Mon, 9 May 2011 14:55:34 +0100 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <3A9C5CD56396439DBF825BB4E5779DB9@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> <034A6447E0D54737B5CEE009A4D20B37@subhasishg> <20110509144610.6b4f7090@lxorguk.ukuu.org.uk> <3A9C5CD56396439DBF825BB4E5779DB9@subhasishg> Message-ID: <20110509145534.2677dfa1@lxorguk.ukuu.org.uk> On Mon, 9 May 2011 19:20:17 +0530 "Subhasish Ghosh" wrote: > >> I then modified this function to as follows and the error is not observed > >> anymore. > > > > That looks like you are somehow calling uart_carrier_raised somewhere > > with interrupts disabled ? > > > I am not calling this function in my driver atall, this is getting called by > tty_port.c Can you verify the status of the interrupt flags at the point that routine is called in your code and get a backtrace of the path. It should never be gettng called with interrupts off, and if it is we need to know what the path is. Otherwise that code change would imply a bug in the core code for the platform which seems less likely. From subhasish at mistralsolutions.com Tue May 10 01:17:02 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 10 May 2011 11:47:02 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <20110509145534.2677dfa1@lxorguk.ukuu.org.uk> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com><1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com><034A6447E0D54737B5CEE009A4D20B37@subhasishg><20110509144610.6b4f7090@lxorguk.ukuu.org.uk><3A9C5CD56396439DBF825BB4E5779DB9@subhasishg> <20110509145534.2677dfa1@lxorguk.ukuu.org.uk> Message-ID: Hi Alan, >> >> I then modified this function to as follows and the error is not >> >> observed >> >> anymore. >> > >> > That looks like you are somehow calling uart_carrier_raised somewhere >> > with interrupts disabled ? >> > >> I am not calling this function in my driver atall, this is getting called >> by >> tty_port.c > > Can you verify the status of the interrupt flags at the point that > routine is called in your code and get a backtrace of the path. It > should never be gettng called with interrupts off, and if it is we need > to know what the path is. Otherwise that code change would imply a bug > in the core code for the platform which seems less likely. Here is the back trace: root at arago:~# ------------[ cut here ]------------ WARNING: at drivers/tty/serial/pruss_suart.c:295 uart_carrier_raised+0x2c/0x74() Modules linked in: pruss_uart [] (unwind_backtrace+0x0/0xec) from [] (warn_slowpath_common+0x4c/0x64) [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x18/0x1c) [] (warn_slowpath_null+0x18/0x1c) from [] (uart_carrier_raised+0x2c/0x74) [] (uart_carrier_raised+0x2c/0x74) from [] (tty_port_carrier_raised+0x1c/0x20) [] (tty_port_carrier_raised+0x1c/0x20) from [] (tty_port_block_til_ready+0x254/0x328) [] (tty_port_block_til_ready+0x254/0x328) from [] (tty_open+0x310/0x478) [] (tty_open+0x310/0x478) from [] (chrdev_open+0x1b4/0x1d0) [] (chrdev_open+0x1b4/0x1d0) from [] (__dentry_open+0x170/0x27c) [] (__dentry_open+0x170/0x27c) from [] (nameidata_to_filp+0x50/0x5c) [] (nameidata_to_filp+0x50/0x5c) from [] (finish_open+0x90/0x17c) [] (finish_open+0x90/0x17c) from [] (do_filp_open+0x23c/0x520) [] (do_filp_open+0x23c/0x520) from [] (do_sys_open+0x58/0xe4) [] (do_sys_open+0x58/0xe4) from [] (ret_fast_syscall+0x0/0x2c) flags ---> 0xA0000013 This is how I generated the trace: 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; WARN_ON(1); dev_dbg(dev, "modem control timer not supported\n"); } From subhasish at mistralsolutions.com Tue May 10 04:53:18 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 10 May 2011 15:23:18 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105051612.18522.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <613E2AF627DE4ABE92AD42D5D9E1BFE5@subhasishg> <201105051612.18522.arnd@arndb.de> Message-ID: > Instead of passing a configuration file into the MFD driver and > calling it a firmware, I can see three other options that I believe > would be nicer: > > 1. have a single firmware blob that describes the child devices > and the code that is to be loaded into both units. If they are > acting as one device, either make sure you only create one > child node, or create one with a name that no driver binds to. > > 2. Call request_firmware separately for the two child devices, > and configure them separately based on the information in the > firmware files. In the case where they should act as a single > device, call the children e.g. "pruss-uart-a" and "pruss-uart-b", > then bind to both names in the pruss-uart drivers and only > start using the device when you have both nodes for the same > parent. > > 3. Do the configuration through sysfs files in the MFD device node, > which then cause the creation of the child devices. This means you > need extra user space scripts to write the addititonal files, but > is also the most flexible way. > Are you suggesting something like: /sys..../pruss/pru0/id /sys..../pruss/pru0/fw_name /sys..../pruss/pru0/load /sys..../pruss/pru1/id /sys..../pruss/pru1/fw_name /sys..../pruss/pru1/load I can program these configs and store them in the pru private. Then write something into load. This can load the PRU based upon the configs. But, I am not sure how to do this effectively using sysfs, I mean I don't want to end up writing six write and six read handlers. From subhasish at mistralsolutions.com Tue May 10 05:11:49 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 10 May 2011 15:41:49 +0530 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <4DC17831.3070801@grandegger.com> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <201105041511.54095.arnd@arndb.de> <4DC163D7.9010309@grandegger.com> <201105041648.37199.arnd@arndb.de> <4DC17831.3070801@grandegger.com> Message-ID: >> >> It sounds to me like the best solution would be change the firmware >> to lift that restriction and simply allow all IDs, in case it's not >> actually a hardware limitation (which sounds unlikely). > > Yes, that would be best but they told us, that it's not possible with > the available hardware resources. Subhasish? Yes, In case if we allow the ALL implementation, it hogs the CPU. In that case we do not need the PRU. The whole purpose of the PRU is to offload the processor for any such implementations. From alan at lxorguk.ukuu.org.uk Tue May 10 05:27:34 2011 From: alan at lxorguk.ukuu.org.uk (Alan Cox) Date: Tue, 10 May 2011 11:27:34 +0100 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <201105041511.54095.arnd@arndb.de> <4DC163D7.9010309@grandegger.com> <201105041648.37199.arnd@arndb.de> <4DC17831.3070801@grandegger.com> Message-ID: <20110510112734.54160824@lxorguk.ukuu.org.uk> On Tue, 10 May 2011 15:41:49 +0530 "Subhasish Ghosh" wrote: > >> > >> It sounds to me like the best solution would be change the firmware > >> to lift that restriction and simply allow all IDs, in case it's not > >> actually a hardware limitation (which sounds unlikely). > > > > Yes, that would be best but they told us, that it's not possible with > > the available hardware resources. Subhasish? > > Yes, In case if we allow the ALL implementation, it hogs the CPU. > In that case we do not need the PRU. The whole purpose of the PRU > is to offload the processor for any such implementations. So the kernel presumably needs to switch between using the PRU and native according to the number of ids being requested at the time ? That would be roughly what we do with other things (eg IP multicast) so that apps don't need to know all the innards From subhasish at mistralsolutions.com Tue May 10 05:54:35 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 10 May 2011 16:24:35 +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: <0B813DC0439B438BBB2B66A88B1A5F7F@subhasishg> >> >> >>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. > Say, if the driver is loaded as a module. If I allocate the sram in the platform code, how to I free it when the driver is unloaded. From subhasish at mistralsolutions.com Tue May 10 07:21:12 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 10 May 2011 17:51:12 +0530 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <20110510112734.54160824@lxorguk.ukuu.org.uk> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com><201105041511.54095.arnd@arndb.de><4DC163D7.9010309@grandegger.com><201105041648.37199.arnd@arndb.de><4DC17831.3070801@grandegger.com> <20110510112734.54160824@lxorguk.ukuu.org.uk> Message-ID: <2BFFDAA0A0DE4820876E5549867938EC@subhasishg> >> Yes, In case if we allow the ALL implementation, it hogs the CPU. >> In that case we do not need the PRU. The whole purpose of the PRU >> is to offload the processor for any such implementations. > > So the kernel presumably needs to switch between using the PRU and native > according to the number of ids being requested at the time ? All the IDs are programmed into the PRU data RAM. The Kernel receives interrupts based upon these IDs. I could not clearly follow "PRU and native", could you please elaborate. > That would be roughly what we do with other things (eg IP multicast) so > that apps don't need to know all the innards From nsekhar at ti.com Tue May 10 08:13:45 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 10 May 2011 18:43:45 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <0B813DC0439B438BBB2B66A88B1A5F7F@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> <0B813DC0439B438BBB2B66A88B1A5F7F@subhasishg> Message-ID: On Tue, May 10, 2011 at 16:24:35, 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. > > > > Say, if the driver is loaded as a module. > If I allocate the sram in the platform code, how to I > free it when the driver is unloaded. This is what I said in my last e-mail. What is the issue you see with this approach? | Thanks for the clarification. In this case, the driver | should use platform callbacks to get/put fast fifo | space. In case this callback is not populated by the | platform or returns an error, the driver should fall | back to allocating from DDR. Thanks, Sekhar From alan at lxorguk.ukuu.org.uk Tue May 10 08:32:43 2011 From: alan at lxorguk.ukuu.org.uk (Alan Cox) Date: Tue, 10 May 2011 14:32:43 +0100 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> <034A6447E0D54737B5CEE009A4D20B37@subhasishg> <20110509144610.6b4f7090@lxorguk.ukuu.org.uk> <3A9C5CD56396439DBF825BB4E5779DB9@subhasishg> <20110509145534.2677dfa1@lxorguk.ukuu.org.uk> Message-ID: <20110510143243.3e6f4050@lxorguk.ukuu.org.uk> Trace all looks fine. I can't see anything else taking the lock so you'll need to do a bit more debugging and find out why the spin lock change makes the difference and what the real root cause is. From arnd at arndb.de Tue May 10 16:44:58 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Tue, 10 May 2011 23:44:58 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105051612.18522.arnd@arndb.de> Message-ID: <201105102344.58598.arnd@arndb.de> On Tuesday 10 May 2011, Subhasish Ghosh wrote: > > Instead of passing a configuration file into the MFD driver and > > calling it a firmware, I can see three other options that I believe > > would be nicer: > > > > 1. have a single firmware blob that describes the child devices > > and the code that is to be loaded into both units. If they are > > acting as one device, either make sure you only create one > > child node, or create one with a name that no driver binds to. > > > > 2. Call request_firmware separately for the two child devices, > > and configure them separately based on the information in the > > firmware files. In the case where they should act as a single > > device, call the children e.g. "pruss-uart-a" and "pruss-uart-b", > > then bind to both names in the pruss-uart drivers and only > > start using the device when you have both nodes for the same > > parent. > > > > 3. Do the configuration through sysfs files in the MFD device node, > > which then cause the creation of the child devices. This means you > > need extra user space scripts to write the addititonal files, but > > is also the most flexible way. > > > > Are you suggesting something like: > > /sys..../pruss/pru0/id > /sys..../pruss/pru0/fw_name > /sys..../pruss/pru0/load > > /sys..../pruss/pru1/id > /sys..../pruss/pru1/fw_name > /sys..../pruss/pru1/load No, that is none of the three I suggested. > I can program these configs and store them in the pru private. > Then write something into load. > This can load the PRU based upon the configs. > But, I am not sure how to do this effectively using sysfs, > I mean I don't want to end up writing six write and six read > handlers. Please look into implementing one of the three I suggested before you go off in another direction. In case of the third one, the idea was to configure the name of the device for each pru using sysfs, which then gets bound to the driver, which loads its own firmware as you do today. Only in the first two suggestions, the mfd driver would be responsible for loading the firmware. Arnd From subhasish at mistralsolutions.com Wed May 11 02:01:01 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 11 May 2011 12:31:01 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <20110510143243.3e6f4050@lxorguk.ukuu.org.uk> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com><1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com><034A6447E0D54737B5CEE009A4D20B37@subhasishg><20110509144610.6b4f7090@lxorguk.ukuu.org.uk><3A9C5CD56396439DBF825BB4E5779DB9@subhasishg><20110509145534.2677dfa1@lxorguk.ukuu.org.uk> <20110510143243.3e6f4050@lxorguk.ukuu.org.uk> Message-ID: > Trace all looks fine. I can't see anything else taking the lock so you'll > need to do a bit more debugging and find out why the spin lock change > makes the difference and what the real root cause is. We do not support Modem control signals. So, I use -clocal with stty, but I observe that still enable_ms and get_mctrl handlers get called. Is that normal, how can I disable these functions from getting called. Actually, this same driver works perfectly with 2.6.33 kernel. With this Kernel I do not observe that these handlers getting called. So, basically the code path is different. Further, I only use the port->lock to disable interrupts, so its never possible that while interrupts are disabled by my driver, the TTY is executing anything. There is another lock in the MFD driver, but that only uses spin_lock/unlock variant. From subhasish at mistralsolutions.com Wed May 11 04:28:23 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Wed, 11 May 2011 14:58:23 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105102344.58598.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105051612.18522.arnd@arndb.de> <201105102344.58598.arnd@arndb.de> Message-ID: <82D549BB195345A1A189D569952B387C@subhasishg> > Please look into implementing one of the three I suggested before > you go off in another direction. In case of the third one, the idea > was to configure the name of the device for each pru using sysfs, > which then gets bound to the driver, which loads its own firmware > as you do today. Only in the first two suggestions, the mfd driver > would be responsible for loading the firmware. Ok, thanks for the clarification. Instead of passing the device name, will it be ok to pass the mfd_id. The benefit will be that I can use the ID directly as an array index for the mfd_cell entries. Just to verify my understanding, all that's needs to be done is add a sysfs entry at /sys/devices/platform/pruss_mfd/mfd_id Writing to this entry would create the respective device. Rewriting would unload the old (mfd_remove_device) and reload the new device. Reading may return the various devices and their name aliases. From alan at lxorguk.ukuu.org.uk Wed May 11 05:35:46 2011 From: alan at lxorguk.ukuu.org.uk (Alan Cox) Date: Wed, 11 May 2011 11:35:46 +0100 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> <034A6447E0D54737B5CEE009A4D20B37@subhasishg> <20110509144610.6b4f7090@lxorguk.ukuu.org.uk> <3A9C5CD56396439DBF825BB4E5779DB9@subhasishg> <20110509145534.2677dfa1@lxorguk.ukuu.org.uk> <20110510143243.3e6f4050@lxorguk.ukuu.org.uk> Message-ID: <20110511113546.4592af20@lxorguk.ukuu.org.uk> > We do not support Modem control signals. So, I use -clocal with stty, > but I observe that still enable_ms and get_mctrl handlers get called. > Is that normal, how can I disable these functions from getting called. It is normal. > Actually, this same driver works perfectly with 2.6.33 kernel. > With this Kernel I do not observe that these handlers getting called. > So, basically the code path is different. Quite possibly - but you'll need to trace down and understand *why* the problem exists. Finding which kernel introduces it might be a step in the right direction. Alan From arnd at arndb.de Wed May 11 15:03:54 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 11 May 2011 22:03:54 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <82D549BB195345A1A189D569952B387C@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105102344.58598.arnd@arndb.de> <82D549BB195345A1A189D569952B387C@subhasishg> Message-ID: <201105112203.54838.arnd@arndb.de> On Wednesday 11 May 2011, Subhasish Ghosh wrote: > > Please look into implementing one of the three I suggested before > > you go off in another direction. In case of the third one, the idea > > was to configure the name of the device for each pru using sysfs, > > which then gets bound to the driver, which loads its own firmware > > as you do today. Only in the first two suggestions, the mfd driver > > would be responsible for loading the firmware. > > Ok, thanks for the clarification. > Instead of passing the device name, will it be ok to pass the mfd_id. > The benefit will be that I can use the ID directly as an array > index for the mfd_cell entries. I think a device name would be clearer here, especially in order to avoid conflicts when the list gets extended in different ways depending on which kernel runs. We had a little discussion at the Linaro Developer Summit about your driver and mfd drivers in general. There was a general feeling among some people (including me) that by the point you dynamically create the subdevices, MFD is probably not the right abstraction any more, as it does not provide any service that you need. Instead, maybe you can simply call platform_device_register at that stage to create the children and not use MFD at all. Samuel, can you comment on this as well? Do you still see pruss as an MFD driver when the uses are completely dynamic and determined by the firmware loaded into it? > Just to verify my understanding, all that's needs to be done is add > a sysfs entry at > /sys/devices/platform/pruss_mfd/mfd_id > > Writing to this entry would create the respective device. > Rewriting would unload the old (mfd_remove_device) > and reload the new device. Yes > Reading may return the various devices and their name > aliases. I would just return the current value. You might want to have two files in sysfs, one per PRU, so you can set them individually. Arnd From arnd at arndb.de Wed May 11 16:31:47 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 11 May 2011 23:31:47 +0200 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <2BFFDAA0A0DE4820876E5549867938EC@subhasishg> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <20110510112734.54160824@lxorguk.ukuu.org.uk> <2BFFDAA0A0DE4820876E5549867938EC@subhasishg> Message-ID: <201105112331.47954.arnd@arndb.de> On Tuesday 10 May 2011, Subhasish Ghosh wrote: > > >> Yes, In case if we allow the ALL implementation, it hogs the CPU. > >> In that case we do not need the PRU. The whole purpose of the PRU > >> is to offload the processor for any such implementations. > > > > So the kernel presumably needs to switch between using the PRU and native > > according to the number of ids being requested at the time ? > > All the IDs are programmed into the PRU data RAM. > The Kernel receives interrupts based upon these IDs. > I could not clearly follow "PRU and native", could you please elaborate. We would really like all CAN drivers to behave the same way. All other drivers are able to work without filters, so pruss_can should allow that too, even if it becomes a CPU hog at that time. It seems to me that the pruss can implementation has one thing backwards: it assumes a specific usage model for CAN that it is trying to do offload for. However, that usage model is currently not even supported by Socket CAN. If I understand Wolfgang correctly, it is in fact considered an unwanted limitation of the pruss can driver, instead of a useful feature. If that interpretation is right, I would seriously recommend rethinking the design of the CAN firmware for pruss, so you can start doing something useful with the offload engine that fits into the Socket CAN API, or that would be a useful extension to Socket CAN that is also implementable in the kernel for all other drivers in a meaningful way. Arnd From arnd at arndb.de Wed May 11 16:44:43 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 11 May 2011 23:44:43 +0200 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <201105112331.47954.arnd@arndb.de> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <2BFFDAA0A0DE4820876E5549867938EC@subhasishg> <201105112331.47954.arnd@arndb.de> Message-ID: <201105112344.44171.arnd@arndb.de> On Wednesday 11 May 2011, Arnd Bergmann wrote: > If that interpretation is right, I would seriously recommend rethinking > the design of the CAN firmware for pruss, so you can start doing something > useful with the offload engine that fits into the Socket CAN API, or that > would be a useful extension to Socket CAN that is also implementable in > the kernel for all other drivers in a meaningful way. I've looked some more into the CAN socket implementation, and I suppose that the idea of the pruss driver was really to help do the work from the can_rcv_filter function in hardware. Doing this right would really mean supporting both a mode where any new filter that gets added to socket can ends up being added to the hardware as long as it fits, similar to how we can add additional unicast mac addresses to an ethernet NIC. However, when the filters from all user sockets combined can not be represented in the hardware driver, the hardware needs to be put into a less efficient mode where all packets are returned to the kernel and processed in software. Arnd From alan at lxorguk.ukuu.org.uk Wed May 11 17:56:52 2011 From: alan at lxorguk.ukuu.org.uk (Alan Cox) Date: Wed, 11 May 2011 23:56:52 +0100 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <4DCB103B.30801@pengutronix.de> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <2BFFDAA0A0DE4820876E5549867938EC@subhasishg> <201105112331.47954.arnd@arndb.de> <201105112344.44171.arnd@arndb.de> <4DCB103B.30801@pengutronix.de> Message-ID: <20110511235652.7ccddb3b@lxorguk.ukuu.org.uk> > I'm not sure if reprogramming hardware filters on the fly works on evey > can core. The more conservative solution would be to configure the > filter list globally (+when the interface is down) via netlink. For anything that isn't so braindead it ought to be done on the fly and behind the users back to avoid having to make app code specially aware. If the lists are fixed either in firmware or in software the stack needs to error attempts to use anything else - and as you say you need some kind of way to configure those global lists, preferably portably. Alan From jaya.krishnan at samsung.com Thu May 12 05:54:35 2011 From: jaya.krishnan at samsung.com (Jayakrishnan Melur Madhathil) Date: Thu, 12 May 2011 10:54:35 +0000 (GMT) Subject: DM365 Vpfe init Message-ID: <16612312.543881305197674885.JavaMail.weblogic@epv6ml05> Hi I am facing a peculiar problem. In my DM365 based video encoder I often get the following messages Unable to handle kernel paging request at virtual address 4b8050cc pgd = cb880000 [4b8050cc] *pgd=00000000 Internal error: Oops: 5 [#1] Modules linked in: rt_timer led vpr model reset alarmsensor spi dm365mmap edmak irqk cmemk CPU: 0 PC is at cache_alloc_refill+0x144/0x5dc LR is at 0xcb002828 pc : [] lr : [] Not tainted sp : cbfa9e38 ip : d703f4f0 fp : cbfa9e84 r10: cb009b60 r9 : cbfa8000 r8 : 00000039 r7 : cb002844 r6 : cb00d400 r5 : cb00a760 r4 : 00000019 r3 : 00000002 r2 : cb002830 r1 : 00000060 r0 : 20200a22 Flags: nzCv IRQs off FIQs on Mode SVC_32 Segment user Control: 5317F Table: 8B880000 DAC: 00000015 Process mainServer (pid: 788, stack limit = 0xcbfa8258) Stack: (0xcbfa9e38 to 0xcbfaa000) 9e20: 000000d0 00000000 9e40: cb00a768 cb00a770 cbfa9e8c 000000d0 c00556cc c00555e0 cb124580 20000013 9e60: cbfa8000 00000000 cbf677c8 0000002b cbfa9f3c cb340660 cbfa9ea4 cbfa9e88 9e80: c009c934 c009c954 cb3404e0 00000000 cbfa9eb4 00000000 cbfa9eb4 cbfa9ea8 9ea0: c00b8550 c009c8ec cbfa9ef4 cbfa9eb8 c00b8768 c00b8544 cb340540 ca0d0360 9ec0: cbfa9eec cbfa9ed0 c009c1a0 00000000 cbfa8000 cb340660 ca0d0360 0000002b 9ee0: cbfa9f3c 0000000d cbfa9f04 cbfa9ef8 c00b8c30 c00b8710 cbfa9f7c cbfa9f08 9f00: c00b9618 c00b8c24 cb34067c cbfa9f48 0000002b 000000ac 00000002 47cf74d8 9f20: 40000000 00000000 00000001 00000000 40042000 00000152 cbfa8000 c03b4080 9f40: cbfa8000 00000002 cb11c760 cbfa9fb0 cbfa8000 0000000d 00000000 47cf61c0 9f60: ca0d0360 0000002b cbfa8000 00000000 cbfa9fa4 cbfa9f80 c00b5384 c00b948c 9f80: cbfa9fac 0000000d 0000002b 40042000 000000dd c003af88 00000000 cbfa9fa8 9fa0: c003ade0 c00b52fc 0000000d 0000002b 0000002b 0000000d 47cf61c0 47cf61a4 9fc0: 0000000d 0000002b 40042000 000000dd 40042000 003d0f00 4002a85c 47cf61fc 9fe0: 003a544c 47cf6180 00023680 40034d3c 80000010 0000002b 0000ffff 0000ffff Backtrace: [] (cache_alloc_refill+0x0/0x5dc) from [] (kmem_cache_alloc+0x58/0x68) [] (kmem_cache_alloc+0x0/0x68) from [] (locks_alloc_lock+0x1c/0x24) r4 = 00000000 [] (locks_alloc_lock+0x0/0x24) from [] (__posix_lock_file_conf+0x68/0x4f8) [] (__posix_lock_file_conf+0x0/0x4f8) from [] (posix_lock_file+0x1c/0x20) [] (posix_lock_file+0x0/0x20) from [] (fcntl_setlk64+0x19c/0x2fc) [] (fcntl_setlk64+0x0/0x2fc) from [] (sys_fcntl64+0x98/0xc8) [] (sys_fcntl64+0x0/0xc8) from [] (ret_fast_syscall+0x0/0x2c) r8 = C003AF88 r7 = 000000DD r6 = 40042000 r5 = 0000002B r4 = 0000000D Code: e59e200c e5963000 e02c2190 e58e4010 (e7972100) <6>note: mainServer[788] exited with preempt_count 1 ipipe_set_resizer, resizer - A enabled ipipe_set_resizer, resizer - B enabled ipipe_set_resizer, resizer - A enabled rsz_set_output_address 0 rsz_set_output_address 1 ipipe_set_resizer, resizer - A enabled ipipe_set_resizer, resizer - B enabled ipipe_set_resizer, resizer - A enabled ipipe_set_resizer, resizer - B enabled rsz_set_output_address 0 rsz_set_output_address 1 ipipe_set_resizer, resizer - A enabled ipipe_set_resizer, resizer - B enabled ipipe_set_resizer, resizer - A enabled Last set of messages are printed forever. The resizer messages are coming from the dm365 and vpfe drivers. And these messages are supposed to come only once during initialization. Why the vpfe_init() is called repeatedly after the kernel crash? Is this a hardware problem? Pls help. Jayakrishnan M M Sr. Research Engineer R&D Group-2 Security Solutions Division SAMSUNG TECHWIN CO.,LTD TEL +82-70-7147-8482 FAX +82-31-8018-3712 Mobile +82-10-6409-3619 E-mail:jaya.krishnan at samsung.com From arnd at arndb.de Thu May 12 07:54:47 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Thu, 12 May 2011 14:54:47 +0200 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <4DCB88A4.2010901@grandegger.com> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <201105112344.44171.arnd@arndb.de> <4DCB88A4.2010901@grandegger.com> Message-ID: <201105121454.47781.arnd@arndb.de> On Thursday 12 May 2011, Wolfgang Grandegger wrote: > Well, that seems sophisticated resulting in a complex implementation > (may code line) also because hardware filters are very hardware > dependent. Usually just one global filter can be defined. I think that's > overkill. A simple interface using: > > ip link set can0 type can filter : [: ...] > > would just be fine. Ok, fair enough. Still I would suggest you first come up with a reasonable user interface (the one you posted may be just right, I don't know), and then let someone do the implementation in the pruss firmware that is the best match for the user interface, rather than the other way around. Arnd From lamiaposta71 at gmail.com Thu May 12 12:11:01 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Thu, 12 May 2011 19:11:01 +0200 Subject: pm loss development Message-ID: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> What happen normally in runtime pm implementation is that every devices are switched off and are enabled only when needed. In our case instead we have a completely functional embedded system and, when an asyncrhonous event appear, we have only some tens milliseconds before the actual power failure takes place. This patchset add a support in order to switch off not vital part of the system, in order to allow the board to survive longer. This allow the possibility to save important data. The implementation has been written by Davide Ciminaghi. My work instead was about analyzing different previuos implementation, a first completely custom one, a second using runtime pm, arriving finally to that one. I have tested PM loss in our DaVinci dm365 basi board and I write here below a piece of code showing a possible usage. ------------------- static int powerfail_status; static irqreturn_t basi_powerfail_stop(int irq, void *dev_id); static irqreturn_t basi_powerfail_quick_check_start(int irq, void *dev_id) { basi_mask_irq_gpio0(IRQ_DM365_GPIO0_2); basi_unmask_irq_gpio0(IRQ_DM365_GPIO0_0); /* PowerFail situation - START: power is going away */ return IRQ_WAKE_THREAD; } static irqreturn_t basi_powerfail_start(int irq, void *dev_id) { if (powerfail_status) return IRQ_HANDLED; powerfail_status = 1; pm_loss_power_changed(SYS_PWR_FAILING); return IRQ_HANDLED; } static irqreturn_t basi_powerfail_quick_check_stop(int irq, void *dev_id) { basi_mask_irq_gpio0(IRQ_DM365_GPIO0_0); basi_unmask_irq_gpio0(IRQ_DM365_GPIO0_2); /* PowerFail situation - STOP: power is coming back */ return IRQ_WAKE_THREAD; } static irqreturn_t basi_powerfail_stop(int irq, void *dev_id) { if (!powerfail_status) return IRQ_HANDLED; powerfail_status = 0; pm_loss_power_changed(SYS_PWR_GOOD); return IRQ_HANDLED; } enum basi_pwrfail_prio { BASI_PWR_FAIL_PRIO_0, BASI_PWR_FAIL_MIN_PRIO = BASI_PWR_FAIL_PRIO_0, BASI_PWR_FAIL_PRIO_1, BASI_PWR_FAIL_PRIO_2, BASI_PWR_FAIL_PRIO_3, BASI_PWR_FAIL_MAX_PRIO = BASI_PWR_FAIL_PRIO_3, }; struct pm_loss_default_policy_item basi_pm_loss_policy_items[] = { { .bus_name = "mmc", .bus_priority = BASI_PWR_FAIL_PRIO_1, }, { .bus_name = "platform", .bus_priority = BASI_PWR_FAIL_PRIO_2, }, }; #define BASI_POLICY_NAME "basi-default" struct pm_loss_default_policy_table basi_pm_loss_policy_table = { .name = BASI_POLICY_NAME, .items = basi_pm_loss_policy_items, .nitems = ARRAY_SIZE(basi_pm_loss_policy_items), }; static void basi_powerfail_configure(void) { int stat; struct pm_loss_policy *p; stat = request_threaded_irq(IRQ_DM365_GPIO0_2, basi_powerfail_quick_check_start, basi_powerfail_start, 0, "pwrfail-on", NULL); if (stat < 0) printk(KERN_ERR "request_threaded_irq for IRQ%d (pwrfail-on) " "failed\n", IRQ_DM365_GPIO0_2); stat = request_threaded_irq(IRQ_DM365_GPIO0_0, basi_powerfail_quick_check_stop, basi_powerfail_stop, 0, "pwrfail-off", NULL); if (stat < 0) printk(KERN_ERR "request_threaded_irq for IRQ%d (pwrfail-off) " "failed\n", IRQ_DM365_GPIO0_0); basi_mask_irq_gpio0(IRQ_DM365_GPIO0_0); p = pm_loss_setup_default_policy(&basi_pm_loss_policy_table); if (!p) printk(KERN_ERR "Could not register pwr change policy\n"); if (pm_loss_set_policy(BASI_POLICY_NAME) < 0) printk(KERN_ERR "Could not set %s power loss policy\n", BASI_POLICY_NAME); } int platform_pm_loss_power_changed(struct device *dev, enum sys_power_state s) { int ret = 0; /* Calling platform bus pm_loss functions */ pr_debug_pm_loss("platform_pm_loss_power_changed(%d)\n", s); if (dev->driver && dev->driver->pm && dev->driver->pm->power_changed) ret = dev->driver->pm->power_changed(dev, s); return ret; } From lamiaposta71 at gmail.com Thu May 12 12:11:02 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Thu, 12 May 2011 19:11:02 +0200 Subject: [PATCH 1/4] export bus_kset In-Reply-To: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> Message-ID: <1305220265-9020-2-git-send-email-lamiaposta71@gmail.com> From: Davide Ciminaghi Signed-off-by: Davide Ciminaghi --- drivers/base/bus.c | 3 ++- include/linux/kobject.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 000e7b2..2134248 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -158,7 +158,8 @@ static const struct kset_uevent_ops bus_uevent_ops = { .filter = bus_uevent_filter, }; -static struct kset *bus_kset; +struct kset *bus_kset; +EXPORT_SYMBOL(bus_kset); #ifdef CONFIG_HOTPLUG diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 8f6d121..456b20d 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -205,6 +205,8 @@ extern struct kobject *power_kobj; /* The global /sys/firmware/ kobject for people to chain off of */ extern struct kobject *firmware_kobj; +extern struct kset *bus_kset ; + #if defined(CONFIG_HOTPLUG) int kobject_uevent(struct kobject *kobj, enum kobject_action action); int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, -- 1.7.0.4 From lamiaposta71 at gmail.com Thu May 12 12:11:03 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Thu, 12 May 2011 19:11:03 +0200 Subject: [PATCH 2/4] PM / Loss: power loss management In-Reply-To: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> Message-ID: <1305220265-9020-3-git-send-email-lamiaposta71@gmail.com> From: Davide Ciminaghi On some embedded systems, an asynchronous power failure notification event is available some tens to hundreds milliseconds before the actual power failure takes place. Such an event can be used to trigger some actions, typically shutting down all non-vital power sinks, thus allowing the board to survive longer to temporary power losses. Signed-off-by: Davide Ciminaghi Signed-off-by: Raffaele Recalcati --- Documentation/power/loss.txt | 191 +++++++++++++ drivers/base/bus.c | 6 + drivers/base/init.c | 1 + drivers/base/platform.c | 14 + drivers/base/power/Makefile | 2 + drivers/base/power/loss.c | 648 ++++++++++++++++++++++++++++++++++++++++++ drivers/base/power/power.h | 14 + include/linux/pm.h | 16 + include/linux/pm_loss.h | 109 +++++++ kernel/power/Kconfig | 27 ++ 10 files changed, 1028 insertions(+), 0 deletions(-) create mode 100644 Documentation/power/loss.txt create mode 100644 drivers/base/power/loss.c create mode 100644 include/linux/pm_loss.h diff --git a/Documentation/power/loss.txt b/Documentation/power/loss.txt new file mode 100644 index 0000000..10da89c --- /dev/null +++ b/Documentation/power/loss.txt @@ -0,0 +1,191 @@ +**** POWER LOSS MANAGEMENT **** + +Davide Ciminaghi 2011 + +1. Overview + +On some embedded systems, an asynchronous power failure notification event is +available some tens to hundreds milliseconds before the actual power failure +takes place. +Such an event can be used to trigger some actions, typically shutting down +all non-vital power sinks, thus allowing the board to survive longer to +temporary power losses. Sometimes, also flash-based block devices can be +stopped after a power loss event notification has been received. This should +be useful for mmc devices, for which a sudden power failure while a write +command is being executed can threaten file system integrity even in case a +journalling fs is in use. +Generally speaking, one can expect the course of action taken when a power +failure warning is received to be deeply system specific. Similarly, the +mechanism used for power failure notifications can equally be board/platform +specific. For these reasons, support for power loss management has been split +into three parts: + + + Generic code (board and driver independent). + + Board dependent code. + + Power loss policy definition. + +The generic part of the code is located under drivers/base/power/loss.c . +On the other hand, board dependent code and power loss policies definitions +should live somewhere in the platform/board specific files. +The header file include/linux/pm_loss.h contains all the pm_loss function +prototypes, together with definitions of data structures. +For what concerns power loss policies, the framework already contains a couple +of predefined policies: "nop" and "default" (see later paragraphs for more +details). + +1.1 Sysfs interface. + +It can be useful (for instance during tests), to be able to quickly switch +from one power loss policy to another, or to simulate power fail and resume +events. To this end, a sysfs interface of the following form has been devised: + +/sys/power/loss + + | + +-- active_policy + | + +-- policies -+ + | | + | +-- nop + | | + | +-- default + + | | | + | | +-- bus_table + | | + | +-- .... + | + +-- pwrfail_sim + +2. Details + +2.1 Sending events to the power loss management core. + +The board specific code shall trigger a power failure event notification by +calling pm_loss_power_changed(SYS_PWR_FAILING). +In case of a temporary power loss, the same function shall be called with +SYS_PWR_GOOD argument on power resume. pm_loss_power_changed() can sleep, so +it shall not be called from interrupt context. + +2.3 Effects on bus and device drivers. + +One more entry has been added to the device PM callbacks: + + int (*power_changed)(struct device *, enum sys_power_state); + +This function can be optionally invoked by power loss policies in case of +system power changes (loss and/or resume). Of course every bus or device driver +can react to such events in some specific way. For instance the mmc block +driver will probably block its request queue during a temporary power loss. + +2.3.1 The platform bus. + +For what concerns platform bus drivers, platform specific code can override +the power_changed() callback : + +platform_pm_loss_power_changed(struct device *dev, enum sys_power_state s) + +whose default (empty) version is defined as a __weak symbol in +drivers/base/platform.c. + +2.4 Power loss policies. + +A power loss policy can be registered via the pm_loss_register_policy() +function, which receives: + + + A pointer to a struct pm_loss_policy_ops , which hosts the pointers + to the policy's specific callbacks (see below for more details). + + A pointer to a struct kobj_type, which will allow the pm loss core + to setup the policy related directory under /sys/power/loss/policies. + See Documentation/kobject.txt for more details on struct kobj_type. + + A pointer to an array of chars containing the name of the policy. + + A pointer to the policy's private data (void *). + +A power loss policy can define the following callbacks: + + + int (*bus_added)(struct pm_loss_policy *, struct bus_type *) : this + is invoked whenever a new bus has been registered within the system. + Since power related events are often managed at bus level, it can be + useful for the policy to have a list of available busses within the + system. When a policy is registered, this callback is invoked once + for every already registered bus. + + int (*bus_removed)(struct pm_loss_policy *, struct bus_type *): + this is invoked when a bus is removed from the system. + + int (*start)(struct pm_loss_policy *): this is called when a policy + becomes active. + + void (*stop)(struct pm_loss_policy *): this is called when a policy + becomes inactive. + +2.4.1 The nop predefined policy + +The nop policy is the first active policy when the pm loss framework is +initialized. It just does nothing in case of power loss / power resume +events. + +2.4.2 The default predefined policy + +When a power failure warning is received, the default policy walks through a +list of critical busses and invokes their drivers' power_changed() callback. +The default policy can be customized and registered by calling: + + pm_loss_setup_default_policy(struct pm_loss_default_policy_table *); + +This function receives a pointer to a pm_loss_default_policy_table structure, +which defines a priority ordered list of critical buffers: + + struct pm_loss_default_policy_table { + struct module *owner ; + const char *name ; + struct pm_loss_default_policy_item *items; + int nitems; + }; + +Here's a short description of such structure: + + + owner : shall point to the module registering the table (or NULL + if the table is not instantiated by a module). + + name : this is the name given to this particular customization of + the default policy. + + items : pointer to an array of table items. + + nitems : number of the items in the array. + +Each item is a struct pm_loss_default_policy_item, defined as follows: + + struct pm_loss_default_policy_item { + const char *bus_name; + int bus_priority ; + }; + +where bus_name is the name of a bus and bus_priority is a numerical priority +of the bus itself. Numerically higher priorities correspond to more prioritary +busses. + +2.4.3 Activating a specific policy. + +A policy can be activated: + + + From within the kernel by calling pm_loss_set_policy(char *name). + The argument passed to this function shall be the name of the policy + to be activated. + + + From user space by writing the name of the policy to be activated + to /sys/power/loss/active_policy. + +2.4.4 Unregistering a policy + +For a generic user defined policy, just call : + + pm_loss_unregister_policy(struct pm_loss_policy *); + +For a default policy customization: + + pm_loss_shutdown_default_policy(struct pm_loss_policy *); + +3. Kernel configuration + +The following configuration options have been defined: + + CONFIG_PM_LOSS : this enables the generic pm_loss framework. + CONFIG_PM_LOSS_SIM : this adds the pwrfail_sim file to the sysfs interface + and allows power loss events simulation. + CONFIG_PM_LOSS_DEBUG : this option enables some debug printk's . + CONFIG_PM_LOSS_TEST: this enables the compilation of a sample test module + containing a customized default policy definition diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 2134248..d67a506 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -910,6 +910,9 @@ int bus_register(struct bus_type *bus) if (retval) goto bus_attrs_fail; +#ifdef CONFIG_PM_LOSS + pm_loss_on_bus_added(bus); +#endif pr_debug("bus: '%s': registered\n", bus->name); return 0; @@ -940,6 +943,9 @@ EXPORT_SYMBOL_GPL(bus_register); void bus_unregister(struct bus_type *bus) { pr_debug("bus: '%s': unregistering\n", bus->name); +#ifdef CONFIG_PM_LOSS + pm_loss_on_bus_removed(bus); +#endif bus_remove_attrs(bus); remove_probe_files(bus); kset_unregister(bus->p->drivers_kset); diff --git a/drivers/base/init.c b/drivers/base/init.c index c8a934e..d979da9 100644 --- a/drivers/base/init.c +++ b/drivers/base/init.c @@ -10,6 +10,7 @@ #include #include "base.h" +#include "power/power.h" /** * driver_init - initialize driver model. diff --git a/drivers/base/platform.c b/drivers/base/platform.c index f051cff..427b686 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "base.h" @@ -947,6 +948,18 @@ int __weak platform_pm_runtime_idle(struct device *dev) #endif /* !CONFIG_PM_RUNTIME */ +#ifdef CONFIG_PM_LOSS + +int __weak platform_pm_loss_power_changed(struct device *dev, + enum sys_power_state s) +{ + return -ENOSYS; +} + +#else +#define platform_pm_loss_power_changed NULL +#endif + static const struct dev_pm_ops platform_dev_pm_ops = { .prepare = platform_pm_prepare, .complete = platform_pm_complete, @@ -965,6 +978,7 @@ static const struct dev_pm_ops platform_dev_pm_ops = { .runtime_suspend = platform_pm_runtime_suspend, .runtime_resume = platform_pm_runtime_resume, .runtime_idle = platform_pm_runtime_idle, + .power_changed = platform_pm_loss_power_changed, }; struct bus_type platform_bus_type = { diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index abe46ed..6373944 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_RUNTIME) += runtime.o +obj-$(CONFIG_PM_LOSS) += loss.o +obj-$(CONFIG_PM_LOSS_TEST) += loss_test.o obj-$(CONFIG_PM_OPS) += generic_ops.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o diff --git a/drivers/base/power/loss.c b/drivers/base/power/loss.c new file mode 100644 index 0000000..a265b8b --- /dev/null +++ b/drivers/base/power/loss.c @@ -0,0 +1,648 @@ +/* + * drivers/base/power/loss.c - Power loss management related functions + * + * Copyright (C) 2011 Davide Ciminaghi + * Copyright (C) 2011 BTicino S.p.A. + * + * This file is released under the GPLv2. + */ + +#ifdef CONFIG_PM_LOSS_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include "../base.h" + +#define LOSS_STR "pm_loss" + +#define xstr(s) str(s) +#define str(s) #s +#define MAX_POLICY_NAME_LENGHT 20 + +struct power_loss_state_struct { + spinlock_t lock; + enum sys_power_state state; + struct pm_loss_policy *active_policy; + wait_queue_head_t wq; + struct kobject *loss_kobj; + struct kset *policies; +}; + +static struct power_loss_state_struct power_state; + + +void pm_loss_power_changed(enum sys_power_state s) +{ + pr_debug_pm_loss("pm_loss_power_changed, " + "power_state.active_policy = %p\n", + power_state.active_policy); + if (!power_state.active_policy || + !power_state.active_policy->ops || + !power_state.active_policy->ops->power_changed) { + pr_debug_pm_loss("pm_loss_power_changed with no active " + "policy or " + "policy with no sys_power_changed cb\n"); + return ; + } + pr_debug_pm_loss("pm_loss_power_changed(%d)\n", s); + spin_lock_irq(&power_state.lock); + if (power_state.state == s) { + spin_unlock_irq(&power_state.lock); + return ; + } + if (power_state.state == SYS_PWR_NOTIFYING) { + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&power_state.wq, &wait, + TASK_UNINTERRUPTIBLE); + if (power_state.state != SYS_PWR_NOTIFYING) + break; + spin_unlock_irq(&power_state.lock); + schedule(); + spin_lock_irq(&power_state.lock); + } + } + power_state.state = SYS_PWR_NOTIFYING ; + spin_unlock_irq(&power_state.lock); + if (power_state.active_policy->ops->power_changed) { + pr_debug_pm_loss("invoking sys_power_changed cb\n"); + power_state.active_policy->ops->power_changed + (power_state.active_policy, s); + } + power_state.state = s; +} +EXPORT_SYMBOL(pm_loss_power_changed); + +#define to_bus(obj) container_of(obj, struct subsys_private, subsys.kobj) + +static int __bus_added_removed(struct pm_loss_policy *p, + struct bus_type *bus, bool added) +{ + int ret = 0; + spin_lock_irq(&power_state.lock); + if (power_state.state == SYS_PWR_NOTIFYING) { + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&power_state.wq, &wait, + TASK_UNINTERRUPTIBLE); + if (power_state.state != SYS_PWR_NOTIFYING) + break; + spin_unlock_irq(&power_state.lock); + schedule(); + spin_lock_irq(&power_state.lock); + } + } + if (added) + ret = p->ops->bus_added ? p->ops->bus_added(p, bus) : 0; + else + ret = p->ops->bus_removed ? p->ops->bus_removed(p, bus) : 0; + spin_unlock_irq(&power_state.lock); + return ret; +} + +static int bus_added(struct pm_loss_policy *p, struct bus_type *bus) +{ + return __bus_added_removed(p, bus, true); +} + +static int bus_removed(struct pm_loss_policy *p, struct bus_type *bus) +{ + return __bus_added_removed(p, bus, false); +} + +static int policy_add_busses(struct pm_loss_policy *p) +{ + struct kobject *k; + int stat; + + if (!p || !p->ops) + return -EINVAL; + if (!p->ops->bus_added) + return 0; + + pr_debug_pm_loss("policy_add_busses()\n"); + + if (!bus_kset) { + pr_debug_pm_loss("bus_kset still NULL\n"); + return 0; + } + + spin_lock(&bus_kset->list_lock); + list_for_each_entry(k, &bus_kset->list, entry) { + spin_unlock(&bus_kset->list_lock); + /* This might sleep */ + pr_debug_pm_loss("invoking bus_added()\n"); + stat = bus_added(p, to_bus(k)->bus); + spin_lock(&bus_kset->list_lock); + } + spin_unlock(&bus_kset->list_lock); + return 0; +} + +static void __pm_loss_on_bus_added_removed(struct bus_type *bus, bool added) +{ + struct list_head *i; + struct kobject *k; + + if (!power_state.policies) + /* Not yet initialized */ + return ; + + spin_lock(&power_state.policies->list_lock); + list_for_each(i, &power_state.policies->list) { + k = container_of(i, struct kobject, entry); + if (added) + bus_added(to_policy(k), bus); + else + bus_removed(to_policy(k), bus); + } + spin_unlock(&power_state.policies->list_lock); +} + +void pm_loss_on_bus_added(struct bus_type *bus) +{ + __pm_loss_on_bus_added_removed(bus, true); +} +EXPORT_SYMBOL(pm_loss_on_bus_added); + +void pm_loss_on_bus_removed(struct bus_type *bus) +{ + __pm_loss_on_bus_added_removed(bus, false); +} +EXPORT_SYMBOL(pm_loss_on_bus_removed); + +struct pm_loss_policy * +pm_loss_register_policy(const struct pm_loss_policy_ops *ops, + struct kobj_type *ktype, + const char *name, void *priv) +{ + struct pm_loss_policy *out = NULL; + int ret ; + + if (strlen(name) > MAX_POLICY_NAME_LENGHT) + printk(KERN_WARNING LOSS_STR "warning : name %s is too long, " + "will be truncated\n", name); + + out = kzalloc(sizeof(struct pm_loss_policy), GFP_KERNEL); + if (!out) + return NULL; + out->ops = ops; + out->priv = priv; + out->kobj.kset = power_state.policies; + + if (policy_add_busses(out) < 0) + goto err0; + + ret = kobject_init_and_add(&out->kobj, ktype, NULL, "%s", name); + if (ret < 0) + goto err1; + + kobject_uevent(&out->kobj, KOBJ_ADD); + + return out; + + err1: + kobject_put(&out->kobj); + err0: + kfree(out); + return NULL; +} +EXPORT_SYMBOL(pm_loss_register_policy); + +int pm_loss_unregister_policy(struct pm_loss_policy *p) +{ + pr_debug_pm_loss("pm_loss_unregister_policy(%p)\n", p); + if (power_state.active_policy == p) { + pr_debug_pm_loss("cannot unregister policy (busy)\n"); + return -EBUSY ; + } + kobject_del(&p->kobj); + kobject_put(&p->kobj); + return 0; +} +EXPORT_SYMBOL(pm_loss_unregister_policy); + +int pm_loss_set_policy(const char *name) +{ + struct kobject *o; + struct pm_loss_policy *p ; + spin_lock_irq(&power_state.lock); + o = kset_find_obj(power_state.policies, name); + if (!o) { + spin_unlock_irq(&power_state.lock); + return -ENODEV; + } + p = to_policy(o); + if (power_state.active_policy) { + if (power_state.active_policy->ops->stop) + power_state.active_policy->ops->stop + (power_state.active_policy); + } + power_state.active_policy = p; + if (power_state.active_policy->ops->start) + power_state.active_policy->ops->start + (power_state.active_policy); + spin_unlock_irq(&power_state.lock); + return 0; +} +EXPORT_SYMBOL(pm_loss_set_policy); + +/* + * Default release function for pm_loss_policy object + */ +static void pm_loss_policy_release(struct kobject *kobj) +{ + struct pm_loss_policy *p; + p = to_policy(kobj); + pr_debug_pm_loss("pm_loss_policy_release(%p)\n", p); + kfree(p); +} + +/* + * Dummy nop policy implementation (all null pointers) + */ +static const struct pm_loss_policy_ops nop_policy_ops = { +}; + +static const struct sysfs_ops nop_sysfs_ops = { + .show = NULL, + .store = NULL, +}; + +static struct attribute *nop_default_attrs[] = { + NULL, +}; + +static struct kobj_type nop_ktype = { + .sysfs_ops = &nop_sysfs_ops, + .release = pm_loss_policy_release, + .default_attrs = nop_default_attrs, +}; + +/* + * Default policy implementation + */ + +struct pm_loss_default_policy_data { + struct pm_loss_default_policy_table *table; + struct list_head busses ; +}; + +struct pm_loss_default_policy_bus_data { + struct bus_type *bus; + int priority ; + struct list_head list; +}; + +static struct pm_loss_default_policy_item * +__find_bus_by_name(const char *name, + struct pm_loss_default_policy_table *table) +{ + int i; + struct pm_loss_default_policy_item *out = NULL; + for (i = 0; i < table->nitems; i++) + if (!strcmp(table->items[i].bus_name, name)) { + out = &table->items[i]; + break; + } + return out; +} + +static void __add_bus(struct pm_loss_default_policy_data *data, + struct pm_loss_default_policy_bus_data *new) +{ + struct pm_loss_default_policy_bus_data *bd; + list_for_each_entry(bd, &data->busses, list) + if (new->priority > bd->priority) { + list_add_tail(&new->list, &bd->list); + return; + } + list_add_tail(&new->list, &bd->list); +} + +static int pm_loss_default_policy_bus_added(struct pm_loss_policy *p, + struct bus_type *bus) +{ + struct pm_loss_default_policy_item *item ; + struct pm_loss_default_policy_data *pd = p->priv; + struct pm_loss_default_policy_bus_data *bd = + kzalloc(sizeof(struct pm_loss_default_policy_bus_data), + GFP_KERNEL); + + BUG_ON(!pd); + if (!bd) + return -ENOMEM; + pr_debug_pm_loss("pm_loss_default_policy_bus_added(), name = %s\n", + bus->name); + item = __find_bus_by_name(bus->name, pd->table); + if (!item) + return 0; + + pr_debug_pm_loss("bus_found\n"); + + bd->priority = item->bus_priority ; + bd->bus = bus ; + __add_bus(pd, bd); + + return 0; +} + +static int pm_loss_default_policy_bus_removed(struct pm_loss_policy *p, + struct bus_type *bus) +{ + struct pm_loss_default_policy_data *pd = p->priv; + struct pm_loss_default_policy_bus_data *bd, *tmp; + + BUG_ON(!pd); + list_for_each_entry_safe(bd, tmp, &pd->busses, list) + if (bd->bus == bus) { + list_del(&bd->list); + kfree(bd); + break; + } + return 0; +} + +struct bus_sys_pwr_changed_data { + struct bus_type *bus; + enum sys_power_state s; +}; + +static int __bus_sys_pwr_changed(struct device *dev, void *_d) +{ + struct bus_sys_pwr_changed_data *data = + (struct bus_sys_pwr_changed_data *)_d; + struct bus_type *bus = data->bus; + + BUG_ON(!dev); + BUG_ON(!bus); + + pr_debug_pm_loss("__bus_sys_pwr_changed(%p), bus %s\n", dev, + bus->name); + + if (bus->pm && bus->pm->power_changed) + bus->pm->power_changed(dev, data->s); + return 0; +} + + +static void +pm_loss_default_policy_sys_power_changed(struct pm_loss_policy *p, + enum sys_power_state s) +{ + struct pm_loss_default_policy_data *pd = p->priv; + struct pm_loss_default_policy_bus_data *bd ; + struct bus_sys_pwr_changed_data cb_data ; + + pr_debug_pm_loss("pm_loss_default_policy_sys_power_changed()\n"); + + BUG_ON(!pd); + + list_for_each_entry(bd, &pd->busses, list) { + struct bus_type *bus = bd->bus; + const struct dev_pm_ops *pm = bus->pm; + pr_debug_pm_loss("bus = %s\n", bus->name); + if (pm && pm->power_changed) { + cb_data.bus = bus ; cb_data.s = s; + pr_debug_pm_loss("before bus_for_each_dev\n"); + bus_for_each_dev(bus, NULL, (void *)&cb_data, + __bus_sys_pwr_changed); + } + } +} + +static int pm_loss_default_policy_start(struct pm_loss_policy *p) +{ + return 0; +} + +static void pm_loss_default_policy_stop(struct pm_loss_policy *p) +{ + struct pm_loss_default_policy_data *pd = p->priv; + if (pd->table->owner) + module_put(pd->table->owner); +} + +static const struct pm_loss_policy_ops default_policy_ops = { + .bus_added = pm_loss_default_policy_bus_added, + .bus_removed = pm_loss_default_policy_bus_removed, + .power_changed = pm_loss_default_policy_sys_power_changed, + .start = pm_loss_default_policy_start, + .stop = pm_loss_default_policy_stop, +}; + +/* sysfs stuff */ + +struct dflt_bus_table_attribute { + struct attribute attr; + ssize_t (*show)(struct pm_loss_policy *, + struct dflt_bus_table_attribute *attr, + char *buf); +}; +#define to_dflt_bus_table_attr(x) \ +container_of(x, struct dflt_bus_table_attribute, attr) + + +static ssize_t bus_table_show(struct pm_loss_policy *p, + struct dflt_bus_table_attribute *attr, + char *buf) +{ + struct pm_loss_default_policy_data *pd = p->priv; + struct pm_loss_default_policy_bus_data *bd ; + char *ptr = buf; + + BUG_ON(!pd); + + list_for_each_entry(bd, &pd->busses, list) { + struct bus_type *bus = bd->bus; + ptr += snprintf(ptr, PAGE_SIZE - (ptr - buf), "%s:%d,", + bus->name, bd->priority); + } + return ptr - buf; +} + +static struct dflt_bus_table_attribute bus_table_attribute = +__ATTR_RO(bus_table); + + +static struct attribute *dflt_default_attrs[] = { + &bus_table_attribute.attr, + NULL, +}; + +/* dflt sysfs ops */ + +static ssize_t dflt_show(struct kobject *kobj, struct attribute *_attr, + char *buf) +{ + if (!strcmp(_attr->name, "bus_table")) { + struct dflt_bus_table_attribute *attr = + to_dflt_bus_table_attr(_attr); + struct pm_loss_policy *p = + to_policy(kobj); + return attr->show(p, attr, buf); + } + return -EIO; +} + +static ssize_t dflt_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + return -EIO; +} + +static const struct sysfs_ops dflt_sysfs_ops = { + .show = dflt_show, + .store = dflt_store, +}; + +static struct kobj_type dflt_ktype = { + .sysfs_ops = &dflt_sysfs_ops, + .release = pm_loss_policy_release, + .default_attrs = dflt_default_attrs, +}; + +struct pm_loss_policy * +pm_loss_setup_default_policy(struct pm_loss_default_policy_table *table) +{ + struct pm_loss_policy *p ; + struct pm_loss_default_policy_data *data ; + + if (!table) + return NULL; + + if (table->owner && try_module_get(table->owner) < 0) + return NULL; + + data = kzalloc(sizeof(struct pm_loss_default_policy_data), GFP_KERNEL); + if (!data) + return NULL; + INIT_LIST_HEAD(&data->busses); + data->table = table; + + p = pm_loss_register_policy(&default_policy_ops, &dflt_ktype, + table->name, data); + if (!p) { + kfree(data); + return p; + } + return p; +} +EXPORT_SYMBOL(pm_loss_setup_default_policy); + +int pm_loss_shutdown_default_policy(struct pm_loss_policy *p) +{ + int ret; + pr_debug_pm_loss("pm_loss_shutdown_default_policy()\n"); + ret = pm_loss_unregister_policy(p); + if (ret < 0) + pr_debug_pm_loss("cannot unregister policy\n"); + return ret; +} +EXPORT_SYMBOL(pm_loss_shutdown_default_policy); + +static ssize_t active_policy_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", + kobject_name(&power_state.active_policy->kobj)); +} + + +static ssize_t active_policy_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + char pname[MAX_POLICY_NAME_LENGHT]; + int ret; + + sscanf(buf, "%" xstr(MAX_POLICY_NAME_LENGHT) "s", pname); + ret = pm_loss_set_policy(pname); + return ret < 0 ? ret : count; +} + +static struct kobj_attribute active_policy_attribute = + __ATTR(active_policy, 0644, active_policy_show, active_policy_store); + +#ifdef CONFIG_PM_LOSS_SIM +static ssize_t pwrfail_sim_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int n; + sscanf(buf, "%d", &n); + pm_loss_power_changed(n ? SYS_PWR_FAILING : SYS_PWR_GOOD); + return count; +} + + +static struct kobj_attribute pwrfail_sim_attribute = + __ATTR(pwrfail_sim, 0200, NULL, pwrfail_sim_store); +#endif + + +static struct attribute *loss_attrs[] = { + &active_policy_attribute.attr, +#ifdef CONFIG_PM_LOSS_SIM + &pwrfail_sim_attribute.attr, +#endif + NULL, +}; + +static struct attribute_group loss_attr_group = { + .attrs = loss_attrs, +}; + +/* + * Init function + */ +int __init pm_loss_init(void) +{ + struct pm_loss_policy *p ; + int ret = 0; + + pr_debug_pm_loss("pm_loss_init()\n"); + spin_lock_init(&power_state.lock); + power_state.state = SYS_PWR_GOOD; + init_waitqueue_head(&power_state.wq); + power_state.active_policy = NULL; + power_state.loss_kobj = kobject_create_and_add("loss", power_kobj); + if (!power_state.loss_kobj) { + ret = -ENOMEM; + goto err0; + } + power_state.policies = + kset_create_and_add("policies", NULL, power_state.loss_kobj); + if (!power_state.policies) { + ret = -ENOMEM; + goto err1; + } + ret = sysfs_create_group(power_state.loss_kobj, &loss_attr_group); + if (ret < 0) + goto err2; + pr_debug_pm_loss("invoking pm_loss_register_policy (nop)\n"); + p = pm_loss_register_policy(&nop_policy_ops, &nop_ktype, "nop", NULL); + BUG_ON(!p); + pr_debug_pm_loss("setting nop policy as current one\n"); + pm_loss_set_policy("nop"); + return ret; + + err2: + kset_unregister(power_state.policies); + err1: + kobject_put(power_state.loss_kobj); + err0: + return ret; +} + +core_initcall(pm_loss_init); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 698dde7..ad54ef5 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -10,6 +10,20 @@ static inline void pm_runtime_remove(struct device *dev) {} #endif /* !CONFIG_PM_RUNTIME */ +#ifdef CONFIG_PM_LOSS + +extern void pm_loss_init(void) ; +extern void pm_loss_on_bus_added(struct bus_type *); +extern void pm_loss_on_bus_removed(struct bus_type *); + +#else /* !CONFIG_PM_LOSS */ + +static inline void pm_loss_init(void) {} +static inline void pm_loss_on_bus_added(struct bus_type *b) {}; +static inline void pm_loss_on_bus_removed(struct bus_type *b) {}; + +#endif + #ifdef CONFIG_PM_SLEEP /* kernel/power/main.c */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 21415cc..0316cd0 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -40,6 +40,7 @@ extern void (*pm_power_off_prepare)(void); */ struct device; +enum sys_power_state ; #ifdef CONFIG_PM extern const char power_group_name[]; /* = "power" */ @@ -199,6 +200,10 @@ typedef struct pm_message { * power state if all of the necessary conditions are satisfied. Check * these conditions and handle the device as appropriate, possibly queueing * a suspend request for it. The return value is ignored by the PM core. + * + * @power_changed: device power has changed state (used by pm_loss). This + * is invoked whenever system power changes its state (from GOOD to + * FAILING or viceversa) */ struct dev_pm_ops { @@ -219,6 +224,7 @@ struct dev_pm_ops { int (*runtime_suspend)(struct device *dev); int (*runtime_resume)(struct device *dev); int (*runtime_idle)(struct device *dev); + int (*power_changed)(struct device *, enum sys_power_state); }; #ifdef CONFIG_PM_SLEEP @@ -421,6 +427,16 @@ enum rpm_request { struct wakeup_source; +/** + * System power states + * + */ +enum sys_power_state { + SYS_PWR_GOOD = 0, + SYS_PWR_FAILING, + SYS_PWR_NOTIFYING, +}; + struct dev_pm_info { pm_message_t power_state; unsigned int can_wakeup:1; diff --git a/include/linux/pm_loss.h b/include/linux/pm_loss.h new file mode 100644 index 0000000..46af47c --- /dev/null +++ b/include/linux/pm_loss.h @@ -0,0 +1,109 @@ +/* + * pm_loss.h - Power loss management related functions, header file + * + * Copyright (C) 2011 Davide Ciminaghi + * Copyright (C) 2011 BTicino S.p.A. + * + * This file is released under the GPLv2. + */ + +#ifndef _LINUX_PM_LOSS_H +#define _LINUX_PM_LOSS_H + +#include +#include + +struct pm_loss_policy ; + +struct pm_loss_policy_ops { + int (*bus_added)(struct pm_loss_policy *, + struct bus_type *); + int (*bus_removed)(struct pm_loss_policy *, + struct bus_type *); + void (*power_changed)(struct pm_loss_policy *, + enum sys_power_state); + int (*start)(struct pm_loss_policy *); + void (*stop)(struct pm_loss_policy *); +}; + +struct pm_loss_policy { + const struct pm_loss_policy_ops *ops; + void *priv; + struct list_head list; + struct kobject kobj; +}; + +#define to_policy(k) container_of(k, struct pm_loss_policy, kobj) + +struct pm_loss_default_policy_table { + struct module *owner ; + const char *name ; + struct pm_loss_default_policy_item *items; + int nitems; +}; + +#ifdef CONFIG_PM_LOSS + +#ifdef CONFIG_PM_LOSS_DEBUG +#define PM_LOSS_STR "pm_loss:" +#define pr_debug_pm_loss(a, args...) \ +printk(KERN_INFO PM_LOSS_STR a, ## args) +#else +#define pr_debug_pm_loss(a, args...) +#endif + +extern void pm_loss_power_changed(enum sys_power_state s); + +extern struct pm_loss_policy * +pm_loss_register_policy(const struct pm_loss_policy_ops *ops, + struct kobj_type *ktype, + const char *name, void *priv); + +extern int pm_loss_unregister_policy(struct pm_loss_policy *); + +extern int pm_loss_set_policy(const char *name); + +struct pm_loss_default_policy_item { + const char *bus_name; + int bus_priority ; +}; + +extern struct pm_loss_policy * +pm_loss_setup_default_policy(struct pm_loss_default_policy_table *); + +extern int pm_loss_shutdown_default_policy(struct pm_loss_policy *); + +#else /* !CONFIG_PM_LOSS */ + +static inline struct pm_loss_policy * +pm_loss_register_policy(const struct pm_loss_policy_ops *ops, + void *priv) +{ + return NULL ; +} + +static inline int pm_loss_unregister_policy(struct pm_loss_policy *p) +{ + return -ENOSYS ; +} + +static inline int pm_loss_set_policy(struct pm_loss_policy *p) +{ + return -ENOSYS ; +} + +static inline struct pm_loss_policy * +pm_loss_setup_default_policy(struct pm_loss_default_policy_table *t) +{ + return NULL; +} + +static inline int +pm_loss_shutdown_default_policy(struct pm_loss_policy *p) +{ + return -ENOSYS; +} + +#endif /* !CONFIG_PM_LOSS */ + +#endif /* _LINUX_PM_LOSS_H */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 2657299..7527847 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -236,6 +236,33 @@ config PM_RUNTIME responsible for the actual handling of the autosuspend requests and wake-up events. +config PM_LOSS + bool "Help drivers reacting to system power losses" + depends on PM + ---help--- + Enable power loss management. On some embedded systems, an + asynchronous power failure notification event is sometimes available + less than one second before the actual power failure takes place. + Custom policies can be setup to react to such an event. See + Documentation/power/loss.txt for more details. + If unsure, say No. + +config PM_LOSS_SIM + bool "Simulate power fail events" + depends on PM_LOSS + ---help--- + Add the /sys/power/loss/pwrfail_sim file. Do + echo 1 > /sys/power/loss/pwrfail_sim + to simulate a power failure and + echo 0 > /sys/power/loss/pwrfail_sim + to simulate a power resume + +config PM_LOSS_DEBUG + bool "Enable power fail debug" + depends on PM_LOSS + ---help--- + This option enables debugging printk's for the PM_LOSS framework + config PM_OPS bool depends on PM_SLEEP || PM_RUNTIME -- 1.7.0.4 From lamiaposta71 at gmail.com Thu May 12 12:11:04 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Thu, 12 May 2011 19:11:04 +0200 Subject: [PATCH 3/4] mmc: bus and block device drivers: support for pm_loss In-Reply-To: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> Message-ID: <1305220265-9020-4-git-send-email-lamiaposta71@gmail.com> From: Davide Ciminaghi Signed-off-by: Davide Ciminaghi Signed-off-by: Raffaele Recalcati --- drivers/mmc/card/block.c | 48 +++++++++++++++++++++++++++++++++++++++++++- drivers/mmc/core/bus.c | 49 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index bfc8a8a..c88afef 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -37,6 +37,9 @@ #include #include +#include +#include + #include #include @@ -755,14 +758,55 @@ static int mmc_blk_resume(struct mmc_card *card) } return 0; } -#else + +#ifdef CONFIG_PM_LOSS + +#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) + +static int mmc_blk_power_changed(struct device *dev, + enum sys_power_state s) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_blk_data *md = mmc_get_drvdata(card); + + switch (s) { + case SYS_PWR_GOOD: + pr_debug_pm_loss("mmc_blk_power_changed(%d)\n", s); + mmc_blk_set_blksize(md, card); + mmc_queue_resume(&md->queue); + break; + case SYS_PWR_FAILING: + pr_debug_pm_loss("mmc_blk_power_changed(%d)\n", s); + mmc_queue_suspend(&md->queue); + break; + default: + BUG(); + } + return 0; +} + +static const struct dev_pm_ops mmc_blk_pm_ops = { + .power_changed = mmc_blk_power_changed, +}; + +#define MMC_BLK_DEV_PM_OPS_PTR (&mmc_blk_pm_ops) + +#else /* !CONFIG_PM_LOSS */ + +#define MMC_BLK_DEV_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_LOSS */ + +#else /* !CONFIG_PM */ #define mmc_blk_suspend NULL #define mmc_blk_resume NULL -#endif +#define MMC_BLK_DEV_PM_OPS_PTR NULL +#endif /* !CONFIG_PM */ static struct mmc_driver mmc_driver = { .drv = { .name = "mmcblk", + .pm = MMC_BLK_DEV_PM_OPS_PTR, }, .probe = mmc_blk_probe, .remove = mmc_blk_remove, diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 63667a8..548d3a9 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -19,6 +19,8 @@ #include #include +#include + #include "core.h" #include "sdio_cis.h" #include "bus.h" @@ -163,19 +165,56 @@ static int mmc_runtime_idle(struct device *dev) return pm_runtime_suspend(dev); } +#define MMC_PM_RUNTIME_OPS_INIT \ +.runtime_suspend = mmc_runtime_suspend, \ +.runtime_resume = mmc_runtime_resume, \ +.runtime_idle = mmc_runtime_idle, + +#else /* !CONFIG_PM_RUNTIME */ + +#define MMC_PM_RUNTIME_OPS_INIT + +#endif /* !CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_LOSS + +static int mmc_bus_power_changed(struct device *dev, + enum sys_power_state s) +{ + int ret = 0; + + pr_debug_pm_loss("mmc_bus_power_changed()\n"); + + if (dev->driver && dev->driver->pm && + dev->driver->pm->power_changed) + ret = dev->driver->pm->power_changed(dev, s); + + return ret; +} + +#define MMC_PM_LOSS_OPS_INIT \ +.power_changed = mmc_bus_power_changed, + +#else /* !CONFIG_PM_LOSS */ + +#define MMC_PM_LOSS_OPS_INIT + +#endif /* !CONFIG_PM_LOSS */ + +#if defined CONFIG_PM_RUNTIME || defined CONFIG_PM_LOSS + static const struct dev_pm_ops mmc_bus_pm_ops = { - .runtime_suspend = mmc_runtime_suspend, - .runtime_resume = mmc_runtime_resume, - .runtime_idle = mmc_runtime_idle, + MMC_PM_RUNTIME_OPS_INIT + MMC_PM_LOSS_OPS_INIT }; #define MMC_PM_OPS_PTR (&mmc_bus_pm_ops) -#else /* !CONFIG_PM_RUNTIME */ +#else /* !(CONFIG_PM_RUNTIME || CONFIG_PM_LOSS) */ #define MMC_PM_OPS_PTR NULL -#endif /* !CONFIG_PM_RUNTIME */ +#endif /* !(CONFIG_PM_RUNTIME || CONFIG_PM_LOSS) */ static struct bus_type mmc_bus_type = { .name = "mmc", -- 1.7.0.4 From lamiaposta71 at gmail.com Thu May 12 12:11:05 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Thu, 12 May 2011 19:11:05 +0200 Subject: [PATCH 4/4] DaVinci: vpfe: support for pm_loss In-Reply-To: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> Message-ID: <1305220265-9020-5-git-send-email-lamiaposta71@gmail.com> From: Raffaele Recalcati Signed-off-by: Raffaele Recalcati --- drivers/media/video/davinci/vpfe_capture.c | 45 ++++++++++++++++++++++++++++ 1 files changed, 45 insertions(+), 0 deletions(-) diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 353eada..21fa9bf 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -74,6 +74,8 @@ #include #include #include +#include +#include #include "ccdc_hw_device.h" static int debug; @@ -2051,6 +2053,46 @@ static int __devexit vpfe_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_LOSS +static int vpfe_capture_power_changed(struct device *dev, + enum sys_power_state s) +{ + int ret; + struct vpfe_device *vpfe_dev = dev_get_drvdata(dev); + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + + if (!sdinfo) + return -EINVAL; + + if (!vpfe_dev->started) { + pr_debug_pm_loss("vpfe_capture_power_changed(%d) " + "BUT NOTHING TO DO\n", s); + return 0; + } + + sdinfo = vpfe_dev->current_subdev; + + switch (s) { + case SYS_PWR_GOOD: + pr_debug_pm_loss("vpfe_capture_power_changed(%d)\n", s); + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 1); + break; + case SYS_PWR_FAILING: + pr_debug_pm_loss("vpfe_capture_power_changed(%d)\n", s); + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 0); + break; + default: + BUG(); + ret = -ENODEV; + } + return ret; +} +#endif /* CONFIG_PM_LOSS */ + static int vpfe_suspend(struct device *dev) { return 0; @@ -2064,6 +2106,9 @@ static int vpfe_resume(struct device *dev) static const struct dev_pm_ops vpfe_dev_pm_ops = { .suspend = vpfe_suspend, .resume = vpfe_resume, +#ifdef CONFIG_PM_LOSS + .power_changed = vpfe_capture_power_changed, +#endif }; static struct platform_driver vpfe_driver = { -- 1.7.0.4 From linux at arm.linux.org.uk Thu May 12 12:45:46 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Thu, 12 May 2011 18:45:46 +0100 Subject: [RFC PATCH v3] Consolidate SRAM support In-Reply-To: <20110415130607.GM1611@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> Message-ID: <20110512174546.GB8633@n2100.arm.linux.org.uk> 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. Signed-off-by: Russell King --- This version fixes the davinci pm free, and adds updates for the davinci pcm driver. As I don't know what's happening with Jean's patch tweaking the genpool allocator, I've kept my version. This still suffers from the "only one region per pvpool" problem which I believe Jean's patch doesn't suffer. 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 | 20 +++----- 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 ++- sound/soc/davinci/davinci-pcm.c | 9 ++-- 21 files changed, 182 insertions(+), 144 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 903c9c4..7a4a625 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -862,6 +862,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 @@ -875,6 +876,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..abee3ca 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 6ea9b6f..4ae455e 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_ARCH_IXP2000) += 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 index e69de29..748367a 100644 --- a/arch/arm/common/pv-pool.c +++ 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 index e69de29..0834216 100644 --- a/arch/arm/include/asm/pv-pool.h +++ 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 b95b919..09f6c12 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 a3a94e9..9bda687 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 4c82c27..3949ed7 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..14676de 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..c2f9767 100644 --- a/arch/arm/mach-davinci/pm.c +++ b/arch/arm/mach-davinci/pm.c @@ -27,14 +27,9 @@ #define DEEPSLEEP_SLEEPCOUNT_MASK 0xFFFF static void (*davinci_sram_suspend) (struct davinci_pm_config *); +static void *davinci_sram_suspend_mem; 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,14 +118,14 @@ static int __init davinci_pm_probe(struct platform_device *pdev) return -ENOENT; } - davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); - if (!davinci_sram_suspend) { + davinci_sram_suspend_mem = pv_pool_alloc(davinci_pv_pool, + davinci_cpu_suspend_sz, NULL); + if (!davinci_sram_suspend_mem) { dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); return -ENOMEM; } - - davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, - davinci_cpu_suspend_sz); + davinci_sram_suspend = fncpy(davinci_sram_suspend_mem, + davinci_cpu_suspend, davinci_cpu_suspend_sz); suspend_set_ops(&davinci_pm_ops); @@ -139,7 +134,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_mem, + davinci_cpu_suspend_sz); return 0; } diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c index db0f778..219d4c5 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..fb23497 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..a989bff 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..30bccc8 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..711931a 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..68f57ff 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 e67b566..0c558e7 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; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 9d35b8c..621e2a1 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -232,14 +232,14 @@ static int allocate_sram(struct snd_pcm_substream *substream, unsigned size, { struct snd_dma_buffer *buf = &substream->dma_buffer; struct snd_dma_buffer *iram_dma = NULL; - dma_addr_t iram_phys = 0; + phys_addr_t iram_phys; void *iram_virt = NULL; if (buf->private_data || !size) return 0; ppcm->period_bytes_max = size; - iram_virt = sram_alloc(size, &iram_phys); + iram_virt = pv_pool_alloc(davinci_pv_pool, size, &iram_phys); if (!iram_virt) goto exit1; iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL); @@ -253,7 +253,7 @@ static int allocate_sram(struct snd_pcm_substream *substream, unsigned size, return 0; exit2: if (iram_virt) - sram_free(iram_virt, size); + pv_pool_free(davinci_pv_pool, iram_virt, size); exit1: return -ENOMEM; } @@ -803,7 +803,8 @@ static void davinci_pcm_free(struct snd_pcm *pcm) buf->area = NULL; iram_dma = buf->private_data; if (iram_dma) { - sram_free(iram_dma->area, iram_dma->bytes); + pv_pool_free(davinci_pv_pool, iram_dma->area, + iram_dma->bytes); kfree(iram_dma); } } From rjw at sisk.pl Thu May 12 14:27:44 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Thu, 12 May 2011 21:27:44 +0200 Subject: pm loss development In-Reply-To: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> Message-ID: <201105122127.44484.rjw@sisk.pl> On Thursday, May 12, 2011, Raffaele Recalcati wrote: > What happen normally in runtime pm implementation is that every devices > are switched off and are enabled only when needed. > In our case instead we have a completely functional embedded system and, > when an asyncrhonous event appear, we have only some tens milliseconds > before the actual power failure takes place. > This patchset add a support in order to switch off not vital part of the system, > in order to allow the board to survive longer. > This allow the possibility to save important data. OK, so first, who decides what parts of the system are vital and what aren't? Thanks, Rafael From rjw at sisk.pl Thu May 12 14:28:53 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Thu, 12 May 2011 21:28:53 +0200 Subject: [PATCH 1/4] export bus_kset In-Reply-To: <1305220265-9020-2-git-send-email-lamiaposta71@gmail.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <1305220265-9020-2-git-send-email-lamiaposta71@gmail.com> Message-ID: <201105122128.54172.rjw@sisk.pl> On Thursday, May 12, 2011, Raffaele Recalcati wrote: > From: Davide Ciminaghi Please explain why you need to export it, what the alternatives are and why you think this approach is better than the alternatives. Thanks, Rafael > Signed-off-by: Davide Ciminaghi > --- > drivers/base/bus.c | 3 ++- > include/linux/kobject.h | 2 ++ > 2 files changed, 4 insertions(+), 1 deletions(-) > > diff --git a/drivers/base/bus.c b/drivers/base/bus.c > index 000e7b2..2134248 100644 > --- a/drivers/base/bus.c > +++ b/drivers/base/bus.c > @@ -158,7 +158,8 @@ static const struct kset_uevent_ops bus_uevent_ops = { > .filter = bus_uevent_filter, > }; > > -static struct kset *bus_kset; > +struct kset *bus_kset; > +EXPORT_SYMBOL(bus_kset); > > > #ifdef CONFIG_HOTPLUG > diff --git a/include/linux/kobject.h b/include/linux/kobject.h > index 8f6d121..456b20d 100644 > --- a/include/linux/kobject.h > +++ b/include/linux/kobject.h > @@ -205,6 +205,8 @@ extern struct kobject *power_kobj; > /* The global /sys/firmware/ kobject for people to chain off of */ > extern struct kobject *firmware_kobj; > > +extern struct kset *bus_kset ; > + > #if defined(CONFIG_HOTPLUG) > int kobject_uevent(struct kobject *kobj, enum kobject_action action); > int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, > From rjw at sisk.pl Thu May 12 14:57:46 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Thu, 12 May 2011 21:57:46 +0200 Subject: [PATCH 2/4] PM / Loss: power loss management In-Reply-To: <1305220265-9020-3-git-send-email-lamiaposta71@gmail.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <1305220265-9020-3-git-send-email-lamiaposta71@gmail.com> Message-ID: <201105122157.46895.rjw@sisk.pl> Hi, On Thursday, May 12, 2011, Raffaele Recalcati wrote: > From: Davide Ciminaghi > > On some embedded systems, an asynchronous power failure notification event is > available some tens to hundreds milliseconds before the actual power failure > takes place. > Such an event can be used to trigger some actions, typically shutting down > all non-vital power sinks, thus allowing the board to survive longer to > temporary power losses. > > Signed-off-by: Davide Ciminaghi > Signed-off-by: Raffaele Recalcati > --- > Documentation/power/loss.txt | 191 +++++++++++++ > drivers/base/bus.c | 6 + > drivers/base/init.c | 1 + > drivers/base/platform.c | 14 + > drivers/base/power/Makefile | 2 + > drivers/base/power/loss.c | 648 ++++++++++++++++++++++++++++++++++++++++++ > drivers/base/power/power.h | 14 + > include/linux/pm.h | 16 + > include/linux/pm_loss.h | 109 +++++++ > kernel/power/Kconfig | 27 ++ > 10 files changed, 1028 insertions(+), 0 deletions(-) > create mode 100644 Documentation/power/loss.txt > create mode 100644 drivers/base/power/loss.c > create mode 100644 include/linux/pm_loss.h > > diff --git a/Documentation/power/loss.txt b/Documentation/power/loss.txt > new file mode 100644 > index 0000000..10da89c > --- /dev/null > +++ b/Documentation/power/loss.txt > @@ -0,0 +1,191 @@ > +**** POWER LOSS MANAGEMENT **** > + > +Davide Ciminaghi 2011 > + > +1. Overview > + > +On some embedded systems, an asynchronous power failure notification event is > +available some tens to hundreds milliseconds before the actual power failure > +takes place. > +Such an event can be used to trigger some actions, typically shutting down > +all non-vital power sinks, thus allowing the board to survive longer to > +temporary power losses. Sometimes, also flash-based block devices can be > +stopped after a power loss event notification has been received. This should > +be useful for mmc devices, for which a sudden power failure while a write > +command is being executed can threaten file system integrity even in case a > +journalling fs is in use. > +Generally speaking, one can expect the course of action taken when a power > +failure warning is received to be deeply system specific. Similarly, the > +mechanism used for power failure notifications can equally be board/platform > +specific. For these reasons, support for power loss management has been split > +into three parts: > + > + + Generic code (board and driver independent). > + + Board dependent code. > + + Power loss policy definition. > + > +The generic part of the code is located under drivers/base/power/loss.c . > +On the other hand, board dependent code and power loss policies definitions > +should live somewhere in the platform/board specific files. I'm not really sure about that. Do you want to hardcode policies in some platform/board-specific files inside of the kernel? > +The header file include/linux/pm_loss.h contains all the pm_loss function > +prototypes, together with definitions of data structures. > +For what concerns power loss policies, the framework already contains a couple > +of predefined policies: "nop" and "default" (see later paragraphs for more > +details). I think it would be cleaner to split the patch so that the actual functionality is added first and the policy part goes in a separate patch on top of that. > + > +1.1 Sysfs interface. > + > +It can be useful (for instance during tests), to be able to quickly switch > +from one power loss policy to another, or to simulate power fail and resume > +events. To this end, a sysfs interface of the following form has been devised: > + > +/sys/power/loss + > + | > + +-- active_policy > + | > + +-- policies -+ > + | | > + | +-- nop > + | | > + | +-- default + > + | | | > + | | +-- bus_table > + | | > + | +-- .... > + | > + +-- pwrfail_sim > + > +2. Details > + > +2.1 Sending events to the power loss management core. > + > +The board specific code shall trigger a power failure event notification by > +calling pm_loss_power_changed(SYS_PWR_FAILING). > +In case of a temporary power loss, the same function shall be called with > +SYS_PWR_GOOD argument on power resume. pm_loss_power_changed() can sleep, so > +it shall not be called from interrupt context. > + > +2.3 Effects on bus and device drivers. > + > +One more entry has been added to the device PM callbacks: > + > + int (*power_changed)(struct device *, enum sys_power_state); Please don't use the second argument. Make it two (or as many as you need) callbacks each with one struct device * argument instead (following the convention we have in struct dev_pm_ops already). That said, I'm not quite sure we _need_ those additional callbacks at all. Your example mmc implementation appears to use a simple "runtime suspend on power fail, runtime resume on power good" approach, for which it's not necessary to add any new callbacks. > +This function can be optionally invoked by power loss policies in case of > +system power changes (loss and/or resume). Of course every bus or device driver > +can react to such events in some specific way. For instance the mmc block > +driver will probably block its request queue during a temporary power loss. I think you'd have to deal with user space somehow before suspending devices. Runtime PM suspends devices when they aren't in use (as indicated by the usage counters), but in this power loss case we may really end up suspending devices that _are_ in use, right? > +2.3.1 The platform bus. > + > +For what concerns platform bus drivers, platform specific code can override > +the power_changed() callback : > + > +platform_pm_loss_power_changed(struct device *dev, enum sys_power_state s) > + > +whose default (empty) version is defined as a __weak symbol in > +drivers/base/platform.c. Please don't use any more __weak symbols in that file. > +2.4 Power loss policies. > + > +A power loss policy can be registered via the pm_loss_register_policy() > +function, which receives: > + > + + A pointer to a struct pm_loss_policy_ops , which hosts the pointers > + to the policy's specific callbacks (see below for more details). > + + A pointer to a struct kobj_type, which will allow the pm loss core > + to setup the policy related directory under /sys/power/loss/policies. > + See Documentation/kobject.txt for more details on struct kobj_type. > + + A pointer to an array of chars containing the name of the policy. > + + A pointer to the policy's private data (void *). This sounds like quilte a bit of new infrastructure and it seems it might be implemented in a more straightforward way (presumably in a less general but still useful manner). > +A power loss policy can define the following callbacks: > + > + + int (*bus_added)(struct pm_loss_policy *, struct bus_type *) : this > + is invoked whenever a new bus has been registered within the system. > + Since power related events are often managed at bus level, it can be > + useful for the policy to have a list of available busses within the > + system. When a policy is registered, this callback is invoked once > + for every already registered bus. Well, we have a list of registered bus types anyway. They may or may not register "power loss" callbacks. Why don't you simply execute the callbacks from the bus types that defined them? What exactly is the purpose of the new list? > + + int (*bus_removed)(struct pm_loss_policy *, struct bus_type *): > + this is invoked when a bus is removed from the system. > + + int (*start)(struct pm_loss_policy *): this is called when a policy > + becomes active. > + + void (*stop)(struct pm_loss_policy *): this is called when a policy > + becomes inactive. What are the start/stop callbacks needed for? IOW, what's your envisioned use case for those callbacks? > +2.4.1 The nop predefined policy > + > +The nop policy is the first active policy when the pm loss framework is > +initialized. It just does nothing in case of power loss / power resume > +events. > + > +2.4.2 The default predefined policy > + > +When a power failure warning is received, the default policy walks through a > +list of critical busses and invokes their drivers' power_changed() callback. Why not to walk all bus types here? > +The default policy can be customized and registered by calling: > + > + pm_loss_setup_default_policy(struct pm_loss_default_policy_table *); > + > +This function receives a pointer to a pm_loss_default_policy_table structure, > +which defines a priority ordered list of critical buffers: > + > + struct pm_loss_default_policy_table { > + struct module *owner ; > + const char *name ; > + struct pm_loss_default_policy_item *items; > + int nitems; > + }; > + > +Here's a short description of such structure: > + > + + owner : shall point to the module registering the table (or NULL > + if the table is not instantiated by a module). > + + name : this is the name given to this particular customization of > + the default policy. > + + items : pointer to an array of table items. > + + nitems : number of the items in the array. > + > +Each item is a struct pm_loss_default_policy_item, defined as follows: > + > + struct pm_loss_default_policy_item { > + const char *bus_name; > + int bus_priority ; > + }; > + > +where bus_name is the name of a bus and bus_priority is a numerical priority > +of the bus itself. Numerically higher priorities correspond to more prioritary > +busses. So I can understand the need for priorities, but why do you use the name instead of a pointer to struct bus_type? > + > +2.4.3 Activating a specific policy. > + > +A policy can be activated: > + > + + From within the kernel by calling pm_loss_set_policy(char *name). > + The argument passed to this function shall be the name of the policy > + to be activated. > + > + + From user space by writing the name of the policy to be activated > + to /sys/power/loss/active_policy. > + > +2.4.4 Unregistering a policy > + > +For a generic user defined policy, just call : > + > + pm_loss_unregister_policy(struct pm_loss_policy *); > + > +For a default policy customization: > + > + pm_loss_shutdown_default_policy(struct pm_loss_policy *); > + > +3. Kernel configuration > + > +The following configuration options have been defined: > + > + CONFIG_PM_LOSS : this enables the generic pm_loss framework. > + CONFIG_PM_LOSS_SIM : this adds the pwrfail_sim file to the sysfs interface > + and allows power loss events simulation. > + CONFIG_PM_LOSS_DEBUG : this option enables some debug printk's . > + CONFIG_PM_LOSS_TEST: this enables the compilation of a sample test module > + containing a customized default policy definition OK, so I think that this feature may be kind of useful, but it will require some work before it's ready to go into the kernel. Please separate the policy part for starters and let's see what's left. Thanks, Rafael From lamiaposta71 at gmail.com Fri May 13 01:39:42 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Fri, 13 May 2011 08:39:42 +0200 Subject: pm loss development In-Reply-To: <201105122127.44484.rjw@sisk.pl> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105122127.44484.rjw@sisk.pl> Message-ID: Hi Rafael, 2011/5/12 Rafael J. Wysocki : > On Thursday, May 12, 2011, Raffaele Recalcati wrote: >> What happen normally in runtime pm implementation is that every devices >> are switched off and are enabled only when needed. >> In our case instead we have a completely functional embedded system and, >> when an asyncrhonous event appear, we have only some tens milliseconds >> before the actual power failure takes place. >> This patchset add a support in order to switch off not vital part of the system, >> in order to allow the board to survive longer. >> This allow the possibility to save important data. > > OK, so first, who decides what parts of the system are vital and what aren't? Take a quick look at Documentation/power/loss.txt paragrpah "2.4 Power loss policies". You can decide what can be powered off. Thanks, Raffaele -- www.opensurf.it From linux at arm.linux.org.uk Fri May 13 04:11:23 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 13 May 2011 10:11:23 +0100 Subject: [RFC PATCH v3] Consolidate SRAM support In-Reply-To: References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110512174546.GB8633@n2100.arm.linux.org.uk> Message-ID: <20110513091123.GA20941@n2100.arm.linux.org.uk> On Fri, May 13, 2011 at 09:30:14AM +0200, Jean Pihet wrote: > On Thu, May 12, 2011 at 7:45 PM, Russell King - ARM Linux > wrote: > > This version fixes the davinci pm free, and adds updates for the > > davinci pcm driver. As I don't know what's happening with Jean's > > patch tweaking the genpool allocator, I've kept my version. > > Sorry I do not get the question. The latest changes I submitted have > been merged in mainline as 'ARM: 6649/1: omap: use fncpy to copy the > PM code functions to SRAM' [1] . Thanks for that! > This change is only about the simple linear SRAM allocator, not the > genpool allocator. The wrong Jean! I meant Jean-Christophe PLAGNIOL-VILLARD. From santosh.shilimkar at ti.com Fri May 13 04:19:50 2011 From: santosh.shilimkar at ti.com (Santosh Shilimkar) Date: Fri, 13 May 2011 14:49:50 +0530 Subject: [RFC PATCH v3] Consolidate SRAM support In-Reply-To: <20110512174546.GB8633@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110512174546.GB8633@n2100.arm.linux.org.uk> Message-ID: <4DCCF7B6.8020305@ti.com> Russell, On 5/12/2011 11:15 PM, Russell King - ARM Linux wrote: > On Fri, Apr 15, 2011 at 02:06:07PM +0100, Russell King - ARM Linux wrote: > This is work in progress. > Tried this patch on OMAP and found couple of issues. 1. Compilation break. Below is the fix for the same. diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index 68f57ff..78d1af4 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -75,6 +75,7 @@ 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 -- 1.6.0.4 2. The boot takes data abort while allocating memory for struct pv_pool. Here is the back-trace. I haven't debugged it further though. -000|NSR:0xFFFF0010(asm) -->|exception -001|kmem_cache_alloc_trace(size = 0x0C, cachep = 0x0, flags = 0x80D0) -002|pv_pool_create(addr = 0xFE400000, phys = 0x40200000, len = 0x00010000, min_alloc_order = 0x3) -003|omap_sram_init() -004|paging_init(?) -005|setup_arch(cmdline_p = 0xC058BFE4) -006|start_kernel() -007|NSR:0x8000803C(asm) ---|end of frame | Regards Santosh From yikim at aprocctv.com Fri May 13 04:33:51 2011 From: yikim at aprocctv.com (=?ks_c_5601-1987?B?sei/68DP?=) Date: Fri, 13 May 2011 18:33:51 +0900 Subject: linphone porting for dm36x Message-ID: <2799AA0D03934E65B0E0526155327E4B@Yong> Hi everyone I am trying to find linphone(voip application)porting for DM365 TI DSP please informe me -------------- next part -------------- An HTML attachment was scrubbed... URL: From subhasish at mistralsolutions.com Fri May 13 05:55:56 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 13 May 2011 16:25:56 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105112203.54838.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105102344.58598.arnd@arndb.de> <82D549BB195345A1A189D569952B387C@subhasishg> <201105112203.54838.arnd@arndb.de> Message-ID: >> >> Ok, thanks for the clarification. >> Instead of passing the device name, will it be ok to pass the mfd_id. >> The benefit will be that I can use the ID directly as an array >> index for the mfd_cell entries. > > I think a device name would be clearer here, especially in order > to avoid conflicts when the list gets extended in different ways > depending on which kernel runs. > > We had a little discussion at the Linaro Developer Summit about your > driver and mfd drivers in general. There was a general feeling among > some people (including me) that by the point you dynamically create > the subdevices, MFD is probably not the right abstraction any more, > as it does not provide any service that you need. > > Instead, maybe you can simply call platform_device_register > at that stage to create the children and not use MFD at all. > > Samuel, can you comment on this as well? Do you still see pruss > as an MFD driver when the uses are completely dynamic and determined > by the firmware loaded into it? > Hi Arnd, But in that case, where do I fit my driver. It's a microcontroller inside of a processor, guess that's unique for Linux. Will a misc device be a better choice. In that case I can remove the MFD cell from the board_file and add an array of platform_device for the UART & CAN. Regarding device register, I will have to implement something similar to mfd_add_devices and mark the dev->parent. Did I miss something or will that be fine. From subhasish at mistralsolutions.com Fri May 13 07:10:42 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 13 May 2011 17:40:42 +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> <0B813DC0439B438BBB2B66A88B1A5F7F@subhasishg> Message-ID: <7C637C15A1AB48869F938B97BBDF22D8@subhasishg> >> >> Say, if the driver is loaded as a module. >> If I allocate the sram in the platform code, how to I >> free it when the driver is unloaded. > > This is what I said in my last e-mail. What is > the issue you see with this approach? > > | Thanks for the clarification. In this case, the driver > | should use platform callbacks to get/put fast fifo > | space. In case this callback is not populated by the > | platform or returns an error, the driver should fall > | back to allocating from DDR. > ok, so what you are suggesting is that I implement some callbacks (like .fifo_alloc, .fifo_dealloc) which can allocate memory using sram_alloc. My doubt is, if already such API's are there (by Russel) or you are suggesting to implement them. From rjw at sisk.pl Fri May 13 11:54:57 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Fri, 13 May 2011 18:54:57 +0200 Subject: pm loss development In-Reply-To: References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105122127.44484.rjw@sisk.pl> Message-ID: <201105131854.57854.rjw@sisk.pl> On Friday, May 13, 2011, Raffaele Recalcati wrote: > Hi Rafael, > > 2011/5/12 Rafael J. Wysocki : > > On Thursday, May 12, 2011, Raffaele Recalcati wrote: > >> What happen normally in runtime pm implementation is that every devices > >> are switched off and are enabled only when needed. > >> In our case instead we have a completely functional embedded system and, > >> when an asyncrhonous event appear, we have only some tens milliseconds > >> before the actual power failure takes place. > >> This patchset add a support in order to switch off not vital part of the system, > >> in order to allow the board to survive longer. > >> This allow the possibility to save important data. > > > > OK, so first, who decides what parts of the system are vital and what aren't? > > Take a quick look at Documentation/power/loss.txt paragrpah "2.4 > Power loss policies". > You can decide what can be powered off. I read the patches. My question was about the general idea of who should be responsible of making these decisions. Thanks, Rafael From bengardiner at nanometrics.ca Fri May 13 16:26:45 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 13 May 2011 17:26:45 -0400 Subject: [PATCH 1/2] [RFC] ASoC: davinci-mcasp: enable ping-pong SRAM buffers In-Reply-To: References: Message-ID: <771769182c177a9cd9472392e1d5a92d21025aa2.1305321198.git.bengardiner@nanometrics.ca> The davinci-i2s driver copies the platform data for playback and capture sram sizes which is in turn used by davinci-pcm to allocate ping-pong buffers. Copy also the platform data in the davinci-mcasp probe(). Not-signed-off-by: Ben Gardiner --- sound/soc/davinci/davinci-mcasp.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 4ddc6d3..8566238 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -909,6 +909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; + dma_data->sram_size = pdata->sram_size_playback; dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + mem->start); @@ -925,6 +926,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; + dma_data->sram_size = pdata->sram_size_capture; dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + mem->start); -- 1.7.4.1 From bengardiner at nanometrics.ca Fri May 13 16:26:44 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 13 May 2011 17:26:44 -0400 Subject: [PATCH RFC 0/2] da850 mcasp playback ping-pong buffers don't work Message-ID: Hi All, I've been testing playback on the da850evm. The good news is on 2.6.39-rc7 playback works fine. The bad news is that if the patches in the series are applied -- playback does not work. The first patch copies the sram-size platform data to the dma parameters used by davinci-pcm the same way that the existing davinci-i2s driver does. The second patch assigns a buffer size to the playback sram platform data and sets the queues as per Troy's introduction of ping-pong buffers. I was hoping this would result in playback of audio; but instead there is soft click and then silence. I added pointer debug and observed that the period_update and hwptr_update events progress; but that's as far as I have probed. Does anyone have any clues why ping-pong buffers on da850 to the McASP would work? Or any clues why the patches are broken? Best Regards, Ben Gardiner Ben Gardiner (2): [RFC] ASoC: davinci-mcasp: enable ping-pong SRAM buffers [RFC] da850evm: enable mcasp ping-pong buffers arch/arm/configs/da8xx_omapl_defconfig | 1 + arch/arm/mach-davinci/board-da850-evm.c | 4 +++- sound/soc/davinci/davinci-mcasp.c | 2 ++ 3 files changed, 6 insertions(+), 1 deletions(-) -- 1.7.4.1 From bengardiner at nanometrics.ca Fri May 13 16:26:46 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 13 May 2011 17:26:46 -0400 Subject: [PATCH 2/2] [RFC] da850evm: enable mcasp ping-pong buffers In-Reply-To: References: Message-ID: <2c7ebe78b4abdc0b9c5bf135b8b85a3770ee5502.1305321198.git.bengardiner@nanometrics.ca> The davinci--mcasp driver will copy the platform data specified sram_size's and eventq's. Set the eventq's and sram size for da850 and disable CONFIG_SUSPEND so that there is SRAM enough left for the McASP (until the patch to allocate sram from the 128K shared RAM region, then there should be plenty to spare) Not-signed-off-by: Ben Gardiner --- arch/arm/configs/da8xx_omapl_defconfig | 1 + arch/arm/mach-davinci/board-da850-evm.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletions(-) diff --git a/arch/arm/configs/da8xx_omapl_defconfig b/arch/arm/configs/da8xx_omapl_defconfig index 88ccde0..d06c89b 100644 --- a/arch/arm/configs/da8xx_omapl_defconfig +++ b/arch/arm/configs/da8xx_omapl_defconfig @@ -34,6 +34,7 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=m CONFIG_CPU_FREQ_GOV_POWERSAVE=m CONFIG_CPU_FREQ_GOV_ONDEMAND=m CONFIG_CPU_IDLE=y +# CONFIG_SUSPEND is not set CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..1566480 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -722,10 +722,12 @@ static struct snd_platform_data da850_evm_snd_data = { .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), .tdm_slots = 2, .serial_dir = da850_iis_serializer_direction, - .asp_chan_q = EVENTQ_1, + .asp_chan_q = EVENTQ_0, + .ram_chan_q = EVENTQ_1, .version = MCASP_VERSION_2, .txnumevt = 1, .rxnumevt = 1, + .sram_size_playback = SZ_8K /* or SZ_4K -- same results */, }; static const short da850_evm_mcasp_pins[] __initconst = { -- 1.7.4.1 From broonie at opensource.wolfsonmicro.com Sat May 14 11:01:27 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Sat, 14 May 2011 18:01:27 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105102344.58598.arnd@arndb.de> <82D549BB195345A1A189D569952B387C@subhasishg> <201105112203.54838.arnd@arndb.de> Message-ID: <20110514160126.GA2791@opensource.wolfsonmicro.com> On Fri, May 13, 2011 at 04:25:56PM +0530, Subhasish Ghosh wrote: > >Instead, maybe you can simply call platform_device_register > >at that stage to create the children and not use MFD at all. > >Samuel, can you comment on this as well? Do you still see pruss > >as an MFD driver when the uses are completely dynamic and determined > >by the firmware loaded into it? > But in that case, where do I fit my driver. > It's a microcontroller inside of a processor, guess that's unique for Linux. It's not that unusual in hardware terms, really. There's an awful lot of this going on in the audio area especially at the minute so there's going to be some effort around providing some framework support for this which should have some general usability but that's not there yet. > Will a misc device be a better choice. > In that case I can remove the MFD cell from the board_file and add an array > of platform_device for the UART & CAN. I think that for me so long as it can simultaneously do two unrelated functions which need to be arbitrated between there's probably a place for it in MFD, certainly at the minute. From lamiaposta71 at gmail.com Sat May 14 15:21:57 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sat, 14 May 2011 22:21:57 +0200 Subject: pm loss development In-Reply-To: <201105131854.57854.rjw@sisk.pl> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105122127.44484.rjw@sisk.pl> <201105131854.57854.rjw@sisk.pl> Message-ID: > I read the patches. ?My question was about the general idea of who should > be responsible of making these decisions. The best should be, I think, to have some guidelines and than the possibility to choose the best policy for each situation. In my board I needed to shutdown video in capture and demodulator circuit, so I have implemented vpfe capture switch off, that does stream_off to all its v4l2 subdevices (a pal decoder and a video demodulator). So I can save 30mA, and it allows to my board to survive longer. I need to do some tests and have some data with and without PM loss. Bye, raffaele -- www.opensurf.it From lamiaposta71 at gmail.com Sat May 14 15:30:31 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sat, 14 May 2011 22:30:31 +0200 Subject: [linux-pm] pm loss development In-Reply-To: <20110514162423.GA8292@gvim.org> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <20110514162423.GA8292@gvim.org> Message-ID: On Sat, May 14, 2011 at 6:24 PM, mark gross wrote: > On Thu, May 12, 2011 at 07:11:01PM +0200, Raffaele Recalcati wrote: >> What happen normally in runtime pm implementation is that every devices >> are switched off and are enabled only when needed. >> In our case instead we have a completely functional embedded system and, >> when an asyncrhonous event appear, we have only some tens milliseconds >> before the actual power failure takes place. > > Very interesting! ?I've been worried about a similar failure on battery > driven devices that can experience significant voltage droops when > battery gets old, or low, and we turn on the flashlight led, vibrator > and make the screen bright while a high volume ring tone gets played. > > I think 10ms is a bit unrealistic. ?I think its more like 300uSec before > you hit brown out. In our circuit it is the real timing, but I can understand your situation. Are you sure about 300usec? >> This patchset add a support in order to switch off not vital part of the system, >> in order to allow the board to survive longer. >> This allow the possibility to save important data. >> >> The implementation has been written by Davide Ciminaghi. >> My work instead was about analyzing different previuos implementation, >> a first completely custom one, a second using runtime pm, arriving >> finally to that one. >> >> I have tested PM loss in our DaVinci dm365 basi board and I write here below a >> piece of code showing a possible usage. >> >> ------------------- >> >> static int powerfail_status; >> >> static irqreturn_t basi_powerfail_stop(int irq, void *dev_id); >> >> static irqreturn_t basi_powerfail_quick_check_start(int irq, void *dev_id) >> { >> ? ? ? ? basi_mask_irq_gpio0(IRQ_DM365_GPIO0_2); >> ? ? ? ? basi_unmask_irq_gpio0(IRQ_DM365_GPIO0_0); >> >> ? ? ? ? /* PowerFail situation - START: power is going away */ >> ? ? ? ? return IRQ_WAKE_THREAD; >> } >> >> static irqreturn_t basi_powerfail_start(int irq, void *dev_id) >> { >> ? ? ? ? if (powerfail_status) >> ? ? ? ? ? ? ? ? return IRQ_HANDLED; >> ? ? ? ? powerfail_status = 1; >> ? ? ? ? pm_loss_power_changed(SYS_PWR_FAILING); >> ? ? ? ? return IRQ_HANDLED; >> } >> >> >> static irqreturn_t basi_powerfail_quick_check_stop(int irq, void *dev_id) >> { >> ? ? ? ? basi_mask_irq_gpio0(IRQ_DM365_GPIO0_0); >> ? ? ? ? basi_unmask_irq_gpio0(IRQ_DM365_GPIO0_2); >> >> ? ? ? ? /* PowerFail situation - STOP: power is coming back */ >> ? ? ? ? return IRQ_WAKE_THREAD; >> } >> >> static irqreturn_t basi_powerfail_stop(int irq, void *dev_id) >> { >> ? ? ? ? if (!powerfail_status) >> ? ? ? ? ? ? ? ? return IRQ_HANDLED; >> ? ? ? ? powerfail_status = 0; >> ? ? ? ? pm_loss_power_changed(SYS_PWR_GOOD); >> ? ? ? ? return IRQ_HANDLED; >> } >> >> enum basi_pwrfail_prio { >> ? ? ? ? BASI_PWR_FAIL_PRIO_0, >> ? ? ? ? BASI_PWR_FAIL_MIN_PRIO = BASI_PWR_FAIL_PRIO_0, >> ? ? ? ? BASI_PWR_FAIL_PRIO_1, >> ? ? ? ? BASI_PWR_FAIL_PRIO_2, >> ? ? ? ? BASI_PWR_FAIL_PRIO_3, >> ? ? ? ? BASI_PWR_FAIL_MAX_PRIO = BASI_PWR_FAIL_PRIO_3, >> }; >> >> struct pm_loss_default_policy_item basi_pm_loss_policy_items[] = { >> ? ? ? ? { >> ? ? ? ? ? ? ? ? .bus_name = "mmc", >> ? ? ? ? ? ? ? ? .bus_priority = BASI_PWR_FAIL_PRIO_1, >> ? ? ? ? }, >> ? ? ? ? { >> ? ? ? ? ? ? ? ? .bus_name = "platform", >> ? ? ? ? ? ? ? ? .bus_priority = BASI_PWR_FAIL_PRIO_2, >> ? ? ? ? }, >> }; >> >> #define BASI_POLICY_NAME "basi-default" >> >> struct pm_loss_default_policy_table basi_pm_loss_policy_table = { >> ? ? ? ? .name = BASI_POLICY_NAME, >> ? ? ? ? .items = basi_pm_loss_policy_items, >> ? ? ? ? .nitems = ARRAY_SIZE(basi_pm_loss_policy_items), >> }; >> >> static void basi_powerfail_configure(void) >> { >> ? ? ? ? int stat; >> ? ? ? ? struct pm_loss_policy *p; >> ? ? ? ? stat = request_threaded_irq(IRQ_DM365_GPIO0_2, > Is this some comparator device that tugs on this gpio when the voltage > drops or goes to 0? ?Is threaded irq fast enough? yes, there is a comparator. I need more data about timing, I'll try to add this info in some days. > Could we consider something that includes a hot path ISR based > notification call back to do stuff like blink off devices that don't > need to save state; ?backlights, vibrators, flashlight LEDs, audio > output drivers <-- I'm not sure about audio HW, and then a slower path > for other things that can be put into lower power states? > > the all-clear notification that power is good again should be on a > slower path I would assume. First I get data, afterwards we can see if your need can be seen as an extension or something else. > > --mark > >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? basi_powerfail_quick_check_start, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? basi_powerfail_start, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "pwrfail-on", NULL); >> ? ? ? ? if (stat < 0) >> ? ? ? ? ? ? ? ? printk(KERN_ERR "request_threaded_irq for IRQ%d (pwrfail-on) " >> ? ? ? ? ? ? ? ? ? ? ? ?"failed\n", IRQ_DM365_GPIO0_2); >> ? ? ? ? stat = request_threaded_irq(IRQ_DM365_GPIO0_0, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? basi_powerfail_quick_check_stop, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? basi_powerfail_stop, 0, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "pwrfail-off", NULL); >> ? ? ? ? if (stat < 0) >> ? ? ? ? ? ? ? ? printk(KERN_ERR "request_threaded_irq for IRQ%d (pwrfail-off) " >> ? ? ? ? ? ? ? ? ? ? ? ?"failed\n", IRQ_DM365_GPIO0_0); >> ? ? ? ? basi_mask_irq_gpio0(IRQ_DM365_GPIO0_0); >> ? ? ? ? p = pm_loss_setup_default_policy(&basi_pm_loss_policy_table); >> >> ? ? ? ? if (!p) >> ? ? ? ? ? ? ? ? printk(KERN_ERR "Could not register pwr change policy\n"); >> >> ? ? ? ? if (pm_loss_set_policy(BASI_POLICY_NAME) < 0) >> ? ? ? ? ? ? ? ? printk(KERN_ERR "Could not set %s power loss policy\n", >> ? ? ? ? ? ? ? ? ? ? ? ?BASI_POLICY_NAME); >> } >> >> int platform_pm_loss_power_changed(struct device *dev, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?enum sys_power_state s) >> { >> ? ? ? ? int ret = 0; >> >> ? ? ? ? /* Calling platform bus pm_loss functions */ >> ? ? ? ? pr_debug_pm_loss("platform_pm_loss_power_changed(%d)\n", s); >> >> ? ? ? ? if (dev->driver && dev->driver->pm && >> ? ? ? ? ? ? ? ? dev->driver->pm->power_changed) >> ? ? ? ? ? ? ? ? ret = dev->driver->pm->power_changed(dev, s); >> ? ? ? ? return ret; >> } >> >> >> >> >> _______________________________________________ >> linux-pm mailing list >> linux-pm at lists.linux-foundation.org >> https://lists.linux-foundation.org/mailman/listinfo/linux-pm > Bye, Raffaele From lamiaposta71 at gmail.com Sat May 14 15:34:09 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sat, 14 May 2011 22:34:09 +0200 Subject: [linux-pm] pm loss development In-Reply-To: <201105142053.34920.oliver@neukum.org> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105122127.44484.rjw@sisk.pl> <201105142053.34920.oliver@neukum.org> Message-ID: On Sat, May 14, 2011 at 8:53 PM, Oliver Neukum wrote: > Am Donnerstag, 12. Mai 2011, 21:27:44 schrieb Rafael J. Wysocki: >> On Thursday, May 12, 2011, Raffaele Recalcati wrote: >> > What happen normally in runtime pm implementation is that every devices >> > are switched off and are enabled only when needed. >> > In our case instead we have a completely functional embedded system and, >> > when an asyncrhonous event appear, we have only some tens milliseconds >> > before the actual power failure takes place. >> > This patchset add a support in order to switch off not vital part of the system, >> > in order to allow the board to survive longer. >> > This allow the possibility to save important data. >> >> OK, so first, who decides what parts of the system are vital and what aren't? > > If you know that power is failing in a few miliseconds, only stuff that can lead to data > corruption is vital. In that timeframe you can't even flush buffers. Remember that if you switch off some peripherals this timeframe becomes longer, so maybe you have enough time to sync some storage devices. Bye, Raffaele From arnd at arndb.de Sat May 14 15:33:53 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Sat, 14 May 2011 22:33:53 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <20110514160126.GA2791@opensource.wolfsonmicro.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <20110514160126.GA2791@opensource.wolfsonmicro.com> Message-ID: <201105142233.53659.arnd@arndb.de> On Saturday 14 May 2011, Mark Brown wrote: > > Will a misc device be a better choice. > > > In that case I can remove the MFD cell from the board_file and add an array > > of platform_device for the UART & CAN. > > I think that for me so long as it can simultaneously do two unrelated > functions which need to be arbitrated between there's probably a place > for it in MFD, certainly at the minute. I guess drivers/mfd would be a better place than drivers/misc, but it might not need to be an mfd driver in the sense that it registers mfd cells. The more important point is to remove the device registration from the board file. You definitely should not have the cells in the board file, but replacing them with platform devices in the board file makes it no better. My whole point has been that you register them from the main pruss driver based on run-time data instead of compile-time pre-configured stuff in the board file. Arnd From broonie at opensource.wolfsonmicro.com Sat May 14 17:14:46 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Sat, 14 May 2011 15:14:46 -0700 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105142233.53659.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <20110514160126.GA2791@opensource.wolfsonmicro.com> <201105142233.53659.arnd@arndb.de> Message-ID: <20110514221445.GB21792@opensource.wolfsonmicro.com> On Sat, May 14, 2011 at 10:33:53PM +0200, Arnd Bergmann wrote: > I guess drivers/mfd would be a better place than drivers/misc, but it might not > need to be an mfd driver in the sense that it registers mfd cells. Yes, it might be more sensible to just open code. OTOH mfd_cell is marginally easier to use. > The more important point is to remove the device registration from the board > file. You definitely should not have the cells in the board file, but replacing > them with platform devices in the board file makes it no better. Agreed entirely, it should be something higher level than that. > My whole point has been that you register them from the main pruss driver > based on run-time data instead of compile-time pre-configured stuff in the > board file. I'm not so sure - if the usage is fixed as a result of the pins on the device being wired a CAN bus then it seems reasonable to tell the system about that so it'll stop the user trying to run SPI or something against it at runtime. From yangsb05 at gmail.com Sat May 14 21:32:08 2011 From: yangsb05 at gmail.com (ShaoBo Yang) Date: Sun, 15 May 2011 02:32:08 +0000 (UTC) Subject: Invitation to connect on LinkedIn Message-ID: <1849537732.9584493.1305426728383.JavaMail.app@ela4-bed78.prod> LinkedIn ------------ I'd like to add you to my professional network on LinkedIn. - ShaoBo ShaoBo Yang Software Engineer at HuaweiSymantec China Confirm that you know ShaoBo Yang https://www.linkedin.com/e/-6res9e-gnpdda65-27/isd/2885962943/NjcX_ztD/ -- (c) 2011, LinkedIn Corporation -------------- next part -------------- An HTML attachment was scrubbed... URL: From arnd at arndb.de Sun May 15 04:33:23 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Sun, 15 May 2011 11:33:23 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <20110514221445.GB21792@opensource.wolfsonmicro.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105142233.53659.arnd@arndb.de> <20110514221445.GB21792@opensource.wolfsonmicro.com> Message-ID: <201105151133.23870.arnd@arndb.de> On Sunday 15 May 2011, Mark Brown wrote: > > My whole point has been that you register them from the main pruss driver > > based on run-time data instead of compile-time pre-configured stuff in the > > board file. > > I'm not so sure - if the usage is fixed as a result of the pins on the > device being wired a CAN bus then it seems reasonable to tell the system > about that so it'll stop the user trying to run SPI or something against > it at runtime. I'm mostly worried about the case where the pins are not hardwired for some specific function -- Subhasish was mentioning that these may be implemented using a pluggable extension board and I want to make sure that you are not required to recompile the kernel when changing the extension board. However, you made a good point that in many cases it will be hardwired so it may be valuable to preconfigure this in a way that does not require scripts to set up variables in sysfs when you already know what is there. Note that my suggestion to put the device name into the firmware file covers this case, because you can then simply ship a firmware blob that matches the hardware configuration. Thinking about the future device tree setup, you can even put the firmware blob itself into a property in the device tree file. Arnd From subhasish at mistralsolutions.com Mon May 16 00:17:18 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 16 May 2011 10:47:18 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver Message-ID: <78EB813416554F1A973EF20AA714ADF8@subhasishg> Hi Sekhar, >>> Say, if the driver is loaded as a module. >>> If I allocate the sram in the platform code, how to I >>> free it when the driver is unloaded. >> >> This is what I said in my last e-mail. What is >> the issue you see with this approach? >> >> | Thanks for the clarification. In this case, the driver >> | should use platform callbacks to get/put fast fifo >> | space. In case this callback is not populated by the >> | platform or returns an error, the driver should fall >> | back to allocating from DDR. >> > > ok, so what you are suggesting is that I implement some > callbacks (like .fifo_alloc, .fifo_dealloc) which can allocate > memory using sram_alloc. > My doubt is, if already such API's are there (by Russel) or you > are suggesting to implement them. I can add the fifo_alloc/dealloc as part of the suart platform_data, Is that's what you are suggesting. If you may please point me out to any example. I am just not clear with this. From subhasish at mistralsolutions.com Mon May 16 01:06:28 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 16 May 2011 11:36:28 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105151133.23870.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105142233.53659.arnd@arndb.de> <20110514221445.GB21792@opensource.wolfsonmicro.com> <201105151133.23870.arnd@arndb.de> Message-ID: Hi!, >> > My whole point has been that you register them from the main pruss >> > driver >> > based on run-time data instead of compile-time pre-configured stuff in >> > the >> > board file. >> >> I'm not so sure - if the usage is fixed as a result of the pins on the >> device being wired a CAN bus then it seems reasonable to tell the system >> about that so it'll stop the user trying to run SPI or something against >> it at runtime. > > I'm mostly worried about the case where the pins are not hardwired for > some specific function -- Subhasish was mentioning that these may be > implemented using a pluggable extension board and I want to make sure > that you are not required to recompile the kernel when changing the > extension board. > > However, you made a good point that in many cases it will be hardwired > so it may be valuable to preconfigure this in a way that does not require > scripts to set up variables in sysfs when you already know what is there. > > Note that my suggestion to put the device name into the firmware file > covers this case, because you can then simply ship a firmware blob that > matches the hardware configuration. Thinking about the future device > tree setup, you can even put the firmware blob itself into a property > in the device tree file. > I earlier had an implementation where I used a pruss_devices structure in the board file. http://linux.omap.com/pipermail/davinci-linux-open-source/ 2011-March/022339.html. We can use this implementation along with the sysfs to load the devices runtime. The configs that I have in the board_file for the devices structure, are fixed for a board. To swap the boards, we do not need to re-compile the kernel. From bengardiner at nanometrics.ca Mon May 16 14:08:21 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 16 May 2011 15:08:21 -0400 Subject: [PATCH 1/1] davinci: changed SRAM allocator to shared ram. In-Reply-To: References: <1297434088-27995-1-git-send-email-subhasish@mistralsolutions.com> <886E26C48DE446C3937D89ECF86030B2@subhasishg> <25DBF349769A4CF7B883D6D7FF01AD70@subhasishg> Message-ID: Hi Subhasish and Sekhar, Subhashish, I was testing your patch here while investigating davinci-pcm ping-pong buffers on da850. On Fri, Feb 11, 2011 at 9:21 AM, Subhasish Ghosh wrote: > This patch modifies the sram allocator to allocate memory > from the DA8XX shared RAM. > > Signed-off-by: Subhasish Ghosh > --- > ?arch/arm/mach-davinci/da850.c ? ? ? ? ? ? ?| ? ?6 +++--- > ?arch/arm/mach-davinci/include/mach/da8xx.h | ? ?1 + Since this changes only the da850 behaviour, a subject prefix of da850 might be more appropriate than 'davinci'. > ?2 files changed, 4 insertions(+), 3 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 3443d97..8a4de97 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -711,7 +711,7 @@ static struct map_desc da850_io_desc[] = { > ? ? ? ?}, > ? ? ? ?{ > ? ? ? ? ? ? ? ?.virtual ? ? ? ?= SRAM_VIRT, > - ? ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(DA8XX_ARM_RAM_BASE), > + ? ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(DA8XX_SHARED_RAM_BASE), > ? ? ? ? ? ? ? ?.length ? ? ? ? = SZ_8K, Assigning only 8K to this iomap will result in a fault for the first victim to access SRAM_VIRT+0x2000. This will happen for example with mcasp ping-pong buffers totalling more than 8K on the da850. Unfortunately SZ_128K cannot be used here since it will cause a panic on boot. SZ_64K works though. > ? ? ? ? ? ? ? ?.type ? ? ? ? ? = MT_DEVICE > ? ? ? ?}, > @@ -1083,8 +1083,8 @@ 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_len ? ? ? ? ? ? ? = SZ_8K, > + ? ? ? .sram_dma ? ? ? ? ? ? ? = DA8XX_SHARED_RAM_BASE, > + ? ? ? .sram_len ? ? ? ? ? ? ? = SZ_128K, This should probably be set to match whatever is reported in the map entry above -- or an ioremap could be issued later but before the sram init? On Wed, Mar 2, 2011 at 12:12 PM, Nori, Sekhar wrote: > [...] >> root at arago:~# rtcwake -d /dev/rtc0 -s 20 -m mem >> wakeup from "mem" at Tue Apr 21 14:31:13 2009 >> PM: Syncing filesystems ... done. >> Freezing user space processes ... (elapsed 0.01 seconds) done. >> Freezing remaining freezable tasks ... (elapsed 0.01 seconds) done. >> Suspending console(s) (use no_console_suspend to debug) >> >> From the Kconfig I had enabled RTC_DRV_OMAP, CONFIG_PM, CONFIG_SUSPEND. > > Nothing stands out from these logs. One thing to > verify is if the RTC Alarm interrupt is really > working by using the test program present in the > Documentation/ folder. > > Anyway, the easiest way for you to move quickly is > to use ramdisk so you can bypass all driver specific > issues. I tested suspend here with the patch applied on top of 2.6.39-rc7 using "rtcwake -d /dev/rtc0 -s 20 -m mem" and it works. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Mon May 16 14:15:51 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 16 May 2011 15:15:51 -0400 Subject: [PATCH RFC 0/2] da850 mcasp playback ping-pong buffers don't work In-Reply-To: References: Message-ID: Hi All, On Fri, May 13, 2011 at 5:26 PM, Ben Gardiner wrote: > [...] > Does anyone have any clues why ping-pong buffers on da850 to the McASP > would work? Or any clues why the patches are broken? To answer my own question: the reason ping-pong buffering was not working is because the SRAM allocator being used was allocating buffers from the "ARM local RAM" block @0xFFFF0000 which is not addressable by the DMA engine -- according to SPRS586. Ping-pong buffers work when I substitute the allocator for one that allocates from the 'Shared RAM' block @0x80000000 using Subhasish Ghosh's patch "davinci: changed SRAM allocator to shared ram." Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From nsekhar at ti.com Tue May 17 08:06:33 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 17 May 2011 18:36:33 +0530 Subject: [RFC PATCH v3] Consolidate SRAM support In-Reply-To: <20110512174546.GB8633@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110512174546.GB8633@n2100.arm.linux.org.uk> Message-ID: Hi Russell, On Thu, May 12, 2011 at 23:15:46, Russell King - ARM Linux wrote: > diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c > index 1bd73a0..c2f9767 100644 > --- a/arch/arm/mach-davinci/pm.c > +++ b/arch/arm/mach-davinci/pm.c [...] > @@ -123,14 +118,14 @@ static int __init davinci_pm_probe(struct platform_device *pdev) > return -ENOENT; > } > > - davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); > - if (!davinci_sram_suspend) { > + davinci_sram_suspend_mem = pv_pool_alloc(davinci_pv_pool, > + davinci_cpu_suspend_sz, NULL); > + if (!davinci_sram_suspend_mem) { > dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); > return -ENOMEM; > } > - > - davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, > - davinci_cpu_suspend_sz); > + davinci_sram_suspend = fncpy(davinci_sram_suspend_mem, > + davinci_cpu_suspend, davinci_cpu_suspend_sz); This gave these build errors: arch/arm/mach-davinci/pm.c: In function 'davinci_pm_probe': arch/arm/mach-davinci/pm.c:127: error: lvalue required in asm statement arch/arm/mach-davinci/pm.c:127: error: invalid lvalue in asm output 0 make[1]: *** [arch/arm/mach-davinci/pm.o] Error 1 Replacing davinci_cpu_suspend with &davinci_cpu_suspend fixed the issue. With that change done, tested suspend-to-RAM on DA850 platform. Also tested audio driver on DM365 platform with IRAM buffers enabled. Can you also fold the following patch in? Without this the kernel panics when suspend-to-RAM is enabled. diff --git a/arch/arm/mach-davinci/sleep.S b/arch/arm/mach-davinci/sleep.S index fb5e72b..2cef533 100644 --- a/arch/arm/mach-davinci/sleep.S +++ b/arch/arm/mach-davinci/sleep.S @@ -37,6 +37,7 @@ #define DEEPSLEEP_SLEEPENABLE_BIT BIT(31) .text + .align 3 /* * Move DaVinci into deep sleep state * Thanks, Sekhar From nsekhar at ti.com Tue May 17 13:25:01 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 17 May 2011 23:55:01 +0530 Subject: [PATCH 1/1] davinci: changed SRAM allocator to shared ram. In-Reply-To: References: <1297434088-27995-1-git-send-email-subhasish@mistralsolutions.com> <886E26C48DE446C3937D89ECF86030B2@subhasishg> <25DBF349769A4CF7B883D6D7FF01AD70@subhasishg> Message-ID: On Tue, May 17, 2011 at 00:38:21, Ben Gardiner wrote: > Hi Subhasish and Sekhar, > > Subhashish, I was testing your patch here while investigating > davinci-pcm ping-pong buffers on da850. > > On Fri, Feb 11, 2011 at 9:21 AM, Subhasish Ghosh > wrote: > > This patch modifies the sram allocator to allocate memory > > from the DA8XX shared RAM. > > > > Signed-off-by: Subhasish Ghosh > > --- > > ?arch/arm/mach-davinci/da850.c ? ? ? ? ? ? ?| ? ?6 +++--- > > ?arch/arm/mach-davinci/include/mach/da8xx.h | ? ?1 + > > Since this changes only the da850 behaviour, a subject prefix of da850 > might be more appropriate than 'davinci'. Please use "davinci: da850: ..." > > > ?2 files changed, 4 insertions(+), 3 deletions(-) > > > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > > index 3443d97..8a4de97 100644 > > --- a/arch/arm/mach-davinci/da850.c > > +++ b/arch/arm/mach-davinci/da850.c > > @@ -711,7 +711,7 @@ static struct map_desc da850_io_desc[] = { > > ? ? ? ?}, > > ? ? ? ?{ > > ? ? ? ? ? ? ? ?.virtual ? ? ? ?= SRAM_VIRT, > > - ? ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(DA8XX_ARM_RAM_BASE), > > + ? ? ? ? ? ? ? .pfn ? ? ? ? ? ?= __phys_to_pfn(DA8XX_SHARED_RAM_BASE), > > ? ? ? ? ? ? ? ?.length ? ? ? ? = SZ_8K, > > Assigning only 8K to this iomap will result in a fault for the first > victim to access SRAM_VIRT+0x2000. This will happen for example with > mcasp ping-pong buffers totalling more than 8K on the da850. > > Unfortunately SZ_128K cannot be used here since it will cause a panic Okay, I am seeing this too. Kernel panics after freeing init memory. No idea on this one, needs to be debugged. Freeing init memory: 136K Kernel panic - not syncing: Attempted to kill init! [] (unwind_backtrace+0x0/0xec) from [] (panic+0x5c/0x184) [] (panic+0x5c/0x184) from [] (do_exit+0xb4/0x6c4) [] (do_exit+0xb4/0x6c4) from [] (do_group_exit+0xb8/0xe8) [] (do_group_exit+0xb8/0xe8) from [] (get_signal_to_deliver+0x398/0x3f4) [] (get_signal_to_deliver+0x398/0x3f4) from [] (do_notify_resume+0x60/0x610) [] (do_notify_resume+0x60/0x610) from [] (work_pending+0x24/0x28) > on boot. SZ_64K works though. Right. > > > ? ? ? ? ? ? ? ?.type ? ? ? ? ? = MT_DEVICE > > ? ? ? ?}, > > @@ -1083,8 +1083,8 @@ 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_len ? ? ? ? ? ? ? = SZ_8K, > > + ? ? ? .sram_dma ? ? ? ? ? ? ? = DA8XX_SHARED_RAM_BASE, > > + ? ? ? .sram_len ? ? ? ? ? ? ? = SZ_128K, > > This should probably be set to match whatever is reported in the map You are right, the two sizes should match. > entry above -- or an ioremap could be issued later but before the sram > init? Yes, ioremap would be better. I am not sure why a fixed mapping for SRAM is required. Please base these patches on Russell's SRAM consolidation patch. Thanks, Sekhar From nsekhar at ti.com Tue May 17 13:32:00 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 18 May 2011 00:02:00 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <78EB813416554F1A973EF20AA714ADF8@subhasishg> References: <78EB813416554F1A973EF20AA714ADF8@subhasishg> Message-ID: On Mon, May 16, 2011 at 10:47:18, Subhasish Ghosh wrote: > Hi Sekhar, > > >>> Say, if the driver is loaded as a module. > >>> If I allocate the sram in the platform code, how to I > >>> free it when the driver is unloaded. > >> > >> This is what I said in my last e-mail. What is > >> the issue you see with this approach? > >> > >> | Thanks for the clarification. In this case, the driver > >> | should use platform callbacks to get/put fast fifo > >> | space. In case this callback is not populated by the > >> | platform or returns an error, the driver should fall > >> | back to allocating from DDR. > >> > > > > ok, so what you are suggesting is that I implement some > > callbacks (like .fifo_alloc, .fifo_dealloc) which can allocate > > memory using sram_alloc. > > My doubt is, if already such API's are there (by Russel) or you > > are suggesting to implement them. > > I can add the fifo_alloc/dealloc as part of the suart platform_data, > Is that's what you are suggesting. If you may please point me out > to any example. I am just not clear with this. Yes, that's what I am suggesting. I don't have an exact example for you, but for an example of how function pointers can be passed along in platform data you can look at the platform data for DA8x FB driver in include/video/da8xx-fb.h. Hope that helps. Thanks, Sekhar From bengardiner at nanometrics.ca Tue May 17 16:41:56 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:41:56 -0400 Subject: [PATCH 0/9] In-Reply-To: References: Message-ID: The davinci platforms are mapping their io regions using iotables. This patch series converts them to mapping using ioremap. This series is based on-top-of '[RFC PATCH v3] Consolidate SRAM support' from Russell King. The first patch in the series is a squash of the neccessary changes as reported by Sekhar Nori in that thread. The davinci sram init is first changed to ioremap the regions specified by each of the soc_infos; then the iotables are each removed; then the SRAM_VIRT definition is removed. Finally, the da850's sram region is changed from the ARM local RAM region to the Shared RAM region. This change is needed to support mcasp ping-pong buffers on da850. Suspend was tested with rtcwake and was found to work. Ben Gardiner (7): davinci: sram: ioremap the davinci_soc_info specified sram regions davinci: da850: remove the SRAM_VIRT iotable entry davinci: dm355: remove the SRAM_VIRT iotable entry davinci: dm365: remove the SRAM_VIRT iotable entry davinci: dm644x: remove the SRAM_VIRT iotable entry davinci: dm646x: remove the SRAM_VIRT iotable entry davinci: remove definition of SRAM_VIRT Nori, Sekhar (1): davinci: pm: fix compiler errors and kernel panics from sram consolidation Subhasish Ghosh (1): davinci: da850: changed SRAM allocator to shared ram. arch/arm/mach-davinci/da850.c | 10 ++-------- arch/arm/mach-davinci/dm355.c | 6 ------ arch/arm/mach-davinci/dm365.c | 6 ------ arch/arm/mach-davinci/dm644x.c | 6 ------ arch/arm/mach-davinci/dm646x.c | 6 ------ arch/arm/mach-davinci/include/mach/common.h | 2 -- arch/arm/mach-davinci/include/mach/da8xx.h | 1 + arch/arm/mach-davinci/pm.c | 2 +- arch/arm/mach-davinci/sleep.S | 1 + arch/arm/mach-davinci/sram.c | 12 ++++++++++-- 10 files changed, 15 insertions(+), 37 deletions(-) -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:41:57 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:41:57 -0400 Subject: [PATCH 1/9] davinci: pm: fix compiler errors and kernel panics from sram consolidation In-Reply-To: References: Message-ID: <467c7cc853555213b2560ebe1f188c09440284c1.1305668470.git.bengardiner@nanometrics.ca> From: Nori, Sekhar Hi Russell, On Thu, May 12, 2011 at 23:15:46, Russell King - ARM Linux wrote: > diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c > index 1bd73a0..c2f9767 100644 > --- a/arch/arm/mach-davinci/pm.c > +++ b/arch/arm/mach-davinci/pm.c [...] > @@ -123,14 +118,14 @@ static int __init davinci_pm_probe(struct platform_device *pdev) > return -ENOENT; > } > > - davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); > - if (!davinci_sram_suspend) { > + davinci_sram_suspend_mem = pv_pool_alloc(davinci_pv_pool, > + davinci_cpu_suspend_sz, NULL); > + if (!davinci_sram_suspend_mem) { > dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); > return -ENOMEM; > } > - > - davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, > - davinci_cpu_suspend_sz); > + davinci_sram_suspend = fncpy(davinci_sram_suspend_mem, > + davinci_cpu_suspend, davinci_cpu_suspend_sz); This gave these build errors: arch/arm/mach-davinci/pm.c: In function 'davinci_pm_probe': arch/arm/mach-davinci/pm.c:127: error: lvalue required in asm statement arch/arm/mach-davinci/pm.c:127: error: invalid lvalue in asm output 0 make[1]: *** [arch/arm/mach-davinci/pm.o] Error 1 Replacing davinci_cpu_suspend with &davinci_cpu_suspend fixed the issue. With that change done, tested suspend-to-RAM on DA850 platform. Also tested audio driver on DM365 platform with IRAM buffers enabled. Can you also fold the following patch in? Without this the kernel panics when suspend-to-RAM is enabled. Thanks, Sekhar --- arch/arm/mach-davinci/pm.c | 2 +- arch/arm/mach-davinci/sleep.S | 1 + 2 files changed, 2 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c index c2f9767..5255223 100644 --- a/arch/arm/mach-davinci/pm.c +++ b/arch/arm/mach-davinci/pm.c @@ -125,7 +125,7 @@ static int __init davinci_pm_probe(struct platform_device *pdev) return -ENOMEM; } davinci_sram_suspend = fncpy(davinci_sram_suspend_mem, - davinci_cpu_suspend, davinci_cpu_suspend_sz); + &davinci_cpu_suspend, davinci_cpu_suspend_sz); suspend_set_ops(&davinci_pm_ops); diff --git a/arch/arm/mach-davinci/sleep.S b/arch/arm/mach-davinci/sleep.S index fb5e72b..2cef533 100644 --- a/arch/arm/mach-davinci/sleep.S +++ b/arch/arm/mach-davinci/sleep.S @@ -37,6 +37,7 @@ #define DEEPSLEEP_SLEEPENABLE_BIT BIT(31) .text + .align 3 /* * Move DaVinci into deep sleep state * -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:41:58 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:41:58 -0400 Subject: [PATCH 2/9] davinci: sram: ioremap the davinci_soc_info specified sram regions In-Reply-To: References: Message-ID: <80c0c2ff5a39baf6140c08460c2691b178c038c0.1305668470.git.bengardiner@nanometrics.ca> The current davinci init sets up SRAM in iotables. There has been an observed failure to boot a da850 with 128K specified in the iotable. Make the davinci sram allocator -- now based on RMK's consolidated SRAM support -- do an ioremap of the region specified by the entries in davinci_soc_info before registering with pv_pool_create(). This commit breaks runtime of davinci boards since the regions that the sram init is now trying to ioremap have been iomapped by their iotable entries. The iotable entries will be removed in the patches to come. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/sram.c | 12 ++++++++++-- 1 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c index 219d4c5..96026df 100644 --- a/arch/arm/mach-davinci/sram.c +++ b/arch/arm/mach-davinci/sram.c @@ -8,6 +8,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +#include #include #include #include @@ -26,16 +27,23 @@ EXPORT_SYMBOL_GPL(davinci_pv_pool); */ static int __init sram_init(void) { + void *addr; unsigned len = davinci_soc_info.sram_len; int status = 0; if (len) { len = min_t(unsigned, len, SRAM_SIZE); - davinci_pv_pool = pv_pool_create((void *)SRAM_VIRT, + addr = ioremap(davinci_soc_info.sram_phys, len); + if (!addr) + return -EIO; + + davinci_pv_pool = pv_pool_create(addr, davinci_soc_info.sram_phys, len, ilog2(SRAM_GRANULARITY)); - if (!davinci_pv_pool) + if (!davinci_pv_pool) { + iounmap(addr); status = -ENOMEM; + } } return status; } -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:41:59 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:41:59 -0400 Subject: [PATCH 3/9] davinci: da850: remove the SRAM_VIRT iotable entry In-Reply-To: References: Message-ID: <67f7f28015746c1fe0ecef925e8f14d44df1b084.1305668470.git.bengardiner@nanometrics.ca> The sram region defined for da850 in the iotable entry removed is also defined in its davinci_soc_info, davinci_soc_info_da850 in da850.c. Remove this duplicate information which is now uneccessary since sram init will ioremap the region defined by davinci_soc_info_da850. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/da850.c | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 09f6c12..5754338 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -725,12 +725,6 @@ static struct map_desc da850_io_desc[] = { .length = DA8XX_CP_INTC_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(DA8XX_ARM_RAM_BASE), - .length = SZ_8K, - .type = MT_DEVICE - }, }; static u32 da850_psc_bases[] = { DA8XX_PSC0_BASE, DA8XX_PSC1_BASE }; -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:42:00 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:42:00 -0400 Subject: [PATCH 4/9] davinci: dm355: remove the SRAM_VIRT iotable entry In-Reply-To: References: Message-ID: <5bdebaf5036b59a247604b72f83e6cb1c35db4c2.1305668470.git.bengardiner@nanometrics.ca> The sram region defined for dm355 in the iotable entry removed is also defined in its davinci_soc_info, davinci_soc_info_dm355 in dm355.c. Remove this duplicate information which is now uneccessary since sram init will ioremap the region defined by davinci_soc_info_dm355. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/dm355.c | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 9bda687..94f44d3 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -757,12 +757,6 @@ static struct map_desc dm355_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:42:02 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:42:02 -0400 Subject: [PATCH 6/9] davinci: dm644x: remove the SRAM_VIRT iotable entry In-Reply-To: References: Message-ID: <02ee65a4734240b16f6e9189c9aad4d0de39c8a9.1305668470.git.bengardiner@nanometrics.ca> The sram region defined for dm644x in the iotable entry removed is also defined in its davinci_soc_info, davinci_soc_info_dm644x in da644x.c. Remove this duplicate information which is now uneccessary since sram init will ioremap the region defined by davinci_soc_info_dm644x. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/dm644x.c | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 3949ed7..11e6481 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -663,12 +663,6 @@ static struct map_desc dm644x_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00008000), - .length = SZ_16K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:42:01 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:42:01 -0400 Subject: [PATCH 5/9] davinci: dm365: remove the SRAM_VIRT iotable entry In-Reply-To: References: Message-ID: <9d809a23414abb7619a7ac1bf49fdcac04251467.1305668470.git.bengardiner@nanometrics.ca> The sram region defined for dm365 in the iotable entry removed is also defined in its davinci_soc_info, davinci_soc_info_dm365 in dm365.c. Remove this duplicate information which is now uneccessary since sram init will ioremap the region defined by davinci_soc_info_dm365. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/dm365.c | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index d306034..58f5b0a 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -970,12 +970,6 @@ static struct map_desc dm365_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; static struct resource dm365_ks_resources[] = { -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:42:03 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:42:03 -0400 Subject: [PATCH 7/9] davinci: dm646x: remove the SRAM_VIRT iotable entry In-Reply-To: References: Message-ID: <54dccebe40a53aa38ad3e4117a70787ae8194b51.1305668470.git.bengardiner@nanometrics.ca> The sram region defined for dm646x in the iotable entry removed is also defined in its davinci_soc_info, davinci_soc_info_dm646x in dm646x.c. Remove this duplicate information which is now uneccessary since sram init will ioremap the region defined by davinci_soc_info_dm646x. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/dm646x.c | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index a4365f7..57d697e 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -747,12 +747,6 @@ static struct map_desc dm646x_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:42:04 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:42:04 -0400 Subject: [PATCH 8/9] davinci: remove definition of SRAM_VIRT In-Reply-To: References: Message-ID: <8739469073d138fef7df16207644e94953e3f7cf.1305668470.git.bengardiner@nanometrics.ca> Previously all platforms were iomapping their SRAM using iotable entries with SRAM_VIRT as the target virtual address. Remove the SRAM_VIRT definition now that all the iotables are removed and sram init is doing ioremaps before regsitering with pv_pool_create. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/include/mach/common.h | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index 665d049..16b5ec5 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -86,8 +86,6 @@ extern struct davinci_soc_info davinci_soc_info; extern void davinci_common_init(struct davinci_soc_info *soc_info); extern void davinci_init_ide(void); -/* standard place to map on-chip SRAMs; they *may* support DMA */ -#define SRAM_VIRT 0xfffe0000 #define SRAM_SIZE SZ_128K #endif /* __ARCH_ARM_MACH_DAVINCI_COMMON_H */ -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 17 16:42:05 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 17 May 2011 17:42:05 -0400 Subject: [PATCH 9/9] davinci: da850: changed SRAM allocator to shared ram. In-Reply-To: References: Message-ID: From: Subhasish Ghosh This patch modifies the sram allocator to allocate memory from the DA8XX shared RAM. Signed-off-by: Subhasish Ghosh [rebased onto consolidated SRAM patches] Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/da850.c | 4 ++-- arch/arm/mach-davinci/include/mach/da8xx.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 5754338..cdb7e77 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1093,8 +1093,8 @@ 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_phys = DA8XX_ARM_RAM_BASE, - .sram_len = SZ_8K, + .sram_phys = DA8XX_SHARED_RAM_BASE, + .sram_len = SZ_128K, .reset_device = &da8xx_wdt_device, }; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index e4fc1af..09b8ddb 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -72,6 +72,7 @@ extern unsigned int da850_max_speed; #define DA8XX_AEMIF_CTL_BASE 0x68000000 #define DA8XX_DDR2_CTL_BASE 0xb0000000 #define DA8XX_ARM_RAM_BASE 0xffff0000 +#define DA8XX_SHARED_RAM_BASE 0x80000000 void __init da830_init(void); void __init da850_init(void); -- 1.7.4.1 From rjw at sisk.pl Tue May 17 18:04:56 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Wed, 18 May 2011 01:04:56 +0200 Subject: [linux-pm] [PATCH 1/4] export bus_kset In-Reply-To: <20110515141307.GC15994@mail.gnudd.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <20110513171148.GM29259@mail.gnudd.com> <20110515141307.GC15994@mail.gnudd.com> Message-ID: <201105180104.56252.rjw@sisk.pl> On Sunday, May 15, 2011, Davide Ciminaghi wrote: > On Fri, May 13, 2011 at 07:11:48PM +0200, Davide Ciminaghi wrote: > > On Thu, May 12, 2011 at 09:28:53PM +0200, Rafael J. Wysocki wrote: > > > > hi, > > > > sorry, I missed this message this morning. > > > > > On Thursday, May 12, 2011, Raffaele Recalcati wrote: > > > > From: Davide Ciminaghi > > > > > > Please explain why you need to export it, what the alternatives are and > > > why you think this approach is better than the alternatives. > > > > > > > what I needed to do was walking through the list of registered busses, > > and invoking the bus_added()/bus_removed() callback of a newly registered > > policy. I couldn't find any other simple way to do it. > > > well, I there is another way to do that: adding a function like this > (include/linux/device.h) : > > /** > * run a callback for each registered bus type > * > * @data : arg passed to callback > * @fn : pointer to callback > */ > int for_each_bus(void *data, int (*fn)(struct bus_type *bus, void *data)); > > which would be similar to the already existing bus_for_each_dev() and > would allow to avoid exporting a global variable. I really think you'd simply need to browse all devices, like the core PM code in drivers/base/power/main.c. You can use dpm_list for that just fine. Thanks, Rafael From rjw at sisk.pl Tue May 17 18:06:56 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Wed, 18 May 2011 01:06:56 +0200 Subject: [linux-pm] pm loss development In-Reply-To: References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105142053.34920.oliver@neukum.org> Message-ID: <201105180106.57147.rjw@sisk.pl> On Saturday, May 14, 2011, Raffaele Recalcati wrote: > On Sat, May 14, 2011 at 8:53 PM, Oliver Neukum wrote: > > Am Donnerstag, 12. Mai 2011, 21:27:44 schrieb Rafael J. Wysocki: > >> On Thursday, May 12, 2011, Raffaele Recalcati wrote: > >> > What happen normally in runtime pm implementation is that every devices > >> > are switched off and are enabled only when needed. > >> > In our case instead we have a completely functional embedded system and, > >> > when an asyncrhonous event appear, we have only some tens milliseconds > >> > before the actual power failure takes place. > >> > This patchset add a support in order to switch off not vital part of the system, > >> > in order to allow the board to survive longer. > >> > This allow the possibility to save important data. > >> > >> OK, so first, who decides what parts of the system are vital and what aren't? > > > > If you know that power is failing in a few miliseconds, only stuff that can lead to data > > corruption is vital. In that timeframe you can't even flush buffers. > > Remember that if you switch off some peripherals this timeframe > becomes longer, so maybe you have enough time to sync some storage > devices. However, switching off peripherals _also_ takes time. So, it may be more useful to take care of the stuff leading to data corruption along with the switching off peripherals. Thanks, Rafael From rjw at sisk.pl Tue May 17 18:07:57 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Wed, 18 May 2011 01:07:57 +0200 Subject: [linux-pm] pm loss development In-Reply-To: <20110514163513.GB8292@gvim.org> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105131854.57854.rjw@sisk.pl> <20110514163513.GB8292@gvim.org> Message-ID: <201105180107.58078.rjw@sisk.pl> On Saturday, May 14, 2011, mark gross wrote: > On Fri, May 13, 2011 at 06:54:57PM +0200, Rafael J. Wysocki wrote: > > On Friday, May 13, 2011, Raffaele Recalcati wrote: > > > Hi Rafael, > > > > > > 2011/5/12 Rafael J. Wysocki : > > > > On Thursday, May 12, 2011, Raffaele Recalcati wrote: > > > >> What happen normally in runtime pm implementation is that every devices > > > >> are switched off and are enabled only when needed. > > > >> In our case instead we have a completely functional embedded system and, > > > >> when an asyncrhonous event appear, we have only some tens milliseconds > > > >> before the actual power failure takes place. > > > >> This patchset add a support in order to switch off not vital part of the system, > > > >> in order to allow the board to survive longer. > > > >> This allow the possibility to save important data. > > > > > > > > OK, so first, who decides what parts of the system are vital and what aren't? > > > > > > Take a quick look at Documentation/power/loss.txt paragrpah "2.4 > > > Power loss policies". > > > You can decide what can be powered off. > > > > I read the patches. My question was about the general idea of who should > > be responsible of making these decisions. > > I would expect the system integrator would based on the application the > device is getting deployed into. > > A generic opportunistic policy for peripherals that are stateless and can > be trivially power gated off/on from an ISR could be a default but, for > peripherals that need to do some processing (like waiting on an eMMC DMA > to complete) can take time to power down into a safe state. What do you mean by safe state? Rafael From rjw at sisk.pl Tue May 17 18:10:52 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Wed, 18 May 2011 01:10:52 +0200 Subject: pm loss development In-Reply-To: References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105131854.57854.rjw@sisk.pl> Message-ID: <201105180110.52631.rjw@sisk.pl> On Saturday, May 14, 2011, Raffaele Recalcati wrote: > > I read the patches. My question was about the general idea of who should > > be responsible of making these decisions. > > The best should be, I think, to have some guidelines and than the > possibility to choose the best policy for each situation. Again, I'd like to know who's supposed to make the choice. > In my board I needed to shutdown video in capture and demodulator > circuit, so I have implemented vpfe capture switch off, that does > stream_off to all its v4l2 subdevices (a pal decoder and a video > demodulator). > So I can save 30mA, and it allows to my board to survive longer. > I need to do some tests and have some data with and without PM loss. That's fine, but in general we need to take care of a few more things, like the interactions between the devices we're switching off and user space (that can be doing just about anything at the moment). We can't simply switch off devices at will, because that may lead to breakage too in general. Thanks, Rafael From rjw at sisk.pl Tue May 17 18:32:30 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Wed, 18 May 2011 01:32:30 +0200 Subject: [PATCH 2/4] PM / Loss: power loss management In-Reply-To: <20110513130212.GF29259@mail.gnudd.com> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105122157.46895.rjw@sisk.pl> <20110513130212.GF29259@mail.gnudd.com> Message-ID: <201105180132.30802.rjw@sisk.pl> Hi, Sorry for the delay. On Friday, May 13, 2011, Davide Ciminaghi wrote: > On Thu, May 12, 2011 at 09:57:46PM +0200, Rafael J. Wysocki wrote: > > Hi, > > > > On Thursday, May 12, 2011, Raffaele Recalcati wrote: > > > From: Davide Ciminaghi > > > > > hello, I (Davide) will reply as the patch author. > > .................... > > > > I'm not really sure about that. Do you want to hardcode policies in some > > platform/board-specific files inside of the kernel? > > > > I think a little history of the patch can help: it looks like recent mmc > devices can do any sort of weird things in case of sudden power losses during > write operations. This is still to be confirmed by actual tests on our > particular hardware, but we suspect that even a journalling fs could be > corrupted in such a case. That very well may be the case. > Stopping the mmc request queue when a power loss is detected, does not > completely solve the problem, but at least mitigates the risk of disasters. > Plus, we needed to get our board to immediately turn off any unneeded power > sink in case of temporary power losses (just try to survive as long as you > can ...). I understand the motivation I think. :-) > Our hardware triggers an interrupt about 100 millisenconds before the cpu > dies, so we needed to notify the drivers about the event as fast as we could. > That was one of the two main reasons why we chose to put this stuff inside > the kernel. The other reason was that the mmc request queue can be easily > stopped from kernel space. OK, but it's not really sufficient to notify drivers in that case, because they may crash user space if they simply switch off devices. Now, if the power loss actually happens, that probably doesn't matter, but if power is restored afterwards, that won't be nice. The mmc request queue is a special case that shouldn't affect user space directly, but other devices may be accessed in many different ways and not all of them are safe to intercept forcibly. However, even for the mmc case you'd need a mechanism that will force the filesystem on it to flush everything to the storage immediately. > Since forcing a given policy seemed the wrong thing to do, we chose to adopt > a modular policy approach. > > > > +The header file include/linux/pm_loss.h contains all the pm_loss function > > > +prototypes, together with definitions of data structures. > > > +For what concerns power loss policies, the framework already contains a couple > > > +of predefined policies: "nop" and "default" (see later paragraphs for more > > > +details). > > > > I think it would be cleaner to split the patch so that the actual functionality > > is added first and the policy part goes in a separate patch on top of that. > > > > agreed. > > > > + > > > +1.1 Sysfs interface. > > > + > > > +It can be useful (for instance during tests), to be able to quickly switch > > > +from one power loss policy to another, or to simulate power fail and resume > > > +events. To this end, a sysfs interface of the following form has been devised: > > > + > > > +/sys/power/loss + > > > + | > > > + +-- active_policy > > > + | > > > + +-- policies -+ > > > + | | > > > + | +-- nop > > > + | | > > > + | +-- default + > > > + | | | > > > + | | +-- bus_table > > > + | | > > > + | +-- .... > > > + | > > > + +-- pwrfail_sim > > > + > > > +2. Details > > > + > > > +2.1 Sending events to the power loss management core. > > > + > > > +The board specific code shall trigger a power failure event notification by > > > +calling pm_loss_power_changed(SYS_PWR_FAILING). > > > +In case of a temporary power loss, the same function shall be called with > > > +SYS_PWR_GOOD argument on power resume. pm_loss_power_changed() can sleep, so > > > +it shall not be called from interrupt context. > > > + > > > +2.3 Effects on bus and device drivers. > > > + > > > +One more entry has been added to the device PM callbacks: > > > + > > > + int (*power_changed)(struct device *, enum sys_power_state); > > > > Please don't use the second argument. Make it two (or as many as you need) > > callbacks each with one struct device * argument instead (following the > > convention we have in struct dev_pm_ops already). > > > > That said, I'm not quite sure we _need_ those additional callbacks at all. > > Your example mmc implementation appears to use a simple "runtime suspend on > > power fail, runtime resume on power good" approach, for which it's not > > necessary to add any new callbacks. > > > > maybe this is design choice comes from our little knowledge of pm_runtime, > but the reason why we didn't use the existing callbacks was that we wanted > to avoid any possible conflict. > In our understanding, pm_runtime works more or less like this: "when > you're done with a peripheral, turn it off" (well, maybe also wait a little > while to avoid continuously toggling power on and off, thus wasting more > power than just doing nothing). This is because its goal is to save power > during normal operation. > On the other hand, we needed something like this: "when some external event > takes place, just turn off anything you can as fast as you can". This is more similar to system suspend than to runtime PM. > Actually, one of the attempts we made before adding pm_loss was to implement > it via pm_runtime, by just immediately stopping devices on idle and refusing > to turn them on again in case a power loss event took place in the meanwhile. > This worked, and was ok for drivers with no implementation of the pm_runtime > callbacks, but introduced a potential conflict as far as pm_runtime enabled > drivers were concerned. That's why we (maybe wrongfully) thought a new > callback was needed. Having considered that for a while I think that additional callbacks may be necessary, because they generally may need to do more than just suspending devices. > > > +This function can be optionally invoked by power loss policies in case of > > > +system power changes (loss and/or resume). Of course every bus or device driver > > > +can react to such events in some specific way. For instance the mmc block > > > +driver will probably block its request queue during a temporary power loss. > > > > I think you'd have to deal with user space somehow before suspending devices. > > Runtime PM suspends devices when they aren't in use (as indicated by the > > usage counters), but in this power loss case we may really end up suspending > > devices that _are_ in use, right? > > > yes. This is not a problem for us right now, because when a power loss happens, > the "power bad" condition just lasts for about 100ms. At present, pm_loss > callbacks are implemented for the mmc block device and a video capture device > only. While that may not be a problem to you, it is a problem in general and it should be taken into account if we are to provide a generic framework for such things. > In practice what happens on our platform is that processes accessing the mmc > can be put to sleep until either the board dies or a power resume event takes > place. If video capture is running, some "black" frames are generated, but > that is not considered a real fault because power losses should not occur > during normal operation (unless you really turn the board off, of course). > > That said, I think you're right anyway, userspace should be notified somehow. > SIGPWR to init ?. As far as I know, there's no single place in the kernel > where SIGPWR is sent to init: > > find . -name \*.c | xargs grep SIGPWR > ... > ./drivers/char/snsc_event.c: kill_cad_pid(SIGPWR, 0); > ... > ./arch/s390/kernel/nmi.c: kill_cad_pid(SIGPWR, 1); > > Maybe pm_loss could become such a place ? I think so. SIGPWR seems to be a good candidate, but that would change the current (documented) behavior where SIGPWR is a synonym for SIGINFO. Thanks, Rafael From sshtylyov at mvista.com Wed May 18 05:29:31 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 18 May 2011 14:29:31 +0400 Subject: [PATCH 9/9] davinci: da850: changed SRAM allocator to shared ram. In-Reply-To: References: Message-ID: <4DD39F8B.3060209@mvista.com> Hello. On 18-05-2011 1:42, Ben Gardiner wrote: > From: Subhasish Ghosh > This patch modifies the sram allocator to allocate memory > from the DA8XX shared RAM. > Signed-off-by: Subhasish Ghosh > [rebased onto consolidated SRAM patches] > Signed-off-by: Ben Gardiner [...] > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 5754338..cdb7e77 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -1093,8 +1093,8 @@ 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_phys = DA8XX_ARM_RAM_BASE, > - .sram_len = SZ_8K, > + .sram_phys = DA8XX_SHARED_RAM_BASE, > + .sram_len = SZ_128K, > .reset_device =&da8xx_wdt_device, > }; I wonder why da830.c doesn't have SRAM setting? > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index e4fc1af..09b8ddb 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h > @@ -72,6 +72,7 @@ extern unsigned int da850_max_speed; > #define DA8XX_AEMIF_CTL_BASE 0x68000000 > #define DA8XX_DDR2_CTL_BASE 0xb0000000 > #define DA8XX_ARM_RAM_BASE 0xffff0000 > +#define DA8XX_SHARED_RAM_BASE 0x80000000 Please keep this list sorted. WBR, Sergei From sshtylyov at mvista.com Wed May 18 05:40:22 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 18 May 2011 14:40:22 +0400 Subject: [PATCH 8/9] davinci: remove definition of SRAM_VIRT In-Reply-To: <8739469073d138fef7df16207644e94953e3f7cf.1305668470.git.bengardiner@nanometrics.ca> References: <8739469073d138fef7df16207644e94953e3f7cf.1305668470.git.bengardiner@nanometrics.ca> Message-ID: <4DD3A216.9040906@mvista.com> Hello. On 18-05-2011 1:42, Ben Gardiner wrote: > Previously all platforms were iomapping their SRAM using iotable entries with > SRAM_VIRT as the target virtual address. > Remove the SRAM_VIRT definition now that all the iotables are removed and > sram init is doing ioremaps before regsitering with pv_pool_create. > Signed-off-by: Ben Gardiner I think patches 3-8 can safely be merged into one patch. WBR, Sergei From nsekhar at ti.com Wed May 18 07:11:56 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 18 May 2011 17:41:56 +0530 Subject: [PATCH 2/9] davinci: sram: ioremap the davinci_soc_info specified sram regions In-Reply-To: <20110518045140.GE18699@game.jcrosoft.org> References: <80c0c2ff5a39baf6140c08460c2691b178c038c0.1305668470.git.bengardiner@nanometrics.ca> <20110518045140.GE18699@game.jcrosoft.org> Message-ID: Hi Russell, On Wed, May 18, 2011 at 10:21:40, Jean-Christophe PLAGNIOL-VILLARD wrote: > On 17:41 Tue 17 May , Ben Gardiner wrote: > > diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c > > index 219d4c5..96026df 100644 > > --- a/arch/arm/mach-davinci/sram.c > > +++ b/arch/arm/mach-davinci/sram.c > > @@ -8,6 +8,7 @@ > > * the Free Software Foundation; either version 2 of the License, or > > * (at your option) any later version. > > */ > > +#include > > #include > > #include > > #include > > @@ -26,16 +27,23 @@ EXPORT_SYMBOL_GPL(davinci_pv_pool); > > */ > > static int __init sram_init(void) > > { > > + void *addr; > > unsigned len = davinci_soc_info.sram_len; > > int status = 0; > > > > if (len) { > > len = min_t(unsigned, len, SRAM_SIZE); > > - davinci_pv_pool = pv_pool_create((void *)SRAM_VIRT, > > + addr = ioremap(davinci_soc_info.sram_phys, len); > > + if (!addr) > > + return -EIO; > > + > > + davinci_pv_pool = pv_pool_create(addr, > > davinci_soc_info.sram_phys, len, > > ilog2(SRAM_GRANULARITY)); > please use the gennalloc directly as discuss with Andrew and Russell my patch > will present in the .40 With Jean-Christophe's patch getting merged, would you be dropping your PV pool patch? Thanks, Sekhar From linux at arm.linux.org.uk Wed May 18 07:14:20 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Wed, 18 May 2011 13:14:20 +0100 Subject: [PATCH 2/9] davinci: sram: ioremap the davinci_soc_info specified sram regions In-Reply-To: References: <80c0c2ff5a39baf6140c08460c2691b178c038c0.1305668470.git.bengardiner@nanometrics.ca> <20110518045140.GE18699@game.jcrosoft.org> Message-ID: <20110518121420.GI5913@n2100.arm.linux.org.uk> On Wed, May 18, 2011 at 05:41:56PM +0530, Nori, Sekhar wrote: > Hi Russell, > > On Wed, May 18, 2011 at 10:21:40, Jean-Christophe PLAGNIOL-VILLARD wrote: > > > On 17:41 Tue 17 May , Ben Gardiner wrote: > > > > diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c > > > index 219d4c5..96026df 100644 > > > --- a/arch/arm/mach-davinci/sram.c > > > +++ b/arch/arm/mach-davinci/sram.c > > > @@ -8,6 +8,7 @@ > > > * the Free Software Foundation; either version 2 of the License, or > > > * (at your option) any later version. > > > */ > > > +#include > > > #include > > > #include > > > #include > > > @@ -26,16 +27,23 @@ EXPORT_SYMBOL_GPL(davinci_pv_pool); > > > */ > > > static int __init sram_init(void) > > > { > > > + void *addr; > > > unsigned len = davinci_soc_info.sram_len; > > > int status = 0; > > > > > > if (len) { > > > len = min_t(unsigned, len, SRAM_SIZE); > > > - davinci_pv_pool = pv_pool_create((void *)SRAM_VIRT, > > > + addr = ioremap(davinci_soc_info.sram_phys, len); > > > + if (!addr) > > > + return -EIO; > > > + > > > + davinci_pv_pool = pv_pool_create(addr, > > > davinci_soc_info.sram_phys, len, > > > ilog2(SRAM_GRANULARITY)); > > please use the gennalloc directly as discuss with Andrew and Russell my patch > > will present in the .40 > > With Jean-Christophe's patch getting merged, would you be dropping > your PV pool patch? I'll make that decision once JC's patch has actually been merged. JC's patch doesn't do any of the consolidation work, so that needs addressing still. From bengardiner at nanometrics.ca Wed May 18 07:33:57 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 18 May 2011 08:33:57 -0400 Subject: [PATCH 9/9] davinci: da850: changed SRAM allocator to shared ram. In-Reply-To: <4DD39F8B.3060209@mvista.com> References: <4DD39F8B.3060209@mvista.com> Message-ID: Hi Sergei, Thank you for the reviews. On Wed, May 18, 2011 at 6:29 AM, Sergei Shtylyov wrote: [...] >> diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h >> b/arch/arm/mach-davinci/include/mach/da8xx.h >> index e4fc1af..09b8ddb 100644 >> --- a/arch/arm/mach-davinci/include/mach/da8xx.h >> +++ b/arch/arm/mach-davinci/include/mach/da8xx.h >> @@ -72,6 +72,7 @@ extern unsigned int da850_max_speed; >> ?#define DA8XX_AEMIF_CTL_BASE ?0x68000000 >> ?#define DA8XX_DDR2_CTL_BASE ? 0xb0000000 >> ?#define DA8XX_ARM_RAM_BASE ? ?0xffff0000 >> +#define DA8XX_SHARED_RAM_BASE ?0x80000000 > > ? Please keep this list sorted. Will do so in V2. I am currently imagining that the v2 series will be based on Russell's next SRAM consolidation patch based on Jean-Christophe's genpool patch. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From nsekhar at ti.com Wed May 18 08:18:37 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 18 May 2011 18:48:37 +0530 Subject: [PATCH 2/9] davinci: sram: ioremap the davinci_soc_info specified sram regions In-Reply-To: <80c0c2ff5a39baf6140c08460c2691b178c038c0.1305668470.git.bengardiner@nanometrics.ca> References: <80c0c2ff5a39baf6140c08460c2691b178c038c0.1305668470.git.bengardiner@nanometrics.ca> Message-ID: Hi Ben, On Wed, May 18, 2011 at 03:11:58, Ben Gardiner wrote: > diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c > index 219d4c5..96026df 100644 > --- a/arch/arm/mach-davinci/sram.c > +++ b/arch/arm/mach-davinci/sram.c > @@ -8,6 +8,7 @@ > * the Free Software Foundation; either version 2 of the License, or > * (at your option) any later version. > */ > +#include > #include > #include > #include > @@ -26,16 +27,23 @@ EXPORT_SYMBOL_GPL(davinci_pv_pool); > */ > static int __init sram_init(void) > { > + void *addr; > unsigned len = davinci_soc_info.sram_len; > int status = 0; > > if (len) { > len = min_t(unsigned, len, SRAM_SIZE); > - davinci_pv_pool = pv_pool_create((void *)SRAM_VIRT, > + addr = ioremap(davinci_soc_info.sram_phys, len); > + if (!addr) > + return -EIO; -ENOMEM here (for the next time you post). See Sergei's mail on error codes below. Useful stuff. https://patchwork.kernel.org/patch/47365/ Thanks, Sekhar From bengardiner at nanometrics.ca Wed May 18 08:27:45 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 18 May 2011 09:27:45 -0400 Subject: [PATCH 1/2] ASoC: davinci-mcasp: enable ping-pong SRAM buffers In-Reply-To: References: Message-ID: The davinci-i2s driver copies the platform data for playback and capture sram sizes which is in turn used by davinci-pcm to allocate ping-pong buffers. Copy also the platform data in davinci-mcasp probe. Signed-off-by: Ben Gardiner --- sound/soc/davinci/davinci-mcasp.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 4ddc6d3..8566238 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -909,6 +909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; + dma_data->sram_size = pdata->sram_size_playback; dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + mem->start); @@ -925,6 +926,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; + dma_data->sram_size = pdata->sram_size_capture; dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + mem->start); -- 1.7.4.1 From bengardiner at nanometrics.ca Wed May 18 08:27:44 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 18 May 2011 09:27:44 -0400 Subject: [PATCH 0/2] ASoC: davinci: ping-pong buffers for mcasp on da850evm Message-ID: davinci-i2s enables the use of ping-pong buffers by copying the sram sizes specified by platform data into the dma params used by davinci-pcm. This patch series first implements that same behaviour in davinic-mcasp and then specified sram sizes and eventq's for da850evm. To achieve the use of ping-pong buffers at runtime this patch depends also on the conversion of da850's SRAM allocator to the "Share RAM" region from the "ARM Local RAM" region -- originally proposed by Subhasish Ghosh[1] and recently reposted by myself [2]. This is because the "ARM Local RAM" region currently used by the da850 SRAM allocator is not addressable by the EDMA. The resulting behaviour, when ping-pong buffers are used by davinci-pcm, is that playback produces silence and capture acquires silence. However, there is no change in behaviour for da850evm build with the in-tree defconfig since suspend allocates SRAM which prevents a successful allocation of 8K by davinci-pcm and thus the behaviour falls back to the usual scheme. In the case of da850evm builds where CONFIG_SUSPEND is not set, playback will produce silence and capture will acquire silence until such time as the patches to change the da850 SRAM allocator to the "Shared RAM" region are merged. [1] http://article.gmane.org/gmane.linux.kernel/1098928 [2] http://article.gmane.org/gmane.linux.ports.arm.kernel/117261 Ben Gardiner (2): ASoC: davinci-mcasp: enable ping-pong SRAM buffers davinci: da850evm: enable mcasp ping-pong buffers and eventq's arch/arm/mach-davinci/board-da850-evm.c | 5 ++++- sound/soc/davinci/davinci-mcasp.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletions(-) -- 1.7.4.1 From bengardiner at nanometrics.ca Wed May 18 08:27:46 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 18 May 2011 09:27:46 -0400 Subject: [PATCH 2/2] davinci: da850evm: enable mcasp ping-pong bufs and evq's In-Reply-To: References: Message-ID: The davinci-mcasp driver will copy the platform data specified sram sizes and eventq's. Set the event queues and sram sizes for da850. 8K SRAM buffers are selected because it is the minimum that resulted in the same period size when testing 48KHz S16_LE stereo. Event queues 0 and 1 are assigned to match those specified by Troy Kisky in his introduction of ping-pong buffers for dm644x. Signed-off-by: Ben Gardiner --- arch/arm/mach-davinci/board-da850-evm.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..2ee1a6f 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -722,10 +722,13 @@ static struct snd_platform_data da850_evm_snd_data = { .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), .tdm_slots = 2, .serial_dir = da850_iis_serializer_direction, - .asp_chan_q = EVENTQ_1, + .asp_chan_q = EVENTQ_0, + .ram_chan_q = EVENTQ_1, .version = MCASP_VERSION_2, .txnumevt = 1, .rxnumevt = 1, + .sram_size_playback = SZ_8K, + .sram_size_capture = SZ_8K, }; static const short da850_evm_mcasp_pins[] __initconst = { -- 1.7.4.1 From bengardiner at nanometrics.ca Wed May 18 08:36:16 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 18 May 2011 09:36:16 -0400 Subject: [PATCH 2/9] davinci: sram: ioremap the davinci_soc_info specified sram regions In-Reply-To: References: <80c0c2ff5a39baf6140c08460c2691b178c038c0.1305668470.git.bengardiner@nanometrics.ca> Message-ID: Hi Sekhar, Thanks for the review. On Wed, May 18, 2011 at 9:18 AM, Nori, Sekhar wrote: [...] >> ? ? ? if (len) { >> ? ? ? ? ? ? ? len = min_t(unsigned, len, SRAM_SIZE); >> - ? ? ? ? ? ? davinci_pv_pool = pv_pool_create((void *)SRAM_VIRT, >> + ? ? ? ? ? ? addr = ioremap(davinci_soc_info.sram_phys, len); >> + ? ? ? ? ? ? if (!addr) >> + ? ? ? ? ? ? ? ? ? ? return -EIO; > > -ENOMEM here (for the next time you post). See Sergei's > mail on error codes below. Useful stuff. > > https://patchwork.kernel.org/patch/47365/ Will do. Thanks for the useful reference also (and to Sergei for writing it). Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From rjw at sisk.pl Wed May 18 14:43:13 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Wed, 18 May 2011 21:43:13 +0200 Subject: [linux-pm] pm loss development In-Reply-To: <20110518031203.GA3640@gvim.org> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105180107.58078.rjw@sisk.pl> <20110518031203.GA3640@gvim.org> Message-ID: <201105182143.13237.rjw@sisk.pl> On Wednesday, May 18, 2011, mark gross wrote: > On Wed, May 18, 2011 at 01:07:57AM +0200, Rafael J. Wysocki wrote: > > On Saturday, May 14, 2011, mark gross wrote: > > > On Fri, May 13, 2011 at 06:54:57PM +0200, Rafael J. Wysocki wrote: > > > > On Friday, May 13, 2011, Raffaele Recalcati wrote: > > > > > Hi Rafael, > > > > > > > > > > 2011/5/12 Rafael J. Wysocki : > > > > > > On Thursday, May 12, 2011, Raffaele Recalcati wrote: > > > > > >> What happen normally in runtime pm implementation is that every devices > > > > > >> are switched off and are enabled only when needed. > > > > > >> In our case instead we have a completely functional embedded system and, > > > > > >> when an asyncrhonous event appear, we have only some tens milliseconds > > > > > >> before the actual power failure takes place. > > > > > >> This patchset add a support in order to switch off not vital part of the system, > > > > > >> in order to allow the board to survive longer. > > > > > >> This allow the possibility to save important data. > > > > > > > > > > > > OK, so first, who decides what parts of the system are vital and what aren't? > > > > > > > > > > Take a quick look at Documentation/power/loss.txt paragrpah "2.4 > > > > > Power loss policies". > > > > > You can decide what can be powered off. > > > > > > > > I read the patches. My question was about the general idea of who should > > > > be responsible of making these decisions. > > > > > > I would expect the system integrator would based on the application the > > > device is getting deployed into. > > > > > > A generic opportunistic policy for peripherals that are stateless and can > > > be trivially power gated off/on from an ISR could be a default but, for > > > peripherals that need to do some processing (like waiting on an eMMC DMA > > > to complete) can take time to power down into a safe state. > > > > What do you mean by safe state? > > > I need to get more details on this but I assume its a state where the > meta data of the file system is committed to the emmc before lights go > off such that when power is reapplied that the damage isn't too big. I don't think you can guarantee that the metadata won't be damaged without notifying the filesystem of the event (and making it react appropriately). > I've also heard some talk of sim card corruption risks but, I don't have > first hand info on that. Well, I guess that might be prevented by the driver alone. Thanks, Rafael From broonie at opensource.wolfsonmicro.com Wed May 18 17:17:26 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 18 May 2011 23:17:26 +0100 Subject: [linux-pm] pm loss development In-Reply-To: <201105182143.13237.rjw@sisk.pl> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105180107.58078.rjw@sisk.pl> <20110518031203.GA3640@gvim.org> <201105182143.13237.rjw@sisk.pl> Message-ID: <20110518221726.GC20909@sirena.org.uk> On Wed, May 18, 2011 at 09:43:13PM +0200, Rafael J. Wysocki wrote: > On Wednesday, May 18, 2011, mark gross wrote: > > I need to get more details on this but I assume its a state where the > > meta data of the file system is committed to the emmc before lights go > > off such that when power is reapplied that the damage isn't too big. > I don't think you can guarantee that the metadata won't be damaged > without notifying the filesystem of the event (and making it react > appropriately). I guess with a journalling filesystem you'd hope that you can at any point write out what's in flight and have it be safe; if it's a non journalling filesystem of course all bets are off. From manjunath.hadli at ti.com Thu May 19 09:53:33 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Thu, 19 May 2011 20:23:33 +0530 Subject: [PATCH v16 01/13] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <201105021158.23956.laurent.pinchart@ideasonboard.com> Message-ID: Laurent, Thank you for your comments. I have addressed all your suggestions. Please find my comments inline. Also, Would you please review the patch again? The branch is at: http://git.linuxtv.org/mhadli/v4l-dvb-davinci_devices.git?a=shortlog;h=refs/heads/forkhilman2 and the patch that you last reviewed was: http://git.linuxtv.org/mhadli/v4l-dvb-davinci_devices.git?a=commit;h=690187eb05de65f1e63fc631ad4dc31358d01e55 Thanks, -Manju On Mon, May 02, 2011 at 15:28:23, Laurent Pinchart wrote: > Hi Manjunath, > > On Tuesday 26 April 2011 16:47:45 Hadli, Manjunath wrote: > > 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=69 > > f60ed7577ab9184ceabd7efbe5bb3453bf7ef1;hp=a400604f47c339831880c50eda6f > > 6b032 > > 21579e3 > > I've reviewed the same patch, here are my comments. > > > +/* > > + * 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, struct vpbe_display > *disp_obj) > > +{ > > + struct osd_state *osd_device = disp_obj->osd_device; > > + struct timespec timevalue; > > + struct vpbe_layer *layer; > > + unsigned long addr; > > + int fid; > > + int i; > > + > > + ktime_get_ts(&timevalue); > > + > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + layer = disp_obj->dev[i]; > > + /* If streaming is started in this layer */ > > + if (!layer->started) > > + continue; > > What about moving everything above to venc_isr(), and having this function handle a single layer only ? It will lower the max indentation level. I also wonder whether you couldn't share some code between the non-interlaced and the interlaced cases by reorganizing the function body (the fid == 1 code looks quite similar to the non-interlaced code). To make it very clean I have broken the isr in a different way and tried to neatly arrange it. It has now been made qute small and re-usable.Hope you like it. > > [snip] > > > +/** > > + * 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 > > This still sounds a bit cryptic to me. > > vpbe_try_format() should return a format closest to what the user requested: > > - If the pixel format is invalid, select a default value (done) > - If the field is invalid or not specified, select a default value (partly > done, you don't check for default values) > - If width and/or height are invalid (including being set to 0), select > default values (partly done, you compute width/height based on bytesperline > and sizeimage when they're set to 0, and I don't understand why) > - If bytesperline is invalid (smaller than the minimum value according to the > selected width, or larger than the maximum allowable value), fix it > - If sizeimage is invalid (smaller than the minimum value according the the > selected height and bytesperline), fix it > > Is there a need to allow sizeimage values different than height * bytesperline ? Cleaned and taken care of. > > > + */ > > [snip] > > > +static int vpbe_display_querycap(struct file *file, void *priv, > > + struct v4l2_capability *cap) { > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > > + > > + cap->version = VPBE_DISPLAY_VERSION_CODE; > > + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; > > + strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver)); > > + strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); > > + /* check the name of davinci device */ > > + if (vpbe_dev->cfg->module_name != NULL) > > module_name can't be NULL, as it's declared as a char[32]. > > > + strlcpy(cap->card, vpbe_dev->cfg->module_name, > > + sizeof(cap->card)); > > + > > + 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_layer *layer = fh->layer; > > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > > + > > + 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) { > > + /* Fill in the information about format */ > > + fmt->fmt.pix = layer->pix_fmt; > > + } else { > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > + return -EINVAL; > > + } > > You should do it the other way around. Return -EINVAL when the type isn't OUTPUT, and remove the else. This will increase code readability by decreasing the max indentation level in the common case. Done. > > > + > > + return 0; > > +} > > [snip] > > > +/** > > + * 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) > > +{ > > + struct vpbe_fh *fh = priv; > > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > > + int ret; > > + > > + 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; > > + } > > + } else { > > + return -EINVAL; > > + } > > Other way around here too please. Done. > > > + > > + 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_layer *layer = fh->layer; > > + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; > > + int ret; > > + > > + 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; > > + } > > + } else { > > + return -EINVAL; > > + } > > And here too. Done. > > > + > > + return 0; > > +} > > [snip] > > > +static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, > > + struct platform_device *pdev) { > > + struct vpbe_layer *vpbe_display_layer = NULL; > > + struct video_device *vbd = NULL; > > + int k; > > + int err; > > + > > + /* Allocate memory for four plane display objects */ > > + > > + disp_dev->dev[i] = > > + kmalloc(sizeof(struct vpbe_layer), GFP_KERNEL); > > You can use kzalloc() and avoid several initializations to 0 below. Sure. > > > + > > + /* If memory allocation fails, return error */ > > + if (!disp_dev->dev[i]) { > > + printk(KERN_ERR "ran out of memory\n"); > > + err = -ENOMEM; > > + goto free_mem; > > + } > > + spin_lock_init(&disp_dev->dev[i]->irqlock); > > + mutex_init(&disp_dev->dev[i]->opslock); > > + > > + /* Get the pointer to the layer object */ > > + vpbe_display_layer = disp_dev->dev[i]; > > + /* Allocate memory for video device */ > > + vbd = video_device_alloc(); > > There's no need to allocate the device dynamically, you can embed struct video_device into struct vpbe_layer (i.e. replace struct video_device *video_dev with struct video_device video_dev inside struct vpbe_layer) > > (and feel free to rename video_dev to something shorter if needed) Done. Did not do the renaming though. > > > + if (vbd == NULL) { > > + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, > > + "ran out of memory\n"); > > + err = -ENOMEM; > > + goto free_mem; > > + } > > + /* Initialize field of video device */ > > + vbd->release = video_device_release; > > You should then use video_device_release_empty instead of video_device_release. Ok. > > > + vbd->fops = &vpbe_fops; > > + vbd->ioctl_ops = &vpbe_ioctl_ops; > > + vbd->minor = -1; > > + vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; > > + vbd->lock = &vpbe_display_layer->opslock; > > + > > + if (disp_dev->vpbe_dev->current_timings.timings_type & > > + VPBE_ENC_STD) { > > + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); > > + vbd->current_norm = > > + disp_dev->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); > > + > > + /* 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); > > + > > + return 0; > > + > > +free_mem: > > + for (k = 0; k < i-1; k++) { > > + /* Get the pointer to the layer object */ > > + vpbe_display_layer = disp_dev->dev[k]; > > + /* Release video device */ > > + video_device_release(vpbe_display_layer->video_dev); > > + vpbe_display_layer->video_dev = NULL; > > + /* free layer memory */ > > + kfree(disp_dev->dev[k]); > > + } > > This should be moved to the error cleanup part of vpbe_display_probe(). A function that registers a single device shouldn't clean other devices up in case of error. Done. > > > + > > + return -ENODEV; > > +} > > + > > +static __devinit int register_devices(int i, struct vpbe_display *disp_dev, > > + struct platform_device *pdev) { > > This registers a single device, so I would call it vpbe_register_device. > > > + struct vpbe_layer *vpbe_display_layer = NULL; > > + int err; > > + int k; > > + > > + vpbe_display_layer = disp_dev->dev[i]; > > Please pass disp_dev->dev[i] to the function instead of i. Done. > > > + v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, > > + "Trying to register VPBE display device.\n"); > > + v4l2_info(&disp_dev->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, > > + -1); > > + if (err) > > + goto video_register_failed; > > + > > + vpbe_display_layer->disp_dev = disp_dev; > > + /* set the driver data in platform device */ > > + platform_set_drvdata(pdev, disp_dev); > > + video_set_drvdata(vpbe_display_layer->video_dev, > > + vpbe_display_layer); > > + > > + return 0; > > + > > +video_register_failed: > > + for (k = 0; k < i-1; k++) > > + video_unregister_device(vpbe_display_layer->video_dev); > > + > > + for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { > > + /* Get the pointer to the layer object */ > > + vpbe_display_layer = disp_dev->dev[k]; > > + /* Release video device */ > > + video_device_release(vpbe_display_layer->video_dev); > > + /* Unregister video device */ > > + video_unregister_device(vpbe_display_layer->video_dev); > > + vpbe_display_layer->video_dev = NULL; > > + /* free layer memory */ > > + kfree(disp_dev->dev[k]); > > + } > > This should be moved to the error cleanup part of vpbe_display_probe() as well. Done. > > > + return -ENODEV; > > +} > > + > > + > > + > > +/* > > + * 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) { > > + struct vpbe_display *disp_dev; > > + struct resource *res; > > + int i; > > + int err; > > + 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; > > + } > > + > > + spin_lock_init(&disp_dev->dma_queue_lock); > > + /* > > + * 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, disp_dev, > > + vpbe_device_get); > > + if (err < 0) > > + return err; > > + /* Initialize the vpbe display controller */ > > + if (NULL != disp_dev->vpbe_dev->ops.initialize) { > > + err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, > > + disp_dev->vpbe_dev); > > + if (err) { > > + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, > > + "Error initing vpbe\n"); > > + err = -ENOMEM; > > + goto probe_out; > > + } > > + } > > + > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + if (init_vpbe_layer(i, disp_dev, pdev)) { > > + err = -ENODEV; > > + goto probe_out; > > + } > > + } > > + > > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > > + if (!res) { > > + v4l2_err(&disp_dev->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(&disp_dev->vpbe_dev->v4l2_dev, > > + "Unable to request interrupt\n"); > > + err = -ENODEV; > > + goto probe_out; > > + } > > + > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + if (register_devices(i, disp_dev, pdev)) { > > + err = -ENODEV; > > + goto probe_out; > > + } > > + } > > + > > + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 > device\n"); > > + return 0; > > + > > +probe_out: > > You need to unregister the IRQ handler (and move the cleanup code from the two previous functions here). Done. > > > + kfree(disp_dev); > > + return err; > > +} > > [snip] > > > +/* Function for module initialization and cleanup */ > > +module_init(vpbe_display_init); module_exit(vpbe_display_cleanup); > > + > > +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); > > What about "TI DM644x/DM355/DM365" then ? DMXXX makes it look like it supports all DaVinci chips. Done. > > > +MODULE_LICENSE("GPL"); > > +MODULE_AUTHOR("Texas Instruments"); > > -- > Regards, > > Laurent Pinchart > From rjw at sisk.pl Thu May 19 15:52:46 2011 From: rjw at sisk.pl (Rafael J. Wysocki) Date: Thu, 19 May 2011 22:52:46 +0200 Subject: [linux-pm] [PATCH 2/4] PM / Loss: power loss management In-Reply-To: References: Message-ID: <201105192252.46836.rjw@sisk.pl> On Thursday, May 19, 2011, Alan Stern wrote: > On Thu, 19 May 2011, Davide Ciminaghi wrote: > > > I'm not completely sure about this. What we wanted to do was to avoid powering > > down the mmc while it is physically writing data into its internal memory. > > If we force a sync when the power loss warning event warning happens, > > it is very difficult to be able to guarantee that all buffered data will be > > written before power actually dies. So we preferred to follow another strategy: > > let the mmc finish any running write operation, and then stop its request > > queue. If power really goes down, then we hope that the file system journal > > will fix things on next boot (yes, some data could get lost, but the fs should > > still be mountable). On the other hand, if power resumes, nothing bad should > > happen for user space processes. > > You could consider a totally different approach. > > Each platform will have a different set of high-power devices it wants > to turn off when a power-loss warning occurs. So instead of changing > the core PM interface, you could add a new "power_loss" notifier list. > Only the most critical drivers would need to listen for notifications, > and this could be different drivers on different platforms. Moreover, it would allow not only drivers, but also filesystems (for one example) to get notifications. Thanks, Rafael From broonie at opensource.wolfsonmicro.com Thu May 19 16:13:45 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Thu, 19 May 2011 14:13:45 -0700 Subject: [PATCH 1/2] ASoC: davinci-mcasp: enable ping-pong SRAM buffers In-Reply-To: References: Message-ID: <20110519211344.GD18849@opensource.wolfsonmicro.com> On Wed, May 18, 2011 at 09:27:45AM -0400, Ben Gardiner wrote: > The davinci-i2s driver copies the platform data for playback and capture > sram sizes which is in turn used by davinci-pcm to allocate ping-pong > buffers. > > Copy also the platform data in davinci-mcasp probe. > > Signed-off-by: Ben Gardiner Applied, thanks. From broonie at opensource.wolfsonmicro.com Thu May 19 16:14:06 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Thu, 19 May 2011 14:14:06 -0700 Subject: [PATCH 2/2] davinci: da850evm: enable mcasp ping-pong bufs and evq's In-Reply-To: References: Message-ID: <20110519211406.GE18849@opensource.wolfsonmicro.com> On Wed, May 18, 2011 at 09:27:46AM -0400, Ben Gardiner wrote: > The davinci-mcasp driver will copy the platform data specified sram sizes and > eventq's. > > Set the event queues and sram sizes for da850. 8K SRAM buffers are selected > because it is the minimum that resulted in the same period size when testing > 48KHz S16_LE stereo. Event queues 0 and 1 are assigned to match those > specified by Troy Kisky in his introduction of ping-pong buffers for dm644x. > > Signed-off-by: Ben Gardiner Davinci folks, are you OK with this? From subhasish at mistralsolutions.com Fri May 20 00:38:58 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 20 May 2011 11:08:58 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. Message-ID: <8B16A22172EC4753AD4E163CBD64C60F@subhasishg> Hi, Anything regarding this. >>> > My whole point has been that you register them from the main pruss >>> > driver >>> > based on run-time data instead of compile-time pre-configured stuff in >>> > the >>> > board file. >>> >>> I'm not so sure - if the usage is fixed as a result of the pins on the >>> device being wired a CAN bus then it seems reasonable to tell the system >>> about that so it'll stop the user trying to run SPI or something against >>> it at runtime. >> >> I'm mostly worried about the case where the pins are not hardwired for >> some specific function -- Subhasish was mentioning that these may be >> implemented using a pluggable extension board and I want to make sure >> that you are not required to recompile the kernel when changing the >> extension board. >> >> However, you made a good point that in many cases it will be hardwired >> so it may be valuable to preconfigure this in a way that does not require >> scripts to set up variables in sysfs when you already know what is there. >> >> Note that my suggestion to put the device name into the firmware file >> covers this case, because you can then simply ship a firmware blob that >> matches the hardware configuration. Thinking about the future device >> tree setup, you can even put the firmware blob itself into a property >> in the device tree file. >> > > I earlier had an implementation where I used a pruss_devices structure > in the board file. > > http://linux.omap.com/pipermail/davinci-linux-open-source/ > 2011-March/022339.html. > > We can use this implementation along with the sysfs to load the devices > runtime. The configs that I have in the board_file for the devices > structure, are fixed for a board. To swap the boards, we do not need to > re-compile the kernel. > > > From nsekhar at ti.com Fri May 20 02:08:49 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 20 May 2011 12:38:49 +0530 Subject: [PATCH 2/2] davinci: da850evm: enable mcasp ping-pong bufs and evq's In-Reply-To: <20110519211406.GE18849@opensource.wolfsonmicro.com> References: <20110519211406.GE18849@opensource.wolfsonmicro.com> Message-ID: Hi Mark, On Fri, May 20, 2011 at 02:44:06, Mark Brown wrote: > On Wed, May 18, 2011 at 09:27:46AM -0400, Ben Gardiner wrote: > > The davinci-mcasp driver will copy the platform data specified sram sizes and > > eventq's. > > > > Set the event queues and sram sizes for da850. 8K SRAM buffers are selected > > because it is the minimum that resulted in the same period size when testing > > 48KHz S16_LE stereo. Event queues 0 and 1 are assigned to match those > > specified by Troy Kisky in his introduction of ping-pong buffers for dm644x. > > > > Signed-off-by: Ben Gardiner > > Davinci folks, are you OK with this? No, please don't merge this just yet. As Ben mentioned in his cover letter, the default SRAM pool on DA850 is not suitable for Audio transfers. This patch depends on SRAM not being available for audio usage in spite of the platform asking for it. This is liable to break. The SRAM allocation on DaVinci and OMAP is under a bit of flux and once that is sorted out, this patch should be safe to merge. So, this probably needs to wait for 2.6.41. Thanks, Sekhar From subhasish at mistralsolutions.com Fri May 20 02:14:00 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 20 May 2011 12:44:00 +0530 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: References: <78EB813416554F1A973EF20AA714ADF8@subhasishg> Message-ID: Hi Sekhar, >> > ok, so what you are suggesting is that I implement some >> > callbacks (like .fifo_alloc, .fifo_dealloc) which can allocate >> > memory using sram_alloc. >> > My doubt is, if already such API's are there (by Russel) or you >> > are suggesting to implement them. >> >> I can add the fifo_alloc/dealloc as part of the suart platform_data, >> Is that's what you are suggesting. If you may please point me out >> to any example. I am just not clear with this. > > Yes, that's what I am suggesting. I don't have an exact example > for you, but for an example of how function pointers can be passed > along in platform data you can look at the platform data for > DA8x FB driver in include/video/da8xx-fb.h. > Does this look ok. This code snippet is in the driver file. if (soft_uart->use_sram) { soft_uart->suart_iomap.p_fifo_buff_virt_base = pdata->fifo_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) { soft_uart->suart_iomap.p_fifo_buff_virt_base = dma_alloc_coherent(&pdev->dev, SUART_CNTX_SZ * NR_SUART * 2, (dma_addr_t *) &soft_uart->suart_iomap. p_fifo_buff_phys_base, GFP_KERNEL); soft_uart->use_sram = false; if (!soft_uart->suart_iomap.p_fifo_buff_virt_base) goto probe_exit_iounmap; } } else { soft_uart->suart_iomap.p_fifo_buff_virt_base = dma_alloc_coherent(&pdev->dev, SUART_CNTX_SZ * NR_SUART * 2, (dma_addr_t *) &soft_uart->suart_iomap. p_fifo_buff_phys_base, GFP_KERNEL); if (!soft_uart->suart_iomap.p_fifo_buff_virt_base) goto probe_exit_iounmap; } I added the fifo_alloc into the suart_data of the board file. static struct da850_evm_pruss_suart_data suart_data = { .version = 1, .setup = da850_evm_pruss_suart_setup, .fifo_alloc = sram_alloc, .fifo_free = sram_free, }; Best Regards, Subhasish Ghosh From Daniele.Bosi at mta.it Fri May 20 03:21:48 2011 From: Daniele.Bosi at mta.it (Bosi Daniele) Date: Fri, 20 May 2011 10:21:48 +0200 Subject: audio issue on dm368 Message-ID: <531B6536C9F737458807DF75064873C801ED5F36E7D0@mta-digimail.MTA.INT> Hi guys, I'm working on dm368 davinci processor, in a board that is acquiring video from 2 cameras and 1 audio channel (mono), from a microphone. I'm using dvsdk_2_10_01_18 with linux kernel patched from UDWORKS (2.6.18_pro500-davinci_evm-arm_v5t_le). I'm basically using the mcvip framework to acquire audio / video data in my application. Meanwhile the video acquisition seems to don't have any problems during long time tests, I see problems on the audio acquisition. After some hours of acquisition (it seems to depend from the cpu load, but I'm not sure), the audio samples on the lonely audio channel I have get silent (I read only 0x00 bytes or 0xFF bytes). About the hardware: the audio is taken from a tvp5158 via I2S line. Has someone seen an issue like this before? Thanks bye Daniele From manjunath.hadli at ti.com Fri May 20 08:46:55 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:16:55 +0530 Subject: [PATCH v17 0/6] davinci vpbe: dm6446 v4l2 driver Message-ID: <1305899215-1886-1-git-send-email-manjunath.hadli@ti.com> version17 : Fixed Laurent Pinchart's comments for: 1.ISR reorganization 2.probe function cleanup 3.try_format cleanup 4.Minor fixes sssssssss Hadli (6): 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 Documentation/video4linux/README.davinci-vpbe | 93 ++ drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 864 ++++++++++++ drivers/media/video/davinci/vpbe_display.c | 1861 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1231 ++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ include/media/davinci/vpbe.h | 184 +++ include/media/davinci/vpbe_display.h | 147 ++ include/media/davinci/vpbe_osd.h | 394 ++++++ include/media/davinci/vpbe_types.h | 91 ++ include/media/davinci/vpbe_venc.h | 45 + 14 files changed, 6041 insertions(+), 0 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 Fri May 20 08:47:20 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:17:20 +0530 Subject: [PATCH v17 1/6] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1305899240-1938-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 | 1861 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 147 +++ include/media/davinci/vpbe_types.h | 91 ++ 3 files changed, 2099 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..ae7add1 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,1861 @@ +/* + * 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 "vpbe_venc_regs.h" + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +module_param(debug, int, 0644); + +static int venc_is_second_field(struct vpbe_display *disp_dev) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int ret; + 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; +} + +static void vpbe_isr_even_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct timespec timevalue; + + if (layer->cur_frm == layer->next_frm) + return; + ktime_get_ts(&timevalue); + layer->cur_frm->ts.tv_sec = timevalue.tv_sec; + layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC; + 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; +} + +static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct osd_state *osd_device = disp_obj->osd_device; + unsigned long addr; + + spin_lock(&disp_obj->dma_queue_lock); + if (list_empty(&layer->dma_queue) || + (layer->cur_frm != layer->next_frm)) { + spin_unlock(&disp_obj->dma_queue_lock); + return; + } + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + 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); + spin_unlock(&disp_obj->dma_queue_lock); + /* 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_obj->cbcr_ofst); +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + struct vpbe_display *disp_dev = (struct vpbe_display *)arg; + struct vpbe_layer *layer; + static unsigned last_event; + unsigned event = 0; + int fid; + int i; + + if ((NULL == arg) || (NULL == disp_dev->dev[0])) + return IRQ_HANDLED; + + if (venc_is_second_field(disp_dev)) + 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; + + 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; + + if (layer->layer_first_int) { + layer->layer_first_int = 0; + continue; + } + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & VENC_END_OF_FRAME)) { + /* Progressive mode */ + + vpbe_isr_even_field(disp_dev, layer); + vpbe_isr_odd_field(disp_dev, layer); + } else { + /* Interlaced mode */ + + layer->field_id ^= 1; + if (event & VENC_FIRST_FIELD) + fid = 0; + else + fid = 1; + + /* + * If field id does not match with store + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + continue; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) + vpbe_isr_even_field(disp_dev, layer); + else /* odd field */ + vpbe_isr_odd_field(disp_dev, layer); + } + } + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < VPBE_DEFAULT_NUM_BUFS) + *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS; + + 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_layer *layer = fh->layer; + struct vpbe_display *disp = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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_layer* +_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev, + struct vpbe_layer *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_osd_display_params(struct vpbe_display *disp_dev, + struct vpbe_layer *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + 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_layer *otherlayer = + _vpbe_display_get_other_win_layer(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_layer *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; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int calculated_xsize; + int h_exp = 0; + int v_exp = 0; + int h_scale; + int v_scale; + + 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)) { + calculated_xsize = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (calculated_xsize <= expected_xsize) { + h_exp = 1; + cfg->xsize = calculated_xsize; + } + } + } + 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)) { + calculated_xsize = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (calculated_xsize <= expected_ysize) { + v_exp = 1; + cfg->ysize = calculated_xsize; + } + } + } + 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_layer *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + cfg->xpos = min((unsigned int)left, + vpbe_dev->current_timings.xres - cfg->xsize); + cfg->ypos = min((unsigned int)top, + vpbe_dev->current_timings.yres - cfg->ysize); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + if ((c->width == 0) || + ((c->width + c->left) > vpbe_dev->current_timings.xres)) + c->width = vpbe_dev->current_timings.xres - c->left; + + if ((c->height == 0) || ((c->height + c->top) > + vpbe_dev->current_timings.yres)) + c->height = vpbe_dev->current_timings.yres - c->top; + + /* window height must be even for interlaced display */ + if (vpbe_dev->current_timings.interlaced) + c->height &= (~0x01); + +} + +/** + * 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. + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int min_height = 1; + int min_width = 32; + int max_height; + int max_width; + int bpp; + + 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_INTERLACED) && + (pixfmt->field != V4L2_FIELD_NONE)) { + 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->width < min_width) || + (pixfmt->width > max_width)) { + pixfmt->width = vpbe_dev->current_timings.xres; + } + + if (!pixfmt->height || (pixfmt->height < min_height) || + (pixfmt->height > max_height)) { + pixfmt->height = vpbe_dev->current_timings.yres; + } + + if (pixfmt->bytesperline < (pixfmt->width * bpp)) + pixfmt->bytesperline = pixfmt->width * bpp; + + /* Make the bytesperline 32 byte aligned */ + pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + 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_layer *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_layer *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_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + cap->version = VPBE_DISPLAY_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); + strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card)); + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (rect->top < 0) + rect->top = 0; + if (rect->left < 0) + rect->left = 0; + + vpbe_disp_check_window_params(disp_dev, rect); + + 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); + + return 0; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + 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; + + return 0; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + /* Fill in the information about format */ + fmt->fmt.pix = layer->pix_fmt; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + 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 > 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 { + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + 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; + } + /* 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; + + if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat) + cfg->pixfmt = PIXFMT_YCbCrI; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_layer *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win_layer(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); + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + +} + +/** + * 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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; + } + } else { + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL == vpbe_dev->ops.enum_outputs) + return -EINVAL; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) + return -EINVAL; + + 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 0; +} + +/** + * 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + if (NULL == vpbe_dev->ops.enum_dv_presets) + return -EINVAL; + + 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 0; +} + +/** + * 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) + return -EINVAL; + + 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 0; +} + +/** + * 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + int ret; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + 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_osd_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->layer_first_int = 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = filep->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned int err = 0; + + 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; +} + +/* + * 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) +{ + struct vpbe_fh *fh = NULL; + struct vpbe_layer *layer = video_drvdata(file); + struct vpbe_display *disp_dev = layer->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int err; + + /* 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) { + + /* 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"); + kfree(fh); + 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_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + + /* 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_layer *otherlayer; + otherlayer = + _vpbe_display_get_other_win_layer(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); + struct vpbe_display *vpbe_disp = data; + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_disp->osd_device = platform_get_drvdata(pdev); + + return 0; +} + +static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, + struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer = NULL; + struct video_device *vbd = NULL; + + /* Allocate memory for four plane display objects */ + + disp_dev->dev[i] = + kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL); + + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + return -ENOMEM; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + vbd = &vpbe_display_layer->video_dev; + /* Initialize field of video device */ + vbd->release = video_device_release_empty; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (disp_dev->vpbe_dev->current_timings.timings_type & + VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + disp_dev->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); + + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + return 0; +} + +static __devinit int register_device(struct vpbe_layer *vpbe_display_layer, + struct vpbe_display *disp_dev, + struct platform_device *pdev) { + int err; + + v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&disp_dev->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, + -1); + if (err) + return -ENODEV; + + vpbe_display_layer->disp_dev = disp_dev; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(&vpbe_display_layer->video_dev, + vpbe_display_layer); + + 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) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev; + struct resource *res = NULL; + int k; + int i; + int err; + 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; + } + + spin_lock_init(&disp_dev->dma_queue_lock); + /* + * 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, disp_dev, + vpbe_device_get); + if (err < 0) + return err; + /* Initialize the vpbe display controller */ + if (NULL != disp_dev->vpbe_dev->ops.initialize) { + err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, + disp_dev->vpbe_dev); + if (err) { + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, + "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (init_vpbe_layer(i, disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&disp_dev->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(&disp_dev->vpbe_dev->v4l2_dev, + "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (register_device(disp_dev->dev[i], disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; + +probe_out: + free_irq(res->start, disp_dev); + for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + if (vpbe_display_layer) { + video_unregister_device( + &vpbe_display_layer->video_dev); + kfree(disp_dev->dev[k]); + } + } + kfree(disp_dev); + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct resource *res; + int i; + + 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); + + } + 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; + + 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 DM644x/DM355/DM365 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..dbf6b37 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,147 @@ +/* + * 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 + +/* 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_layer { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* Pointer to the vpbe_display */ + struct vpbe_display *disp_dev; + /* 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; + u8 layer_first_int; +}; + +/* 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_layer *dev[VPBE_DISPLAY_MAX_DEVICES]; + struct vpbe_device *vpbe_dev; + struct osd_state *osd_device; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_layer *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]; +}; + +#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..727f551 --- /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_version { + 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 Fri May 20 08:47:55 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:17:55 +0530 Subject: [PATCH v17 2/6] davinci vpbe: VPBE display driver Message-ID: <1305899275-2000-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 | 864 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 184 ++++++++ 2 files changed, 1048 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..d773d30 --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,864 @@ +/* + * 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 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; + int 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; + int 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; + int 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; + int 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 encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + struct vpbe_config *cfg = vpbe_dev->cfg; + int enc_out_index; + int sd_index; + int ret = 0; + + 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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 ret = 0; + int i; + + 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 out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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 j = 0; + int i; + + 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 out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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_enc_mode_info *preset_mode = NULL; + struct vpbe_config *cfg = vpbe_dev->cfg; + struct v4l2_dv_preset dv_preset; + struct osd_state *osd_device; + int out_index = vpbe_dev->current_out_index; + int ret = 0; + int i; + + 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); + + osd_device = vpbe_dev->osd_device; + 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); + struct vpbe_device *vpbe_dev = data; + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_dev->osd_device = platform_get_drvdata(pdev); + + 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; + struct osd_state *osd_device; + struct i2c_adapter *i2c_adap; + int output_index; + int num_encoders; + int ret = 0; + int err; + int i; + + /* + * 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, vpbe_dev, + 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 */ + osd_device = vpbe_dev->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; + /* disable 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_device *vpbe_dev; + struct vpbe_config *cfg; + 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..8b11fb0 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,184 @@ +/* + * 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 +#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; + /* osd_device pointer */ + struct osd_state *osd_device; + /* + * 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 Fri May 20 08:48:11 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:18:11 +0530 Subject: [PATCH v17 3/6] davinci vpbe: OSD(On Screen Display) block Message-ID: <1305899291-2041-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 | 1231 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 ++++++++ include/media/davinci/vpbe_osd.h | 394 +++++++++ 3 files changed, 1989 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..5352884 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1231 @@ +/* + * 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) +{ + static const int map_2bpp[] = { 0, 5, 10, 15 }; + static const int map_1bpp[] = { 0, 15 }; + int bmp_offset; + int bmp_shift; + int bmp_mask; + int bmp_reg; + + 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]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + 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]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + 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; + + /* 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; + unsigned long flags; + int reject_config; + + 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_platform_data *pdata; + struct osd_state *osd; + struct resource *res; + 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_version)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..d7e397a --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,394 @@ +/* + * 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 + +#include + +#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; +}; + +/* 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_version 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_version vpbe_type; + int field_inv_wa_enable; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Fri May 20 08:48:27 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:18:27 +0530 Subject: [PATCH v17 4/6] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1305899307-2079-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 | 566 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 ++++++++ include/media/davinci/vpbe_venc.h | 45 ++ 3 files changed, 788 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..03a3e5c --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,566 @@ +/* + * 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) +{ + 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: + return -EINVAL; + } + + return 0; +} + +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 ret; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + 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; + + /* 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..426c205 --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,45 @@ +/* + * 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 +#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_version 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 Fri May 20 08:48:44 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:18:44 +0530 Subject: [PATCH v17 5/6] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1305899324-2118-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 Fri May 20 08:49:08 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:19:08 +0530 Subject: [PATCH v17 6/6] davinci vpbe: Readme text for Dm6446 vpbe Message-ID: <1305899348-2164-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 Fri May 20 08:58:49 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:28:49 +0530 Subject: [PATCH 1/1] davinci: dm646x: move vpif related code to driver core header from platform Message-ID: <1305899929-2509-1-git-send-email-manjunath.hadli@ti.com> move vpif related code for capture and display drivers from dm646x platform header file to vpif.h as these definitions are related to driver code more than the platform or board. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/include/mach/dm646x.h | 53 +------------------- drivers/media/video/davinci/vpif.h | 1 + drivers/media/video/davinci/vpif_capture.h | 2 +- drivers/media/video/davinci/vpif_display.h | 1 + include/media/davinci/vpif.h | 73 +++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 53 deletions(-) create mode 100644 include/media/davinci/vpif.h diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 7a27f3f..245a1c0 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -17,6 +17,7 @@ #include #include #include +#include #define DM646X_EMAC_BASE (0x01C80000) #define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) @@ -36,58 +37,6 @@ int __init dm646x_init_edma(struct edma_rsv_info *rsv); void dm646x_video_init(void); -enum vpif_if_type { - VPIF_IF_BT656, - VPIF_IF_BT1120, - VPIF_IF_RAW_BAYER -}; - -struct vpif_interface { - enum vpif_if_type if_type; - unsigned hd_pol:1; - unsigned vd_pol:1; - unsigned fid_pol:1; -}; - -struct vpif_subdev_info { - const char *name; - struct i2c_board_info board_info; - u32 input; - u32 output; - unsigned can_route:1; - struct vpif_interface vpif_if; -}; - -struct vpif_display_config { - int (*set_clock)(int, int); - struct vpif_subdev_info *subdevinfo; - int subdev_count; - const char **output; - int output_count; - const char *card_name; -}; - -struct vpif_input { - struct v4l2_input input; - const char *subdev_name; -}; - -#define VPIF_CAPTURE_MAX_CHANNELS 2 - -struct vpif_capture_chan_config { - const struct vpif_input *inputs; - int input_count; -}; - -struct vpif_capture_config { - int (*setup_input_channel_mode)(int); - int (*setup_input_path)(int, const char *); - struct vpif_capture_chan_config chan_config[VPIF_CAPTURE_MAX_CHANNELS]; - struct vpif_subdev_info *subdev_info; - int subdev_count; - const char *card_name; -}; - void dm646x_setup_vpif(struct vpif_display_config *, struct vpif_capture_config *); diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index 10550bd..e76dded 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -20,6 +20,7 @@ #include #include #include +#include /* Maximum channel allowed */ #define VPIF_NUM_CHANNELS (4) diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h index 7a4196d..fa50b6b 100644 --- a/drivers/media/video/davinci/vpif_capture.h +++ b/drivers/media/video/davinci/vpif_capture.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "vpif.h" diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h index b53aaa8..b531a01 100644 --- a/drivers/media/video/davinci/vpif_display.h +++ b/drivers/media/video/davinci/vpif_display.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "vpif.h" diff --git a/include/media/davinci/vpif.h b/include/media/davinci/vpif.h new file mode 100644 index 0000000..e4a4dc1 --- /dev/null +++ b/include/media/davinci/vpif.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011 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 _VPIF_INC_H +#define _VPIF_INC_H + +#include + +#define VPIF_CAPTURE_MAX_CHANNELS 2 + +enum vpif_if_type { + VPIF_IF_BT656, + VPIF_IF_BT1120, + VPIF_IF_RAW_BAYER +}; + +struct vpif_interface { + enum vpif_if_type if_type; + unsigned hd_pol:1; + unsigned vd_pol:1; + unsigned fid_pol:1; +}; + +struct vpif_subdev_info { + const char *name; + struct i2c_board_info board_info; + u32 input; + u32 output; + unsigned can_route:1; + struct vpif_interface vpif_if; +}; + +struct vpif_display_config { + int (*set_clock)(int, int); + struct vpif_subdev_info *subdevinfo; + int subdev_count; + const char **output; + int output_count; + const char *card_name; +}; + +struct vpif_input { + struct v4l2_input input; + const char *subdev_name; +}; + +struct vpif_capture_chan_config { + const struct vpif_input *inputs; + int input_count; +}; + +struct vpif_capture_config { + int (*setup_input_channel_mode)(int); + int (*setup_input_path)(int, const char *); + struct vpif_capture_chan_config chan_config[VPIF_CAPTURE_MAX_CHANNELS]; + struct vpif_subdev_info *subdev_info; + int subdev_count; + const char *card_name; +}; +#endif /* _VPIF_INC_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Fri May 20 09:08:17 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:38:17 +0530 Subject: [PATCH 1/4] davinci: dm644x: move EMAC definitions from dm644x.h to dm644x.c Message-ID: <1305900497-3668-1-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm644x platform file from platform header dm644x.h to dm644x.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm644x.c | 16 +++++++++++----- arch/arm/mach-davinci/include/mach/dm644x.h | 7 ------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 9a2376b..4fbb250 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -30,11 +30,6 @@ #include "clock.h" #include "mux.h" -/* - * Device specific clocks - */ -#define DM644X_REF_FREQ 27000000 - static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, @@ -45,6 +40,8 @@ static struct pll_data pll2_data = { .phys_base = DAVINCI_PLL2_BASE, }; +#define DM644X_REF_FREQ 27000000 + static struct clk ref_clk = { .name = "ref_clk", .rate = DM644X_REF_FREQ, @@ -318,6 +315,11 @@ static struct clk_lookup dm644x_clks[] = { CLK(NULL, NULL, NULL), }; +#define DM644X_EMAC_CNTRL_OFFSET 0x0000 +#define DM644X_EMAC_CNTRL_MOD_OFFSET 0x1000 +#define DM644X_EMAC_CNTRL_RAM_OFFSET 0x2000 +#define DM644X_EMAC_CNTRL_RAM_SIZE 0x2000 + static struct emac_platform_data dm644x_emac_pdata = { .ctrl_reg_offset = DM644X_EMAC_CNTRL_OFFSET, .ctrl_mod_reg_offset = DM644X_EMAC_CNTRL_MOD_OFFSET, @@ -326,6 +328,8 @@ static struct emac_platform_data dm644x_emac_pdata = { .version = EMAC_VERSION_1, }; +#define DM644X_EMAC_BASE 0x01C80000 + static struct resource dm644x_emac_resources[] = { { .start = DM644X_EMAC_BASE, @@ -349,6 +353,8 @@ static struct platform_device dm644x_emac_device = { .resource = dm644x_emac_resources, }; +#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) + static struct resource dm644x_mdio_resources[] = { { .start = DM644X_EMAC_MDIO_BASE, diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 5a1b26d..724377f 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -27,13 +27,6 @@ #include #include -#define DM644X_EMAC_BASE (0x01C80000) -#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) -#define DM644X_EMAC_CNTRL_OFFSET (0x0000) -#define DM644X_EMAC_CNTRL_MOD_OFFSET (0x1000) -#define DM644X_EMAC_CNTRL_RAM_OFFSET (0x2000) -#define DM644X_EMAC_CNTRL_RAM_SIZE (0x2000) - #define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01E00000 #define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 #define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -- 1.6.2.4 From manjunath.hadli at ti.com Fri May 20 09:08:40 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:38:40 +0530 Subject: [PATCH 2/4] davinci: dm365: move macros local to dm365.c to that file Message-ID: <1305900520-3725-1-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm365 platform file from platform header dm365.h to dm365.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm365.c | 20 ++++++++++++++++++-- arch/arm/mach-davinci/include/mach/dm365.h | 12 ------------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 02d2cc3..7e2464e 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -39,8 +39,6 @@ #include "clock.h" #include "mux.h" -#define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */ - static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, @@ -53,6 +51,8 @@ static struct pll_data pll2_data = { .flags = PLL_HAS_POSTDIV | PLL_HAS_PREDIV, }; +#define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */ + static struct clk ref_clk = { .name = "ref_clk", .rate = DM365_REF_FREQ, @@ -681,6 +681,10 @@ void __init dm365_init_spi0(unsigned chipselect_mask, platform_device_register(&dm365_spi0_device); } +#define DM365_EMAC_CNTRL_OFFSET 0x0000 +#define DM365_EMAC_CNTRL_MOD_OFFSET 0x3000 +#define DM365_EMAC_CNTRL_RAM_OFFSET 0x1000 +#define DM365_EMAC_CNTRL_RAM_SIZE 0x2000 static struct emac_platform_data dm365_emac_pdata = { .ctrl_reg_offset = DM365_EMAC_CNTRL_OFFSET, .ctrl_mod_reg_offset = DM365_EMAC_CNTRL_MOD_OFFSET, @@ -689,6 +693,8 @@ static struct emac_platform_data dm365_emac_pdata = { .version = EMAC_VERSION_2, }; +#define DM365_EMAC_BASE 0x01D07000 + static struct resource dm365_emac_resources[] = { { .start = DM365_EMAC_BASE, @@ -727,6 +733,8 @@ static struct platform_device dm365_emac_device = { .resource = dm365_emac_resources, }; +#define DM365_EMAC_MDIO_BASE (DM365_EMAC_BASE + 0x4000) + static struct resource dm365_mdio_resources[] = { { .start = DM365_EMAC_MDIO_BASE, @@ -922,6 +930,10 @@ static struct platform_device dm365_asp_device = { .resource = dm365_asp_resources, }; +#define DAVINCI_DM365_VC_BASE 0x01D0C000 +#define DAVINCI_DMA_VC_TX 2 +#define DAVINCI_DMA_VC_RX 3 + static struct resource dm365_vc_resources[] = { { .start = DAVINCI_DM365_VC_BASE, @@ -947,6 +959,8 @@ static struct platform_device dm365_vc_device = { .resource = dm365_vc_resources, }; +#define DM365_RTC_BASE 0x01C69000 + static struct resource dm365_rtc_resources[] = { { .start = DM365_RTC_BASE, @@ -981,6 +995,8 @@ static struct map_desc dm365_io_desc[] = { }, }; +#define DM365_KEYSCAN_BASE 0x01C69400 + static struct resource dm365_ks_resources[] = { { /* registers */ diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h index 2563bf4..c3c69a6 100644 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ b/arch/arm/mach-davinci/include/mach/dm365.h @@ -20,18 +20,6 @@ #include #include -#define DM365_EMAC_BASE (0x01D07000) -#define DM365_EMAC_MDIO_BASE (DM365_EMAC_BASE + 0x4000) -#define DM365_EMAC_CNTRL_OFFSET (0x0000) -#define DM365_EMAC_CNTRL_MOD_OFFSET (0x3000) -#define DM365_EMAC_CNTRL_RAM_OFFSET (0x1000) -#define DM365_EMAC_CNTRL_RAM_SIZE (0x2000) - -/* Base of key scan register bank */ -#define DM365_KEYSCAN_BASE (0x01C69400) - -#define DM365_RTC_BASE (0x01C69000) - #define DAVINCI_DM365_VC_BASE (0x01D0C000) #define DAVINCI_DMA_VC_TX 2 #define DAVINCI_DMA_VC_RX 3 -- 1.6.2.4 From manjunath.hadli at ti.com Fri May 20 09:12:01 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:42:01 +0530 Subject: [PATCH 3/4] davinci: dm646x: remove the macros from the header to move to c file Message-ID: <1305900721-3890-1-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm646x platform file from platform header dm646x.h to dm646x.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm646x.c | 34 +++++++++++++++----------- arch/arm/mach-davinci/include/mach/dm646x.h | 7 ----- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..9efa380 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -30,20 +30,6 @@ #include "clock.h" #include "mux.h" -#define DAVINCI_VPIF_BASE (0x01C12000) -#define VDD3P3V_PWDN_OFFSET (0x48) -#define VSCLKDIS_OFFSET (0x6C) - -#define VDD3P3V_VID_MASK (BIT_MASK(3) | BIT_MASK(2) | BIT_MASK(1) |\ - BIT_MASK(0)) -#define VSCLKDIS_MASK (BIT_MASK(11) | BIT_MASK(10) | BIT_MASK(9) |\ - BIT_MASK(8)) - -/* - * Device specific clocks - */ -#define DM646X_AUX_FREQ 24000000 - static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, @@ -58,6 +44,8 @@ static struct clk ref_clk = { .name = "ref_clk", }; +#define DM646X_AUX_FREQ 24000000 + static struct clk aux_clkin = { .name = "aux_clkin", .rate = DM646X_AUX_FREQ, @@ -354,6 +342,11 @@ static struct clk_lookup dm646x_clks[] = { CLK(NULL, NULL, NULL), }; +#define DM646X_EMAC_CNTRL_OFFSET 0x0000 +#define DM646X_EMAC_CNTRL_MOD_OFFSET 0x1000 +#define DM646X_EMAC_CNTRL_RAM_OFFSET 0x2000 +#define DM646X_EMAC_CNTRL_RAM_SIZE 0x2000 + static struct emac_platform_data dm646x_emac_pdata = { .ctrl_reg_offset = DM646X_EMAC_CNTRL_OFFSET, .ctrl_mod_reg_offset = DM646X_EMAC_CNTRL_MOD_OFFSET, @@ -362,6 +355,8 @@ static struct emac_platform_data dm646x_emac_pdata = { .version = EMAC_VERSION_2, }; +#define DM646X_EMAC_BASE 0x01C80000 + static struct resource dm646x_emac_resources[] = { { .start = DM646X_EMAC_BASE, @@ -400,6 +395,8 @@ static struct platform_device dm646x_emac_device = { .resource = dm646x_emac_resources, }; +#define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) + static struct resource dm646x_mdio_resources[] = { { .start = DM646X_EMAC_MDIO_BASE, @@ -671,6 +668,8 @@ static struct platform_device dm646x_dit_device = { static u64 vpif_dma_mask = DMA_BIT_MASK(32); +#define DAVINCI_VPIF_BASE 0x01C12000 + static struct resource vpif_resource[] = { { .start = DAVINCI_VPIF_BASE, @@ -866,6 +865,13 @@ void __init dm646x_init_mcasp1(struct snd_platform_data *pdata) platform_device_register(&dm646x_dit_device); } +#define VDD3P3V_PWDN_OFFSET 0x48 +#define VSCLKDIS_OFFSET 0x6C +#define VDD3P3V_VID_MASK (BIT_MASK(3) | BIT_MASK(2) | BIT_MASK(1) |\ + BIT_MASK(0)) +#define VSCLKDIS_MASK (BIT_MASK(11) | BIT_MASK(10) | BIT_MASK(9) |\ + BIT_MASK(8)) + void dm646x_setup_vpif(struct vpif_display_config *display_config, struct vpif_capture_config *capture_config) { diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 245a1c0..e36ab28 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -19,13 +19,6 @@ #include #include -#define DM646X_EMAC_BASE (0x01C80000) -#define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) -#define DM646X_EMAC_CNTRL_OFFSET (0x0000) -#define DM646X_EMAC_CNTRL_MOD_OFFSET (0x1000) -#define DM646X_EMAC_CNTRL_RAM_OFFSET (0x2000) -#define DM646X_EMAC_CNTRL_RAM_SIZE (0x2000) - #define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 #define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 -- 1.6.2.4 From manjunath.hadli at ti.com Fri May 20 09:14:01 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 20 May 2011 19:44:01 +0530 Subject: [PATCH 4/4] davinci: create new common platform header for davinci Message-ID: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> remove the code from individual platform header files for dm365, dm355, dm644x and dm646x and consolidate it into a single and common header file dmx.h. Include the new header file in individual platform header files as a pre-cursor for deleting these headers in follow up patches. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/board-dm355-evm.c | 2 +- arch/arm/mach-davinci/board-dm355-leopard.c | 2 +- arch/arm/mach-davinci/board-dm365-evm.c | 6 ++- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/board-dm646x-evm.c | 5 ++- arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- arch/arm/mach-davinci/board-sffsdr.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/dm355.h | 32 ----------- arch/arm/mach-davinci/include/mach/dm365.h | 40 -------------- arch/arm/mach-davinci/include/mach/dm644x.h | 40 -------------- arch/arm/mach-davinci/include/mach/dmx.h | 77 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpif.h | 2 +- drivers/media/video/davinci/vpif_capture.h | 1 + drivers/media/video/davinci/vpif_display.c | 2 +- 18 files changed, 98 insertions(+), 125 deletions(-) delete mode 100644 arch/arm/mach-davinci/include/mach/dm355.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm365.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm644x.h create mode 100644 arch/arm/mach-davinci/include/mach/dmx.h diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index 6e7cad1..bcb2974 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index 543f991..3694b32 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index c67f684..7be75b2 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include #include @@ -143,6 +143,10 @@ static struct davinci_nand_pdata davinci_nand_data = { .ecc_bits = 4, }; +#define DM365_ASYNC_EMIF_CONTROL_BASE 0x01D10000 +#define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 +#define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 + static struct resource davinci_nand_resources[] = { { .start = DM365_ASYNC_EMIF_DATA_CE0_BASE, diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 0ca90b8..c365458 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index f6ac9ba..73497a6 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include #include @@ -91,6 +91,9 @@ static struct davinci_nand_pdata davinci_nand_data = { .options = 0, }; +#define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 +#define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 + static struct resource davinci_nand_resources[] = { { .start = DM646X_ASYNC_EMIF_CS2_SPACE_BASE, diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index 6c389ff..52b30c8 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c index 61ac96d..16c96a5 100644 --- a/arch/arm/mach-davinci/board-sffsdr.c +++ b/arch/arm/mach-davinci/board-sffsdr.c @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index a5f8a80..36cf3bb 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -19,7 +19,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 7e2464e..54f6aaf 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -22,7 +22,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 4fbb250..3efcdef 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -16,7 +16,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 9efa380..015c59f 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -16,7 +16,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/include/mach/dm355.h b/arch/arm/mach-davinci/include/mach/dm355.h deleted file mode 100644 index 36dff4a..0000000 --- a/arch/arm/mach-davinci/include/mach/dm355.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Chip specific defines for DM355 SoC - * - * Author: Kevin Hilman, Deep Root Systems, LLC - * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#ifndef __ASM_ARCH_DM355_H -#define __ASM_ARCH_DM355_H - -#include -#include -#include - -#define DM355_ASYNC_EMIF_CONTROL_BASE 0x01E10000 -#define DM355_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 - -#define ASP1_TX_EVT_EN 1 -#define ASP1_RX_EVT_EN 2 - -struct spi_board_info; - -void __init dm355_init(void); -void dm355_init_spi0(unsigned chipselect_mask, - struct spi_board_info *info, unsigned len); -void __init dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata); -void dm355_set_vpfe_config(struct vpfe_config *cfg); - -#endif /* __ASM_ARCH_DM355_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h deleted file mode 100644 index c3c69a6..0000000 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ -#ifndef __ASM_ARCH_DM365_H -#define __ASM_ARCH_DM665_H - -#include -#include -#include -#include -#include -#include - -#define DAVINCI_DM365_VC_BASE (0x01D0C000) -#define DAVINCI_DMA_VC_TX 2 -#define DAVINCI_DMA_VC_RX 3 - -#define DM365_ASYNC_EMIF_CONTROL_BASE 0x01D10000 -#define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 -#define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 - -void __init dm365_init(void); -void __init dm365_init_asp(struct snd_platform_data *pdata); -void __init dm365_init_vc(struct snd_platform_data *pdata); -void __init dm365_init_ks(struct davinci_ks_platform_data *pdata); -void __init dm365_init_rtc(void); -void dm365_init_spi0(unsigned chipselect_mask, - struct spi_board_info *info, unsigned len); - -void dm365_set_vpfe_config(struct vpfe_config *cfg); -#endif /* __ASM_ARCH_DM365_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h deleted file mode 100644 index 724377f..0000000 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file contains the processor specific definitions - * of the TI DM644x. - * - * Copyright (C) 2008 Texas Instruments. - * - * 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. - * - * 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 __ASM_ARCH_DM644X_H -#define __ASM_ARCH_DM644X_H - -#include -#include -#include -#include - -#define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01E00000 -#define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 -#define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -#define DM644X_ASYNC_EMIF_DATA_CE2_BASE 0x06000000 -#define DM644X_ASYNC_EMIF_DATA_CE3_BASE 0x08000000 - -void __init dm644x_init(void); -void __init dm644x_init_asp(struct snd_platform_data *pdata); -void dm644x_set_vpfe_config(struct vpfe_config *cfg); - -#endif /* __ASM_ARCH_DM644X_H */ diff --git a/arch/arm/mach-davinci/include/mach/dmx.h b/arch/arm/mach-davinci/include/mach/dmx.h new file mode 100644 index 0000000..67b1671 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/dmx.h @@ -0,0 +1,77 @@ +/* + * This file contains the processor specific definitions + * of the TI DM644x, DM355, DM365, and DM646x. + * + * Copyright (C) 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. + */ +#ifndef __DMX_H +#define __DMX_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* DM355 base addresses */ +#define DM355_ASYNC_EMIF_CONTROL_BASE 0x01E10000 +#define DM355_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 + +#define ASP1_TX_EVT_EN 1 +#define ASP1_RX_EVT_EN 2 + +/* DM644x base addresses */ +#define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01E00000 +#define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 +#define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 +#define DM644X_ASYNC_EMIF_DATA_CE2_BASE 0x06000000 +#define DM644X_ASYNC_EMIF_DATA_CE3_BASE 0x08000000 + +/* DM355 function declarations */ +struct spi_board_info; + +void dm355_init(void); +void dm355_init_spi0(unsigned chipselect_mask, + struct spi_board_info *info, unsigned len); +void dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata); +void dm355_set_vpfe_config(struct vpfe_config *cfg); + +/* DM365 function declarations */ +void dm365_init(void); +void dm365_init_asp(struct snd_platform_data *pdata); +void dm365_init_vc(struct snd_platform_data *pdata); +void dm365_init_ks(struct davinci_ks_platform_data *pdata); +void dm365_init_rtc(void); +void dm365_init_spi0(unsigned chipselect_mask, + struct spi_board_info *info, unsigned len); +void dm365_set_vpfe_config(struct vpfe_config *cfg); + +/* DM644X function declarations */ +void dm644x_init(void); +void dm644x_init_asp(struct snd_platform_data *pdata); +void dm644x_set_vpfe_config(struct vpfe_config *cfg); + +/* DM646X function declarations */ +void dm646x_init(void); +void dm646x_init_mcasp0(struct snd_platform_data *pdata); +void dm646x_init_mcasp1(struct snd_platform_data *pdata); +void dm646x_board_setup_refclk(struct clk *clk); +int dm646x_init_edma(struct edma_rsv_info *rsv); + +void dm646x_video_init(void); + +void dm646x_setup_vpif(struct vpif_display_config *, + struct vpif_capture_config *); +#endif /*__DMX_H */ diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index e76dded..5949e64 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include /* Maximum channel allowed */ diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h index fa50b6b..cafa762 100644 --- a/drivers/media/video/davinci/vpif_capture.h +++ b/drivers/media/video/davinci/vpif_capture.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "vpif.h" diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index cdf659a..a7d8bc8 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -40,7 +40,7 @@ #include #include -#include +#include #include "vpif_display.h" #include "vpif.h" -- 1.6.2.4 From sshtylyov at mvista.com Fri May 20 09:32:08 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 20 May 2011 18:32:08 +0400 Subject: [PATCH v17 5/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1305899324-2118-1-git-send-email-manjunath.hadli@ti.com> References: <1305899324-2118-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DD67B68.7070704@mvista.com> Hello. Manjunath Hadli wrote: > 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 [...] > 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" BTW, as this seems DM644x specific, shouldn't this depend on CONFIG_ARCH_DAVINCI_DM644x? > + 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 Or this one, if it selects VIDEO_DM644X_VPBE? > + default y Hm, "y" shouldn't be the default. WBR, Sergei From sshtylyov at mvista.com Fri May 20 10:06:27 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 20 May 2011 19:06:27 +0400 Subject: [PATCH 2/4] davinci: dm365: move macros local to dm365.c to that file In-Reply-To: <1305900520-3725-1-git-send-email-manjunath.hadli@ti.com> References: <1305900520-3725-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DD68373.6060808@mvista.com> Hello. Manjunath Hadli wrote: > move the register base addresses and offsets used only by dm365 > platform file from platform header dm365.h to dm365.c as they > are used only in the c file. > Signed-off-by: Manjunath Hadli > --- > arch/arm/mach-davinci/dm365.c | 20 ++++++++++++++++++-- > arch/arm/mach-davinci/include/mach/dm365.h | 12 ------------ > 2 files changed, 18 insertions(+), 14 deletions(-) > diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c > index 02d2cc3..7e2464e 100644 > --- a/arch/arm/mach-davinci/dm365.c > +++ b/arch/arm/mach-davinci/dm365.c > @@ -39,8 +39,6 @@ > #include "clock.h" > #include "mux.h" > > -#define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */ > - > static struct pll_data pll1_data = { > .num = 1, > .phys_base = DAVINCI_PLL1_BASE, > @@ -53,6 +51,8 @@ static struct pll_data pll2_data = { > .flags = PLL_HAS_POSTDIV | PLL_HAS_PREDIV, > }; > > +#define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */ Why move it? > static struct clk ref_clk = { > .name = "ref_clk", > .rate = DM365_REF_FREQ, > @@ -681,6 +681,10 @@ void __init dm365_init_spi0(unsigned chipselect_mask, > platform_device_register(&dm365_spi0_device); > } > > +#define DM365_EMAC_CNTRL_OFFSET 0x0000 > +#define DM365_EMAC_CNTRL_MOD_OFFSET 0x3000 > +#define DM365_EMAC_CNTRL_RAM_OFFSET 0x1000 > +#define DM365_EMAC_CNTRL_RAM_SIZE 0x2000 > static struct emac_platform_data dm365_emac_pdata = { > .ctrl_reg_offset = DM365_EMAC_CNTRL_OFFSET, > .ctrl_mod_reg_offset = DM365_EMAC_CNTRL_MOD_OFFSET, > @@ -689,6 +693,8 @@ static struct emac_platform_data dm365_emac_pdata = { > .version = EMAC_VERSION_2, > }; > > +#define DM365_EMAC_BASE 0x01D07000 > + > static struct resource dm365_emac_resources[] = { > { > .start = DM365_EMAC_BASE, > @@ -727,6 +733,8 @@ static struct platform_device dm365_emac_device = { > .resource = dm365_emac_resources, > }; > > +#define DM365_EMAC_MDIO_BASE (DM365_EMAC_BASE + 0x4000) > + Could we still have base address #define's grouped together at the start of file? > static struct resource dm365_mdio_resources[] = { > { > .start = DM365_EMAC_MDIO_BASE, > @@ -922,6 +930,10 @@ static struct platform_device dm365_asp_device = { > .resource = dm365_asp_resources, > }; > > +#define DAVINCI_DM365_VC_BASE 0x01D0C000 > +#define DAVINCI_DMA_VC_TX 2 > +#define DAVINCI_DMA_VC_RX 3 You've duplicated these... > diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h > index 2563bf4..c3c69a6 100644 > --- a/arch/arm/mach-davinci/include/mach/dm365.h > +++ b/arch/arm/mach-davinci/include/mach/dm365.h > @@ -20,18 +20,6 @@ [...] > #define DAVINCI_DM365_VC_BASE (0x01D0C000) > #define DAVINCI_DMA_VC_TX 2 > #define DAVINCI_DMA_VC_RX 3 ... as you've forgotten to remove these. WBR, Sergei From sshtylyov at mvista.com Fri May 20 10:08:19 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 20 May 2011 19:08:19 +0400 Subject: [PATCH 1/4] davinci: dm644x: move EMAC definitions from dm644x.h to dm644x.c In-Reply-To: <1305900497-3668-1-git-send-email-manjunath.hadli@ti.com> References: <1305900497-3668-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DD683E3.3020804@mvista.com> Hello. Manjunath Hadli wrote: > move the register base addresses and offsets used only by dm644x > platform file from platform header dm644x.h to dm644x.c as they > are used only in the c file. > Signed-off-by: Manjunath Hadli > --- > arch/arm/mach-davinci/dm644x.c | 16 +++++++++++----- > arch/arm/mach-davinci/include/mach/dm644x.h | 7 ------- > 2 files changed, 11 insertions(+), 12 deletions(-) > diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c > index 9a2376b..4fbb250 100644 > --- a/arch/arm/mach-davinci/dm644x.c > +++ b/arch/arm/mach-davinci/dm644x.c > @@ -30,11 +30,6 @@ > #include "clock.h" > #include "mux.h" > > -/* > - * Device specific clocks > - */ > -#define DM644X_REF_FREQ 27000000 > - > static struct pll_data pll1_data = { > .num = 1, > .phys_base = DAVINCI_PLL1_BASE, > @@ -45,6 +40,8 @@ static struct pll_data pll2_data = { > .phys_base = DAVINCI_PLL2_BASE, > }; > > +#define DM644X_REF_FREQ 27000000 Why move it (and remove the comment)? > static struct clk ref_clk = { > .name = "ref_clk", > .rate = DM644X_REF_FREQ, > @@ -318,6 +315,11 @@ static struct clk_lookup dm644x_clks[] = { > CLK(NULL, NULL, NULL), > }; > > +#define DM644X_EMAC_CNTRL_OFFSET 0x0000 > +#define DM644X_EMAC_CNTRL_MOD_OFFSET 0x1000 > +#define DM644X_EMAC_CNTRL_RAM_OFFSET 0x2000 > +#define DM644X_EMAC_CNTRL_RAM_SIZE 0x2000 > + > static struct emac_platform_data dm644x_emac_pdata = { > .ctrl_reg_offset = DM644X_EMAC_CNTRL_OFFSET, > .ctrl_mod_reg_offset = DM644X_EMAC_CNTRL_MOD_OFFSET, > @@ -326,6 +328,8 @@ static struct emac_platform_data dm644x_emac_pdata = { > .version = EMAC_VERSION_1, > }; > > +#define DM644X_EMAC_BASE 0x01C80000 > + > static struct resource dm644x_emac_resources[] = { > { > .start = DM644X_EMAC_BASE, > @@ -349,6 +353,8 @@ static struct platform_device dm644x_emac_device = { > .resource = dm644x_emac_resources, > }; > > +#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) I'd prefer that these macros would still be grouped together at the start of file... WBR, Sergei From sshtylyov at mvista.com Fri May 20 10:11:58 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 20 May 2011 19:11:58 +0400 Subject: [PATCH 3/4] davinci: dm646x: remove the macros from the header to move to c file In-Reply-To: <1305900721-3890-1-git-send-email-manjunath.hadli@ti.com> References: <1305900721-3890-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DD684BE.7010600@mvista.com> Hello. Manjunath Hadli wrote: > move the register base addresses and offsets used only by dm646x > platform file from platform header dm646x.h to dm646x.c as they > are used only in the c file. > Signed-off-by: Manjunath Hadli > --- > arch/arm/mach-davinci/dm646x.c | 34 +++++++++++++++----------- > arch/arm/mach-davinci/include/mach/dm646x.h | 7 ----- > 2 files changed, 20 insertions(+), 21 deletions(-) > diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c > index 1e0f809..9efa380 100644 > --- a/arch/arm/mach-davinci/dm646x.c > +++ b/arch/arm/mach-davinci/dm646x.c > @@ -30,20 +30,6 @@ > #include "clock.h" > #include "mux.h" > > -#define DAVINCI_VPIF_BASE (0x01C12000) > -#define VDD3P3V_PWDN_OFFSET (0x48) > -#define VSCLKDIS_OFFSET (0x6C) > - > -#define VDD3P3V_VID_MASK (BIT_MASK(3) | BIT_MASK(2) | BIT_MASK(1) |\ > - BIT_MASK(0)) > -#define VSCLKDIS_MASK (BIT_MASK(11) | BIT_MASK(10) | BIT_MASK(9) |\ > - BIT_MASK(8)) > - > -/* > - * Device specific clocks > - */ > -#define DM646X_AUX_FREQ 24000000 > - > static struct pll_data pll1_data = { > .num = 1, > .phys_base = DAVINCI_PLL1_BASE, > @@ -58,6 +44,8 @@ static struct clk ref_clk = { > .name = "ref_clk", > }; > > +#define DM646X_AUX_FREQ 24000000 > + Why move it (and delete the comment)? > static struct clk aux_clkin = { > .name = "aux_clkin", > .rate = DM646X_AUX_FREQ, > @@ -354,6 +342,11 @@ static struct clk_lookup dm646x_clks[] = { > CLK(NULL, NULL, NULL), > }; > > +#define DM646X_EMAC_CNTRL_OFFSET 0x0000 > +#define DM646X_EMAC_CNTRL_MOD_OFFSET 0x1000 > +#define DM646X_EMAC_CNTRL_RAM_OFFSET 0x2000 > +#define DM646X_EMAC_CNTRL_RAM_SIZE 0x2000 > + > static struct emac_platform_data dm646x_emac_pdata = { > .ctrl_reg_offset = DM646X_EMAC_CNTRL_OFFSET, > .ctrl_mod_reg_offset = DM646X_EMAC_CNTRL_MOD_OFFSET, > @@ -362,6 +355,8 @@ static struct emac_platform_data dm646x_emac_pdata = { > .version = EMAC_VERSION_2, > }; > > +#define DM646X_EMAC_BASE 0x01C80000 > + > static struct resource dm646x_emac_resources[] = { > { > .start = DM646X_EMAC_BASE, > @@ -400,6 +395,8 @@ static struct platform_device dm646x_emac_device = { > .resource = dm646x_emac_resources, > }; > > +#define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) > + > static struct resource dm646x_mdio_resources[] = { > { > .start = DM646X_EMAC_MDIO_BASE, > @@ -671,6 +668,8 @@ static struct platform_device dm646x_dit_device = { > > static u64 vpif_dma_mask = DMA_BIT_MASK(32); > > +#define DAVINCI_VPIF_BASE 0x01C12000 > + > static struct resource vpif_resource[] = { > { > .start = DAVINCI_VPIF_BASE, I'd prefer that these macros are still grouped together at the start of file... > @@ -866,6 +865,13 @@ void __init dm646x_init_mcasp1(struct snd_platform_data *pdata) > platform_device_register(&dm646x_dit_device); > } > > +#define VDD3P3V_PWDN_OFFSET 0x48 > +#define VSCLKDIS_OFFSET 0x6C > +#define VDD3P3V_VID_MASK (BIT_MASK(3) | BIT_MASK(2) | BIT_MASK(1) |\ > + BIT_MASK(0)) > +#define VSCLKDIS_MASK (BIT_MASK(11) | BIT_MASK(10) | BIT_MASK(9) |\ > + BIT_MASK(8)) I'd prefer that these remain on their places too. Let's wait for Sekhar's word though... > + > void dm646x_setup_vpif(struct vpif_display_config *display_config, > struct vpif_capture_config *capture_config) > { WBR, Sergei From sshtylyov at mvista.com Fri May 20 10:18:35 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 20 May 2011 19:18:35 +0400 Subject: [PATCH 4/4] davinci: create new common platform header for davinci In-Reply-To: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> References: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DD6864B.6040305@mvista.com> Hello. Manjunath Hadli wrote: > remove the code from individual platform header files for > dm365, dm355, dm644x and dm646x and consolidate it into a > single and common header file dmx.h. Could we call it davinci.h instead? > Include the new header file in individual platform header > files as a pre-cursor for deleting these headers in follow > up patches. > Signed-off-by: Manjunath Hadli [...] > diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c > index c67f684..7be75b2 100644 > --- a/arch/arm/mach-davinci/board-dm365-evm.c > +++ b/arch/arm/mach-davinci/board-dm365-evm.c [...] > @@ -143,6 +143,10 @@ static struct davinci_nand_pdata davinci_nand_data = { > .ecc_bits = 4, > }; > > +#define DM365_ASYNC_EMIF_CONTROL_BASE 0x01D10000 > +#define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 > +#define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 No, we don't want to duplicate those in every future DM365 board file. Leave them in the header please. > diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c > index f6ac9ba..73497a6 100644 > --- a/arch/arm/mach-davinci/board-dm646x-evm.c > +++ b/arch/arm/mach-davinci/board-dm646x-evm.c > @@ -91,6 +91,9 @@ static struct davinci_nand_pdata davinci_nand_data = { > .options = 0, > }; > > +#define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 > +#define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 > + No, we don't want to duplicate those in every future DM646x board file. Leave them in the header please. > diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h > index fa50b6b..cafa762 100644 > --- a/drivers/media/video/davinci/vpif_capture.h > +++ b/drivers/media/video/davinci/vpif_capture.h > @@ -28,6 +28,7 @@ > #include > #include > #include > +#include Why, if it didn't #include any of the deleted headers before? WBR, Sergei From sshtylyov at mvista.com Fri May 20 10:35:23 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 20 May 2011 19:35:23 +0400 Subject: [PATCH 4/4] davinci: create new common platform header for davinci In-Reply-To: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> References: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DD68A3B.7040800@mvista.com> Hello. Manjunath Hadli wrote: > remove the code from individual platform header files for > dm365, dm355, dm644x and dm646x and consolidate it into a > single and common header file dmx.h. > Include the new header file in individual platform header > files as a pre-cursor for deleting these headers in follow > up patches. > Signed-off-by: Manjunath Hadli > --- > arch/arm/mach-davinci/board-dm355-evm.c | 2 +- > arch/arm/mach-davinci/board-dm355-leopard.c | 2 +- > arch/arm/mach-davinci/board-dm365-evm.c | 6 ++- > arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- > arch/arm/mach-davinci/board-dm646x-evm.c | 5 ++- > arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- > arch/arm/mach-davinci/board-sffsdr.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/dm355.h | 32 ----------- > arch/arm/mach-davinci/include/mach/dm365.h | 40 -------------- > arch/arm/mach-davinci/include/mach/dm644x.h | 40 -------------- > arch/arm/mach-davinci/include/mach/dmx.h | 77 +++++++++++++++++++++++++++ > drivers/media/video/davinci/vpif.h | 2 +- > drivers/media/video/davinci/vpif_capture.h | 1 + > drivers/media/video/davinci/vpif_display.c | 2 +- > 18 files changed, 98 insertions(+), 125 deletions(-) > delete mode 100644 arch/arm/mach-davinci/include/mach/dm355.h > delete mode 100644 arch/arm/mach-davinci/include/mach/dm365.h > delete mode 100644 arch/arm/mach-davinci/include/mach/dm644x.h > create mode 100644 arch/arm/mach-davinci/include/mach/dmx.h BTW, you forgot to remove ... WBR, Sergei From arnd at arndb.de Fri May 20 12:27:22 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Fri, 20 May 2011 19:27:22 +0200 Subject: [PATCH 4/4] davinci: create new common platform header for davinci In-Reply-To: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> References: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201105201927.22386.arnd@arndb.de> On Friday 20 May 2011 16:14:01 Manjunath Hadli wrote: > remove the code from individual platform header files for > dm365, dm355, dm644x and dm646x and consolidate it into a > single and common header file dmx.h. > Include the new header file in individual platform header > files as a pre-cursor for deleting these headers in follow > up patches. > > Signed-off-by: Manjunath Hadli I probably missed the discussion that led to this patch, but please explain the motivation behind the change in the changelog. Your current changelog reads like this is pointless churn, which may or may not be the case. The diffstat shows 27 lines being removed in total, which is roughly the amount of copyright headers. Arnd From nsekhar at ti.com Fri May 20 12:33:52 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 20 May 2011 23:03:52 +0530 Subject: new tree for TI DaVinci support Message-ID: Hello, Going forward I will be using the following tree to track upstream development. git://gitorious.org/linux-davinci/linux-davinci.git This tree will be maintained in a manner similar to how Kevin used to maintain his tree so the switch should be relatively painless. The davinci-next branch of this tree is already being included in linux-next project. Thanks, Sekhar From nsekhar at ti.com Fri May 20 13:14:10 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 20 May 2011 23:44:10 +0530 Subject: [PATCH 4/4] davinci: create new common platform header for davinci In-Reply-To: <201105201927.22386.arnd@arndb.de> References: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> <201105201927.22386.arnd@arndb.de> Message-ID: Hi Arnd, On Fri, May 20, 2011 at 22:57:22, Arnd Bergmann wrote: > On Friday 20 May 2011 16:14:01 Manjunath Hadli wrote: > > remove the code from individual platform header files for > > dm365, dm355, dm644x and dm646x and consolidate it into a > > single and common header file dmx.h. > > Include the new header file in individual platform header > > files as a pre-cursor for deleting these headers in follow > > up patches. > > > > Signed-off-by: Manjunath Hadli > > I probably missed the discussion that led to this patch, but > please explain the motivation behind the change in the changelog. > > Your current changelog reads like this is pointless churn, > which may or may not be the case. The diffstat shows 27 lines > being removed in total, which is roughly the amount of copyright > headers. Well, it started with the need to get rid of using IO_ADDRESS() on "system module" which is a bunch of SoC specific registers found on dm365, dm355, dm644x and dm646x SoCs (all at the same base address, but having different SoC specific register content). Please see the previous discussion here: http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2011-April/022523.html We could have probably avoided the churn by sticking DAVINCI_SYSMODULE_VIRT() into yet another (unrelated or new) header file, but it made sense to have a single header file containing platform private stuff for all these SoCs. Thanks, Sekhar From arnd at arndb.de Fri May 20 14:46:32 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Fri, 20 May 2011 21:46:32 +0200 Subject: [PATCH 4/4] davinci: create new common platform header for davinci In-Reply-To: References: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> <201105201927.22386.arnd@arndb.de> Message-ID: <201105202146.32233.arnd@arndb.de> On Friday 20 May 2011 20:14:10 Nori, Sekhar wrote: > Well, it started with the need to get rid of using IO_ADDRESS() > on "system module" which is a bunch of SoC specific registers > found on dm365, dm355, dm644x and dm646x SoCs (all at the same > base address, but having different SoC specific register content). That part certainly sounds useful, but I still don't understand how it relates to this patch here, especially since you are not actually adding the davinci_sysmodbase. Your other three patches also look good to me. > Please see the previous discussion here: > > http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2011-April/022523.html > > We could have probably avoided the churn by sticking DAVINCI_SYSMODULE_VIRT() > into yet another (unrelated or new) header file, but it made sense to have a > single header file containing platform private stuff for all these SoCs. In the long run, a more valuable goal would be to reduce the number of definitions in the include/mach/ directories, and since most of the stuff in the files you touch is just the interface between the soc and board files, I would recommend making them local to the directory that holds the users, i.e. arch/arm/mach-davinci/dm*.h. Anything that still needs to be exported to drivers can either stay in arch/arm/mach-davinci/include/mach/davinci.h or be moved out at a later point. I also feel that a global pointer point to different registers depending on what SoC you run on is a rather poor interface. How about making the pointer local to mach-davinci/devices.c and just exporting high-level functions to other parts of the kernel that need access to it? Arnd From linux at arm.linux.org.uk Fri May 20 17:05:10 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 20 May 2011 23:05:10 +0100 Subject: [PATCH] Fix generic irq chip Message-ID: <20110520220510.GC30675@n2100.arm.linux.org.uk> As a result of c42321c (genirq: Make generic irq chip depend on CONFIG_GENERIC_IRQ_CHIP), we now need those platforms using this in my tree to select this symbol. Signed-off-by: Russell King -- Please send acks ASAP. Thanks to Mark for pointing this out with a patch for Samsung. arch/arm/Kconfig | 2 ++ arch/arm/plat-omap/Kconfig | 1 + 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 44c16f0..9b63bab 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -854,6 +854,7 @@ config ARCH_DAVINCI select HAVE_IDE select CLKDEV_LOOKUP select GENERIC_ALLOCATOR + select GENERIC_IRQ_CHIP select ARCH_HAS_HOLES_MEMORYMODEL help Support for TI's DaVinci platform. @@ -1033,6 +1034,7 @@ config PLAT_IOP config PLAT_ORION bool + select GENERIC_IRQ_CHIP select HAVE_SCHED_CLOCK config PLAT_PXA diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index cd5f993..29b4c35 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -18,6 +18,7 @@ config ARCH_OMAP1 config ARCH_OMAP2PLUS bool "TI OMAP2/3/4" select CLKDEV_LOOKUP + select GENERIC_IRQ_CHIP select OMAP_DM_TIMER help "Systems based on OMAP2, OMAP3 or OMAP4" From arnd at arndb.de Sun May 22 05:30:56 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Sun, 22 May 2011 12:30:56 +0200 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <4DCBF1B6.6000104@hartkopp.net> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> <4DCB88A4.2010901@grandegger.com> <4DCBF1B6.6000104@hartkopp.net> Message-ID: <201105221230.56243.arnd@arndb.de> On Thursday 12 May 2011 16:41:58 Oliver Hartkopp wrote: > E.g. assume you need the CAN-IDs 0x100, 0x200 and 0x300 in your application > and for that reason you configure these IDs in the pruss CAN driver. > > What if someone generates a 100% CAN busload exactly on CAN-ID 0x100 then? > > Worst case (1MBit/s, DLC=0) you would need to handle about 21.000 irqs/s for > the correctly received CAN frames with the filtered CAN-ID 0x100 ... Then I guess the main thing that a "smart" CAN implementation like pruss should do is interrupt mitigation. When you have a constant flow of packets coming in, the hardware should be able to DMA a lot of them into kernel memory before the driver is required to pick them up, and only get into interrupt driven mode when the kernel has managed to process all outstanding packets. > This all depends heavily on Linux networking (skb handling, caching, etc) and > is pretty fast and optimized!! That was also the reason why it ran on the old > PowerPC that smoothly. The mostly seen effect if anything drops is when the > application (holding the socket) was not fast enough to handle the incoming > data. NB: For that reason we implemented a CAN content filter (CAN_BCM) that > is able to do content filtering and timeout monitoring in Kernelspace - all > performed in the SoftIRQ. Right, dropping packets that no process is waiting for should be done as early as possible. In pruss-can, the idea was to do it in hardware, which doesn't really work all that well for the reasons discussed before. Dropping the frames in the NAPI poll function (softirq time) seems like a logical choice. > Having 'Mailboxes' bound to CAN-IDs is something that's useful for 8/16 bit > CPUs where an application is tightly bound to the embedded ECUs functionality. Makes sense. Arnd From sameo at linux.intel.com Sun May 22 15:21:31 2011 From: sameo at linux.intel.com (Samuel Ortiz) Date: Sun, 22 May 2011 22:21:31 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105112203.54838.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105102344.58598.arnd@arndb.de> <82D549BB195345A1A189D569952B387C@subhasishg> <201105112203.54838.arnd@arndb.de> Message-ID: <20110522202129.GE18610@sortiz-mobl> Hi Arnd, On Wed, May 11, 2011 at 10:03:54PM +0200, Arnd Bergmann wrote: > On Wednesday 11 May 2011, Subhasish Ghosh wrote: > > > Please look into implementing one of the three I suggested before > > > you go off in another direction. In case of the third one, the idea > > > was to configure the name of the device for each pru using sysfs, > > > which then gets bound to the driver, which loads its own firmware > > > as you do today. Only in the first two suggestions, the mfd driver > > > would be responsible for loading the firmware. > > > > Ok, thanks for the clarification. > > Instead of passing the device name, will it be ok to pass the mfd_id. > > The benefit will be that I can use the ID directly as an array > > index for the mfd_cell entries. > > I think a device name would be clearer here, especially in order > to avoid conflicts when the list gets extended in different ways > depending on which kernel runs. > > We had a little discussion at the Linaro Developer Summit about your > driver and mfd drivers in general. There was a general feeling among > some people (including me) that by the point you dynamically create > the subdevices, MFD is probably not the right abstraction any more, > as it does not provide any service that you need. I agree it's not what it's been designed for. > Instead, maybe you can simply call platform_device_register > at that stage to create the children and not use MFD at all. The MFD APIs are slightly easier to use though, imho. > Samuel, can you comment on this as well? Do you still see pruss > as an MFD driver when the uses are completely dynamic and determined > by the firmware loaded into it? Even though that is definitely not a typical MFD use case, I wouldn't object strongly against it. Right now mfd is probably the least worst choice for this kind of drivers, which still doesn't make it an ideal situation. Cheers, Samuel. -- Intel Open Source Technology Centre http://oss.intel.com/ From sameo at linux.intel.com Sun May 22 15:24:06 2011 From: sameo at linux.intel.com (Samuel Ortiz) Date: Sun, 22 May 2011 22:24:06 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105142233.53659.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <20110514160126.GA2791@opensource.wolfsonmicro.com> <201105142233.53659.arnd@arndb.de> Message-ID: <20110522202405.GF18610@sortiz-mobl> Hi Arnd, On Sat, May 14, 2011 at 10:33:53PM +0200, Arnd Bergmann wrote: > On Saturday 14 May 2011, Mark Brown wrote: > > > Will a misc device be a better choice. > > > > > In that case I can remove the MFD cell from the board_file and add an array > > > of platform_device for the UART & CAN. > > > > I think that for me so long as it can simultaneously do two unrelated > > functions which need to be arbitrated between there's probably a place > > for it in MFD, certainly at the minute. > > I guess drivers/mfd would be a better place than drivers/misc, but it might not > need to be an mfd driver in the sense that it registers mfd cells. > I agree with that. Although if it makes it to drivers/mfd/, I'd prefer to see it using the MF API. > The more important point is to remove the device registration from the board > file. You definitely should not have the cells in the board file, but replacing > them with platform devices in the board file makes it no better. > > My whole point has been that you register them from the main pruss driver > based on run-time data instead of compile-time pre-configured stuff in the > board file. That's certainly right. Cheers, Samuel. -- Intel Open Source Technology Centre http://oss.intel.com/ From nsekhar at ti.com Mon May 23 03:15:05 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 23 May 2011 13:45:05 +0530 Subject: [PATCH 4/4] davinci: create new common platform header for davinci In-Reply-To: <201105202146.32233.arnd@arndb.de> References: <1305900841-4020-1-git-send-email-manjunath.hadli@ti.com> <201105201927.22386.arnd@arndb.de> <201105202146.32233.arnd@arndb.de> Message-ID: On Sat, May 21, 2011 at 01:16:32, Arnd Bergmann wrote: > On Friday 20 May 2011 20:14:10 Nori, Sekhar wrote: > > Well, it started with the need to get rid of using IO_ADDRESS() > > on "system module" which is a bunch of SoC specific registers > > found on dm365, dm355, dm644x and dm646x SoCs (all at the same > > base address, but having different SoC specific register content). > > That part certainly sounds useful, but I still don't understand how > it relates to this patch here, especially since you are not actually > adding the davinci_sysmodbase. The idea was to add davinci_sysmodbase to arch/arm/mach- davinci/include/mach/dmx.h created by this patch. That should probably be added to this series to make it look more meaningful. > Your other three patches also look good to me. Thanks (on behalf of Manju). > > Please see the previous discussion here: > > > > http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2011-April/022523.html > > > > We could have probably avoided the churn by sticking DAVINCI_SYSMODULE_VIRT() > > into yet another (unrelated or new) header file, but it made sense to have a > > single header file containing platform private stuff for all these SoCs. > > In the long run, a more valuable goal would be to reduce the number of > definitions in the include/mach/ directories, and since most of the > stuff in the files you touch is just the interface between the soc > and board files, I would recommend making them local to the directory > that holds the users, i.e. arch/arm/mach-davinci/dm*.h. Anything that > still needs to be exported to drivers can either stay in > arch/arm/mach-davinci/include/mach/davinci.h or be moved out at > a later point. Right, good point. > I also feel that a global pointer point to different registers depending > on what SoC you run on is a rather poor interface. How about making the > pointer local to mach-davinci/devices.c and just exporting high-level > functions to other parts of the kernel that need access to it? Agreed, this should be explored. The main challenge will be in coming up high level functions usable on more than one SoC (because of the highly SoC specific nature of this functionality). Thanks, Sekhar From nsekhar at ti.com Mon May 23 03:28:26 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 23 May 2011 13:58:26 +0530 Subject: [PATCH v17 5/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <4DD67B68.7070704@mvista.com> References: <1305899324-2118-1-git-send-email-manjunath.hadli@ti.com> <4DD67B68.7070704@mvista.com> Message-ID: On Fri, May 20, 2011 at 20:02:08, Sergei Shtylyov wrote: > Hello. > > Manjunath Hadli wrote: > > > 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 > [...] > > > 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" > > BTW, as this seems DM644x specific, shouldn't this depend on > CONFIG_ARCH_DAVINCI_DM644x? Since VENC/OSD etc are also applicable to other DaVinci devices, this KConfig entry should probably be split to refer to them individually and in a generic way. "depends on" can then be used to make sure only the relevant ones show up. Thanks, Sekhar From nsekhar at ti.com Mon May 23 04:53:09 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 23 May 2011 15:23:09 +0530 Subject: [PATCH] Fix generic irq chip In-Reply-To: <20110520220510.GC30675@n2100.arm.linux.org.uk> References: <20110520220510.GC30675@n2100.arm.linux.org.uk> Message-ID: On Sat, May 21, 2011 at 03:35:10, Russell King - ARM Linux wrote: > As a result of c42321c (genirq: Make generic irq chip depend on > CONFIG_GENERIC_IRQ_CHIP), we now need those platforms using this in > my tree to select this symbol. > > Signed-off-by: Russell King > -- > Please send acks ASAP. Thanks to Mark for pointing this out with a patch > for Samsung. Acked-by: Sekhar Nori Thanks, Sekhar From nsekhar at ti.com Mon May 23 08:34:28 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 23 May 2011 19:04:28 +0530 Subject: [PATCH 3/4] davinci: dm646x: remove the macros from the header to move to c file In-Reply-To: <20110520142401.GV15292@game.jcrosoft.org> References: <1305900721-3890-1-git-send-email-manjunath.hadli@ti.com> <20110520142401.GV15292@game.jcrosoft.org> Message-ID: Hi Jean-Christophe, On Fri, May 20, 2011 at 19:54:01, Jean-Christophe PLAGNIOL-VILLARD wrote: > On 19:42 Fri 20 May , Manjunath Hadli wrote: > > move the register base addresses and offsets used only by dm646x > > platform file from platform header dm646x.h to dm646x.c as they > > are used only in the c file. > > > > Signed-off-by: Manjunath Hadli > I do not think it's a good idea to put the register mapping in the c file > evenif they are used only today in dm646x > Unless a definition is used across C files, it would be better to keep it local to the C file. Why treat base addresses and register offsets differently? Thanks, Sekhar From khilman at ti.com Mon May 23 08:48:41 2011 From: khilman at ti.com (Kevin Hilman) Date: Mon, 23 May 2011 06:48:41 -0700 Subject: [PATCH] Fix generic irq chip In-Reply-To: <20110520220510.GC30675@n2100.arm.linux.org.uk> (Russell King's message of "Fri, 20 May 2011 23:05:10 +0100") References: <20110520220510.GC30675@n2100.arm.linux.org.uk> Message-ID: <87lixx34ue.fsf@ti.com> Russell King - ARM Linux writes: > As a result of c42321c (genirq: Make generic irq chip depend on > CONFIG_GENERIC_IRQ_CHIP), we now need those platforms using this in > my tree to select this symbol. > > Signed-off-by: Russell King > -- > Please send acks ASAP. Thanks to Mark for pointing this out with a patch > for Samsung. Acked-by: Kevin Hilman From nsekhar at ti.com Mon May 23 09:17:23 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 23 May 2011 19:47:23 +0530 Subject: [PATCH 3/4] davinci: dm646x: remove the macros from the header to move to c file In-Reply-To: <4DD684BE.7010600@mvista.com> References: <1305900721-3890-1-git-send-email-manjunath.hadli@ti.com> <4DD684BE.7010600@mvista.com> Message-ID: On Fri, May 20, 2011 at 20:41:58, Sergei Shtylyov wrote: > > diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c > > @@ -866,6 +865,13 @@ void __init dm646x_init_mcasp1(struct snd_platform_data *pdata) > > platform_device_register(&dm646x_dit_device); > > } > > > > +#define VDD3P3V_PWDN_OFFSET 0x48 > > +#define VSCLKDIS_OFFSET 0x6C > > +#define VDD3P3V_VID_MASK (BIT_MASK(3) | BIT_MASK(2) | BIT_MASK(1) |\ > > + BIT_MASK(0)) > > +#define VSCLKDIS_MASK (BIT_MASK(11) | BIT_MASK(10) | BIT_MASK(9) |\ > > + BIT_MASK(8)) > > I'd prefer that these remain on their places too. Let's wait for Sekhar's > word though... I think there is some personal coding style involved here and some folks prefer to keep the definitions near the actual usage so that you don't have to scroll two or three pages up just to see what the macro is defined to. However, consistency is important too and almost all across mach-davinci I see the defines (including base address definitions) grouped at the top of the file. So, I will go with what Sergei is saying here. Thanks, Sekhar From linux at arm.linux.org.uk Mon May 23 10:01:20 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Mon, 23 May 2011 16:01:20 +0100 Subject: [PATCH] Fix generic irq chip In-Reply-To: <87lixx34ue.fsf@ti.com> References: <20110520220510.GC30675@n2100.arm.linux.org.uk> <87lixx34ue.fsf@ti.com> Message-ID: <20110523150120.GA15153@n2100.arm.linux.org.uk> On Mon, May 23, 2011 at 06:48:41AM -0700, Kevin Hilman wrote: > Russell King - ARM Linux writes: > > > As a result of c42321c (genirq: Make generic irq chip depend on > > CONFIG_GENERIC_IRQ_CHIP), we now need those platforms using this in > > my tree to select this symbol. > > > > Signed-off-by: Russell King > > -- > > Please send acks ASAP. Thanks to Mark for pointing this out with a patch > > for Samsung. > > Acked-by: Kevin Hilman Thanks, but does that cover Davinci or OMAP? From arnd at arndb.de Mon May 23 10:13:48 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Mon, 23 May 2011 17:13:48 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <20110522202129.GE18610@sortiz-mobl> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105112203.54838.arnd@arndb.de> <20110522202129.GE18610@sortiz-mobl> Message-ID: <201105231713.49067.arnd@arndb.de> Hi Samuel, On Sunday 22 May 2011, Samuel Ortiz wrote: > On Wed, May 11, 2011 at 10:03:54PM +0200, Arnd Bergmann wrote: > > On Wednesday 11 May 2011, Subhasish Ghosh wrote: > > We had a little discussion at the Linaro Developer Summit about your > > driver and mfd drivers in general. There was a general feeling among > > some people (including me) that by the point you dynamically create > > the subdevices, MFD is probably not the right abstraction any more, > > as it does not provide any service that you need. > I agree it's not what it's been designed for. > > > > Instead, maybe you can simply call platform_device_register > > at that stage to create the children and not use MFD at all. > The MFD APIs are slightly easier to use though, imho. > > > > Samuel, can you comment on this as well? Do you still see pruss > > as an MFD driver when the uses are completely dynamic and determined > > by the firmware loaded into it? > Even though that is definitely not a typical MFD use case, I wouldn't object > strongly against it. Right now mfd is probably the least worst choice for this > kind of drivers, which still doesn't make it an ideal situation. Thanks for your input! When you say that MFD APIs are easier to use than platform_device APIs, is that something we should fix with platform devices to make them easier to use? Arnd From khilman at ti.com Mon May 23 10:20:03 2011 From: khilman at ti.com (Kevin Hilman) Date: Mon, 23 May 2011 08:20:03 -0700 Subject: [PATCH] Fix generic irq chip In-Reply-To: <20110523150120.GA15153@n2100.arm.linux.org.uk> (Russell King's message of "Mon, 23 May 2011 16:01:20 +0100") References: <20110520220510.GC30675@n2100.arm.linux.org.uk> <87lixx34ue.fsf@ti.com> <20110523150120.GA15153@n2100.arm.linux.org.uk> Message-ID: <877h9h30m4.fsf@ti.com> Russell King - ARM Linux writes: > On Mon, May 23, 2011 at 06:48:41AM -0700, Kevin Hilman wrote: >> Russell King - ARM Linux writes: >> >> > As a result of c42321c (genirq: Make generic irq chip depend on >> > CONFIG_GENERIC_IRQ_CHIP), we now need those platforms using this in >> > my tree to select this symbol. >> > >> > Signed-off-by: Russell King >> > -- >> > Please send acks ASAP. Thanks to Mark for pointing this out with a patch >> > for Samsung. >> >> Acked-by: Kevin Hilman > > Thanks, but does that cover Davinci or OMAP? Both, but since Sekhar (DaVinci co-maintainer) ack'd already for davinci, I was mainly adding my ack for OMAP. Kevin From arnd at arndb.de Mon May 23 10:30:06 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Mon, 23 May 2011 17:30:06 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105151133.23870.arnd@arndb.de> Message-ID: <201105231730.06564.arnd@arndb.de> On Monday 16 May 2011, Subhasish Ghosh wrote: > I earlier had an implementation where I used a pruss_devices structure > in the board file. > > http://linux.omap.com/pipermail/davinci-linux-open-source/ > 2011-March/022339.html. > > We can use this implementation along with the sysfs to load the devices > runtime. Possibly, but the actual data structures might end up differently when they are built around a sysfs interface. If you have a sysfs interface, it is more important to have that in a clean way than the board file, so we should first discuss the set of sysfs attributes that you are going to need, and then see how to represent that in platform data for predefined boards. > The configs that I have in the board_file for the devices > structure, are fixed for a board. To swap the boards, we do not need to re-compile > the kernel. The other point to consider is that we are definitely moving towards the flattened device tree for these definitions now. It's probably good to make the sysfs attributes directly correspond to fdt device properties. I'm not sure if we also need to allow platform data. The easiest way could be to just require the use of device tree for predefined pruss devices. I'm sorry that this is moving in a different direction now, you had an unfortunate timing here. Let's first discuss the exact properties that are really required to define the differences between PRU backends, as those will be required in any case. What do you need for PRU specific data besides the firmware and the name of the device? Arnd From nsekhar at ti.com Mon May 23 11:38:20 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 23 May 2011 22:08:20 +0530 Subject: [PATCH v18 07/13] davinci: move DM64XX_VDD3P3V_PWDN to devices.c In-Reply-To: <1301737380-4288-1-git-send-email-manjunath.hadli@ti.com> References: <1301737380-4288-1-git-send-email-manjunath.hadli@ti.com> Message-ID: On Sat, Apr 02, 2011 at 15:13:00, Hadli, Manjunath wrote: > 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 Applying this after updating the patch description to point out that this also helps rid hardware.h of platform private stuff.. > --- > 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 .. and moving this to the top of the file. Thanks, Sekhar From nsekhar at ti.com Mon May 23 12:17:53 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 23 May 2011 22:47:53 +0530 Subject: [PATCH 1/1] davinci: dm646x: move vpif related code to driver core header from platform In-Reply-To: <1305899929-2509-1-git-send-email-manjunath.hadli@ti.com> References: <1305899929-2509-1-git-send-email-manjunath.hadli@ti.com> Message-ID: On Fri, May 20, 2011 at 19:28:49, Hadli, Manjunath wrote: > move vpif related code for capture and display drivers > from dm646x platform header file to vpif.h as these definitions > are related to driver code more than the platform or board. > > Signed-off-by: Manjunath Hadli > diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h > index 10550bd..e76dded 100644 > --- a/drivers/media/video/davinci/vpif.h > +++ b/drivers/media/video/davinci/vpif.h > @@ -20,6 +20,7 @@ > #include > #include > #include > +#include mach/hardware.h and mach/dm646x.h can now be dropped. Thanks, Sekhar From nsekhar at ti.com Tue May 24 05:21:53 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 24 May 2011 15:51:53 +0530 Subject: [GIT PULL] Pull request for 2.6.40 Message-ID: Hi Russell, I have been tracking these four patches for inclusion in 2.6.40 can you please pull them? These are clean-up rather than consolidation patches and all of them have zero or negative net diffstat. Thanks, Sekhar The following changes since commit 0ee5623f9a6e52df90a78bd21179f8ab370e102e: Linus Torvalds (1): Linux 2.6.39-rc6 are available in the git repository at: git://gitorious.org/linux-davinci/linux-davinci.git davinci-next Manjunath Hadli (1): davinci: move DM64XX_VDD3P3V_PWDN to devices.c Sergei Shtylyov (3): DA8xx: kill duplicate #define DA8XX_GPIO_BASE DA8xx: kill duplicate #define DA8XX_PLL1_BASE DA8xx: move base address #define's to their proper place arch/arm/mach-davinci/da850.c | 2 +- arch/arm/mach-davinci/devices-da8xx.c | 16 +++++++++------- arch/arm/mach-davinci/devices.c | 3 +++ arch/arm/mach-davinci/include/mach/da8xx.h | 4 ---- arch/arm/mach-davinci/include/mach/hardware.h | 3 --- 5 files changed, 13 insertions(+), 15 deletions(-) From nsekhar at ti.com Tue May 24 05:54:13 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 24 May 2011 16:24:13 +0530 Subject: [PATCH 3/4] davinci: dm646x: remove the macros from the header to move to c file In-Reply-To: <20110524053220.GD863@game.jcrosoft.org> References: <1305900721-3890-1-git-send-email-manjunath.hadli@ti.com> <20110520142401.GV15292@game.jcrosoft.org> <20110524053220.GD863@game.jcrosoft.org> Message-ID: On Tue, May 24, 2011 at 11:02:20, Jean-Christophe PLAGNIOL-VILLARD wrote: > On 19:04 Mon 23 May , Nori, Sekhar wrote: > > > > Hi Jean-Christophe, > > > > On Fri, May 20, 2011 at 19:54:01, Jean-Christophe PLAGNIOL-VILLARD wrote: > > > On 19:42 Fri 20 May , Manjunath Hadli wrote: > > > > move the register base addresses and offsets used only by dm646x > > > > platform file from platform header dm646x.h to dm646x.c as they > > > > are used only in the c file. > > > > > > > > Signed-off-by: Manjunath Hadli > > > I do not think it's a good idea to put the register mapping in the c file > > > evenif they are used only today in dm646x > > > > > > > Unless a definition is used across C files, it would be better to > > keep it local to the C file. Why treat base addresses and register > > offsets differently? > no difference the macro are nearly every time put in header not in C code I don't think there is a preference of this sort. There are numerous instances in kernel where macros are defined in C files. Thanks, Sekhar From christian.riesch at omicron.at Tue May 24 06:31:28 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Tue, 24 May 2011 13:31:28 +0200 Subject: DA850 board with 25 MHz oscillator frequency Message-ID: Hi, I am currently developing a custom board based on the AM1808 SoC. On this board the SoC is driven by a 25 MHz oscillator. For the board I created a board configuration file, e.g., arch/arm/mach-davinci/board-xyz.c However, the SoC specific file arch/arm/mach-davinci/da850.c assumes that the input clock frequency is 24MHz (#define DA850_REF_FREQ 24000000). What would be the correct way to change this clock frequency? Using clk_set_rate() from the init function in my board-xyz.c file? Regards, Christian From subhasish at mistralsolutions.com Tue May 24 07:17:32 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 24 May 2011 17:47:32 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <201105231730.06564.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105151133.23870.arnd@arndb.de> <201105231730.06564.arnd@arndb.de> Message-ID: <7E10264BABCB4C1788DCD9715461BDE8@subhasishg> Hi Arnd, >> http://linux.omap.com/pipermail/davinci-linux-open-source/ >> 2011-March/022339.html. >> >> We can use this implementation along with the sysfs to load the devices >> runtime. > > Possibly, but the actual data structures might end up differently > when they are built around a sysfs interface. If you have a sysfs > interface, it is more important to have that in a clean way than > the board file, so we should first discuss the set of sysfs > attributes that you are going to need, and then see how to > represent that in platform data for predefined boards. > >> The configs that I have in the board_file for the devices >> structure, are fixed for a board. To swap the boards, we do not need to >> re-compile >> the kernel. > > The other point to consider is that we are definitely moving > towards the flattened device tree for these definitions now. > It's probably good to make the sysfs attributes directly correspond > to fdt device properties. I'm not sure if we also need to allow platform > data. The easiest way could be to just require the use of device tree > for predefined pruss devices. > > I'm sorry that this is moving in a different direction now, you > had an unfortunate timing here. > > Let's first discuss the exact properties that are really required > to define the differences between PRU backends, as those will > be required in any case. What do you need for PRU specific > data besides the firmware and the name of the device? Would it be a good approach to first get a basic sensible driver into the tree and then work towards improving and adjusting for future compatibilities. That way we can gradually build towards the perfect driver in steps, rather than digressing far too off suddenly. That would be some source of motivation for me too. From arnd at arndb.de Tue May 24 07:40:34 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Tue, 24 May 2011 14:40:34 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <7E10264BABCB4C1788DCD9715461BDE8@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105231730.06564.arnd@arndb.de> <7E10264BABCB4C1788DCD9715461BDE8@subhasishg> Message-ID: <201105241440.34854.arnd@arndb.de> On Tuesday 24 May 2011, Subhasish Ghosh wrote: > Would it be a good approach to first get a basic sensible > driver into the tree and then work towards improving and > adjusting for future compatibilities. The main problem with that is that you cannot really change user-visible interfaces after the driver is merged. Anything regarding sysfs attributes and firmware file contents needs to be fixed. You can however still change in-kernel interfaces at a later point without problems. > That way we can gradually build towards the perfect driver > in steps, rather than digressing far too off suddenly. > That would be some source of motivation for me too. You can definitely add the driver to drivers/staging/pruss/, as the rules for staging drivers are different. I think that would indeed be helpful at this point. As soon as all interfaces have stabilized, you can then move the individual parts to their final location. Greg maintains the staging drivers, and he can surely point you to a document describing what is required for a driver. Mainly, this includes having a patch that adds a single directory with all the driver files under drivers/staging. The driver must be able to compile without errors and you need a TODO file listing the remaining issues that prevent you from having a non-staging driver. I think the list will be relatively short, given that you have made a lot of progress and the driver is now essentially ready, aside from a few details and the big question of how the firmware should be configured and loaded. Arnd From manjunath.hadli at ti.com Tue May 24 07:59:10 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 24 May 2011 18:29:10 +0530 Subject: [PATCH v17 5/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: Message-ID: On Mon, May 23, 2011 at 13:58:26, Nori, Sekhar wrote: > On Fri, May 20, 2011 at 20:02:08, Sergei Shtylyov wrote: > > Hello. > > > > Manjunath Hadli wrote: > > > > > 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 > > [...] > > > > > 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" > > > > BTW, as this seems DM644x specific, shouldn't this depend on > > CONFIG_ARCH_DAVINCI_DM644x? > > Since VENC/OSD etc are also applicable to other DaVinci devices, this KConfig entry should probably be split to refer to them individually and in a generic way. "depends on" can then be used to make sure only the relevant ones show up. Both venc and osd have to be used together always, so might not make a good idea to split. However, I will add a dependency on DM644x, and include others with appropriate patch sets. > > Thanks, > Sekhar > > From greg at kroah.com Tue May 24 08:43:38 2011 From: greg at kroah.com (Greg KH) Date: Tue, 24 May 2011 06:43:38 -0700 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <201105241440.34854.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105231730.06564.arnd@arndb.de> <7E10264BABCB4C1788DCD9715461BDE8@subhasishg> <201105241440.34854.arnd@arndb.de> Message-ID: <20110524134338.GA13491@kroah.com> On Tue, May 24, 2011 at 02:40:34PM +0200, Arnd Bergmann wrote: > On Tuesday 24 May 2011, Subhasish Ghosh wrote: > > Would it be a good approach to first get a basic sensible > > driver into the tree and then work towards improving and > > adjusting for future compatibilities. > > The main problem with that is that you cannot really change > user-visible interfaces after the driver is merged. Anything > regarding sysfs attributes and firmware file contents needs > to be fixed. You can however still change in-kernel interfaces > at a later point without problems. > > > That way we can gradually build towards the perfect driver > > in steps, rather than digressing far too off suddenly. > > That would be some source of motivation for me too. > > You can definitely add the driver to drivers/staging/pruss/, > as the rules for staging drivers are different. I think that > would indeed be helpful at this point. As soon as all > interfaces have stabilized, you can then move the individual > parts to their final location. > > Greg maintains the staging drivers, and he can surely point > you to a document describing what is required for a driver. There really are only 3 rules: - proper license - self-contained in a drivers/staging/DRIVER_NAME/ directory - must properly build > Mainly, this includes having a patch that adds a single > directory with all the driver files under drivers/staging. > The driver must be able to compile without errors and you need > a TODO file listing the remaining issues that prevent you > from having a non-staging driver. Ah, forgot the TODO on the list of rules, I'll have to add that next time. Someone feel free to send me a patch :) thanks, greg k-h From manjunath.hadli at ti.com Tue May 24 09:01:39 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Tue, 24 May 2011 19:31:39 +0530 Subject: [PATCH v18 0/6] davinci vpbe: dm6446 v4l2 driver Message-ID: <1306245699-3236-1-git-send-email-manjunath.hadli@ti.com> Fixed Sergei's comments for Kconfig dm644x dependencies Fixed Sekhar'c comment on indentation Manjunath Hadli (6): 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 Documentation/video4linux/README.davinci-vpbe | 93 ++ drivers/media/video/davinci/Kconfig | 23 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 864 ++++++++++++ drivers/media/video/davinci/vpbe_display.c | 1860 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1231 ++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ include/media/davinci/vpbe.h | 184 +++ include/media/davinci/vpbe_display.h | 147 ++ include/media/davinci/vpbe_osd.h | 394 ++++++ include/media/davinci/vpbe_types.h | 91 ++ include/media/davinci/vpbe_venc.h | 45 + 14 files changed, 6041 insertions(+), 0 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 delete mode 100644 drivers/staging/vme/bridges/Module.symvers 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 Tue May 24 09:01:54 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Tue, 24 May 2011 19:31:54 +0530 Subject: [PATCH v18 1/6] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1306245714-3293-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 | 1860 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 147 +++ include/media/davinci/vpbe_types.h | 91 ++ 3 files changed, 2098 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..7f1d83a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,1860 @@ +/* + * 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 "vpbe_venc_regs.h" + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +module_param(debug, int, 0644); + +static int venc_is_second_field(struct vpbe_display *disp_dev) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int ret; + 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; +} + +static void vpbe_isr_even_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct timespec timevalue; + + if (layer->cur_frm == layer->next_frm) + return; + ktime_get_ts(&timevalue); + layer->cur_frm->ts.tv_sec = timevalue.tv_sec; + layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC; + 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; +} + +static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct osd_state *osd_device = disp_obj->osd_device; + unsigned long addr; + + spin_lock(&disp_obj->dma_queue_lock); + if (list_empty(&layer->dma_queue) || + (layer->cur_frm != layer->next_frm)) { + spin_unlock(&disp_obj->dma_queue_lock); + return; + } + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + 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); + spin_unlock(&disp_obj->dma_queue_lock); + /* 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_obj->cbcr_ofst); +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + struct vpbe_display *disp_dev = (struct vpbe_display *)arg; + struct vpbe_layer *layer; + static unsigned last_event; + unsigned event = 0; + int fid; + int i; + + if ((NULL == arg) || (NULL == disp_dev->dev[0])) + return IRQ_HANDLED; + + if (venc_is_second_field(disp_dev)) + 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; + + 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; + + if (layer->layer_first_int) { + layer->layer_first_int = 0; + continue; + } + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & VENC_END_OF_FRAME)) { + /* Progressive mode */ + + vpbe_isr_even_field(disp_dev, layer); + vpbe_isr_odd_field(disp_dev, layer); + } else { + /* Interlaced mode */ + + layer->field_id ^= 1; + if (event & VENC_FIRST_FIELD) + fid = 0; + else + fid = 1; + + /* + * If field id does not match with store + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + layer->field_id = fid; + continue; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) + vpbe_isr_even_field(disp_dev, layer); + else /* odd field */ + vpbe_isr_odd_field(disp_dev, layer); + } + } + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < VPBE_DEFAULT_NUM_BUFS) + *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS; + + 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_layer *layer = fh->layer; + struct vpbe_display *disp = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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_layer* +_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev, + struct vpbe_layer *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_osd_display_params(struct vpbe_display *disp_dev, + struct vpbe_layer *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + 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_layer *otherlayer = + _vpbe_display_get_other_win_layer(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_layer *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; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int calculated_xsize; + int h_exp = 0; + int v_exp = 0; + int h_scale; + int v_scale; + + 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)) { + calculated_xsize = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (calculated_xsize <= expected_xsize) { + h_exp = 1; + cfg->xsize = calculated_xsize; + } + } + } + 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)) { + calculated_xsize = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (calculated_xsize <= expected_ysize) { + v_exp = 1; + cfg->ysize = calculated_xsize; + } + } + } + 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_layer *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + cfg->xpos = min((unsigned int)left, + vpbe_dev->current_timings.xres - cfg->xsize); + cfg->ypos = min((unsigned int)top, + vpbe_dev->current_timings.yres - cfg->ysize); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + if ((c->width == 0) || + ((c->width + c->left) > vpbe_dev->current_timings.xres)) + c->width = vpbe_dev->current_timings.xres - c->left; + + if ((c->height == 0) || ((c->height + c->top) > + vpbe_dev->current_timings.yres)) + c->height = vpbe_dev->current_timings.yres - c->top; + + /* window height must be even for interlaced display */ + if (vpbe_dev->current_timings.interlaced) + c->height &= (~0x01); + +} + +/** + * 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. + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int min_height = 1; + int min_width = 32; + int max_height; + int max_width; + int bpp; + + 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_INTERLACED) && + (pixfmt->field != V4L2_FIELD_NONE)) { + 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->width < min_width) || + (pixfmt->width > max_width)) { + pixfmt->width = vpbe_dev->current_timings.xres; + } + + if (!pixfmt->height || (pixfmt->height < min_height) || + (pixfmt->height > max_height)) { + pixfmt->height = vpbe_dev->current_timings.yres; + } + + if (pixfmt->bytesperline < (pixfmt->width * bpp)) + pixfmt->bytesperline = pixfmt->width * bpp; + + /* Make the bytesperline 32 byte aligned */ + pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + 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_layer *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_layer *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_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + cap->version = VPBE_DISPLAY_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); + strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card)); + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (rect->top < 0) + rect->top = 0; + if (rect->left < 0) + rect->left = 0; + + vpbe_disp_check_window_params(disp_dev, rect); + + 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); + + return 0; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + 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; + + return 0; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + /* Fill in the information about format */ + fmt->fmt.pix = layer->pix_fmt; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + 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 > 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 { + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + 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; + } + /* 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; + + if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat) + cfg->pixfmt = PIXFMT_YCbCrI; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_layer *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win_layer(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); + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + +} + +/** + * 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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; + } + } else { + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL == vpbe_dev->ops.enum_outputs) + return -EINVAL; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) + return -EINVAL; + + 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 0; +} + +/** + * 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + if (NULL == vpbe_dev->ops.enum_dv_presets) + return -EINVAL; + + 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 0; +} + +/** + * 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) + return -EINVAL; + + 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 0; +} + +/** + * 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + int ret; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + 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_osd_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->layer_first_int = 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = filep->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned int err = 0; + + 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; +} + +/* + * 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) +{ + struct vpbe_fh *fh = NULL; + struct vpbe_layer *layer = video_drvdata(file); + struct vpbe_display *disp_dev = layer->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int err; + + /* 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) { + + /* 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"); + kfree(fh); + 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_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + + /* 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_layer *otherlayer; + otherlayer = + _vpbe_display_get_other_win_layer(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); + struct vpbe_display *vpbe_disp = data; + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_disp->osd_device = platform_get_drvdata(pdev); + + return 0; +} + +static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, + struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer = NULL; + struct video_device *vbd = NULL; + + /* Allocate memory for four plane display objects */ + + disp_dev->dev[i] = + kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL); + + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + return -ENOMEM; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + vbd = &vpbe_display_layer->video_dev; + /* Initialize field of video device */ + vbd->release = video_device_release_empty; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (disp_dev->vpbe_dev->current_timings.timings_type & + VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + disp_dev->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); + + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + return 0; +} + +static __devinit int register_device(struct vpbe_layer *vpbe_display_layer, + struct vpbe_display *disp_dev, + struct platform_device *pdev) { + int err; + + v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&disp_dev->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, + -1); + if (err) + return -ENODEV; + + vpbe_display_layer->disp_dev = disp_dev; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(&vpbe_display_layer->video_dev, + vpbe_display_layer); + + 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) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev; + struct resource *res = NULL; + int k; + int i; + int err; + 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; + } + + spin_lock_init(&disp_dev->dma_queue_lock); + /* + * 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, disp_dev, + vpbe_device_get); + if (err < 0) + return err; + /* Initialize the vpbe display controller */ + if (NULL != disp_dev->vpbe_dev->ops.initialize) { + err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, + disp_dev->vpbe_dev); + if (err) { + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, + "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (init_vpbe_layer(i, disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&disp_dev->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(&disp_dev->vpbe_dev->v4l2_dev, + "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (register_device(disp_dev->dev[i], disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; + +probe_out: + free_irq(res->start, disp_dev); + for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + if (vpbe_display_layer) { + video_unregister_device( + &vpbe_display_layer->video_dev); + kfree(disp_dev->dev[k]); + } + } + kfree(disp_dev); + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct resource *res; + int i; + + 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); + + } + 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; + + 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 DM644x/DM355/DM365 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..dbf6b37 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,147 @@ +/* + * 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 + +/* 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_layer { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* Pointer to the vpbe_display */ + struct vpbe_display *disp_dev; + /* 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; + u8 layer_first_int; +}; + +/* 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_layer *dev[VPBE_DISPLAY_MAX_DEVICES]; + struct vpbe_device *vpbe_dev; + struct osd_state *osd_device; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_layer *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]; +}; + +#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..727f551 --- /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_version { + 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 Tue May 24 09:02:18 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Tue, 24 May 2011 19:32:18 +0530 Subject: [PATCH v18 2/6] davinci vpbe: VPBE display driver Message-ID: <1306245738-3339-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 | 864 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 184 ++++++++ 2 files changed, 1048 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..d773d30 --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,864 @@ +/* + * 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 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; + int 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; + int 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; + int 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; + int 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 encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + struct vpbe_config *cfg = vpbe_dev->cfg; + int enc_out_index; + int sd_index; + int ret = 0; + + 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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 ret = 0; + int i; + + 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 out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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 j = 0; + int i; + + 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 out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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_enc_mode_info *preset_mode = NULL; + struct vpbe_config *cfg = vpbe_dev->cfg; + struct v4l2_dv_preset dv_preset; + struct osd_state *osd_device; + int out_index = vpbe_dev->current_out_index; + int ret = 0; + int i; + + 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); + + osd_device = vpbe_dev->osd_device; + 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); + struct vpbe_device *vpbe_dev = data; + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_dev->osd_device = platform_get_drvdata(pdev); + + 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; + struct osd_state *osd_device; + struct i2c_adapter *i2c_adap; + int output_index; + int num_encoders; + int ret = 0; + int err; + int i; + + /* + * 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, vpbe_dev, + 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 */ + osd_device = vpbe_dev->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; + /* disable 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_device *vpbe_dev; + struct vpbe_config *cfg; + 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..8b11fb0 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,184 @@ +/* + * 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 +#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; + /* osd_device pointer */ + struct osd_state *osd_device; + /* + * 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 Tue May 24 09:02:33 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Tue, 24 May 2011 19:32:33 +0530 Subject: [PATCH v18 3/6] davinci vpbe: OSD(On Screen Display) block Message-ID: <1306245753-3379-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 | 1231 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 ++++++++ include/media/davinci/vpbe_osd.h | 394 +++++++++ 3 files changed, 1989 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..5352884 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1231 @@ +/* + * 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) +{ + static const int map_2bpp[] = { 0, 5, 10, 15 }; + static const int map_1bpp[] = { 0, 15 }; + int bmp_offset; + int bmp_shift; + int bmp_mask; + int bmp_reg; + + 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]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + 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]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + 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; + + /* 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; + unsigned long flags; + int reject_config; + + 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_platform_data *pdata; + struct osd_state *osd; + struct resource *res; + 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_version)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..d7e397a --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,394 @@ +/* + * 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 + +#include + +#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; +}; + +/* 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_version 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_version vpbe_type; + int field_inv_wa_enable; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Tue May 24 09:02:47 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Tue, 24 May 2011 19:32:47 +0530 Subject: [PATCH v18 4/6] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1306245767-3418-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 | 566 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 ++++++++ include/media/davinci/vpbe_venc.h | 45 ++ 3 files changed, 788 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..03a3e5c --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,566 @@ +/* + * 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) +{ + 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: + return -EINVAL; + } + + return 0; +} + +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 ret; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + 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; + + /* 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..426c205 --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,45 @@ +/* + * 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 +#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_version 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 christian.riesch at omicron.at Tue May 24 09:02:35 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Tue, 24 May 2011 16:02:35 +0200 Subject: [PATCH] DA850: Move oscillator input frequency to board specific files. In-Reply-To: References: Message-ID: <1306245755-16531-1-git-send-email-christian.riesch@omicron.at> Signed-off-by: Christian Riesch --- Hi again, how about this solution? I just took a look how this is solved on other ARM platforms. Regards, Christian arch/arm/mach-davinci/board-da850-evm.c | 4 +++- arch/arm/mach-davinci/board-mityomapl138.c | 4 +++- arch/arm/mach-davinci/board-omapl138-hawk.c | 4 +++- arch/arm/mach-davinci/da850.c | 7 +++---- arch/arm/mach-davinci/include/mach/da8xx.h | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..8984096 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -42,6 +42,8 @@ #include #include +#define DA850_EVM_REF_FREQ 24000000 + #define DA850_EVM_PHY_ID "0:00" #define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8) #define DA850_LCD_BL_PIN GPIO_TO_PIN(2, 15) @@ -1252,7 +1254,7 @@ console_initcall(da850_evm_console_init); static void __init da850_evm_map_io(void) { - da850_init(); + da850_init(DA850_EVM_REF_FREQ); } MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 606a6f2..58fdabb 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -29,6 +29,8 @@ #include #include +#define MITYOMAPL138_REF_FREQ 24000000 + #define MITYOMAPL138_PHY_ID "" #define FACTORY_CONFIG_MAGIC 0x012C0138 @@ -561,7 +563,7 @@ console_initcall(mityomapl138_console_init); static void __init mityomapl138_map_io(void) { - da850_init(); + da850_init(MITYOMAPL138_REF_FREQ); } MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 67c38d0..27dd43c 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -21,6 +21,8 @@ #include #include +#define HAWKBOARD_REF_FREQ 24000000 + #define HAWKBOARD_PHY_ID "0:07" #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) #define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) @@ -334,7 +336,7 @@ console_initcall(omapl138_hawk_console_init); static void __init omapl138_hawk_map_io(void) { - da850_init(); + da850_init(HAWKBOARD_REF_FREQ); } MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..0bcdc34 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -39,8 +39,6 @@ #define DA850_TIMER64P2_BASE 0x01f0c000 #define DA850_TIMER64P3_BASE 0x01f0d000 -#define DA850_REF_FREQ 24000000 - #define CFGCHIP3_ASYNC3_CLKSRC BIT(4) #define CFGCHIP3_PLL1_MASTER_LOCK BIT(5) #define CFGCHIP0_PLL_MASTER_LOCK BIT(4) @@ -57,7 +55,6 @@ static struct pll_data pll0_data = { static struct clk ref_clk = { .name = "ref_clk", - .rate = DA850_REF_FREQ, }; static struct clk pll0_clk = { @@ -1104,10 +1101,12 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .reset_device = &da8xx_wdt_device, }; -void __init da850_init(void) +void __init da850_init(unsigned long ref_clk_rate) { unsigned int v; + ref_clk.rate = ref_clk_rate; + davinci_common_init(&davinci_soc_info_da850); da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..e8e704e 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -70,7 +70,7 @@ extern unsigned int da850_max_speed; #define DA8XX_ARM_RAM_BASE 0xffff0000 void __init da830_init(void); -void __init da850_init(void); +void __init da850_init(unsigned long ref_clk_rate); int da830_register_edma(struct edma_rsv_info *rsv); int da850_register_edma(struct edma_rsv_info *rsv[2]); -- 1.7.0.4 From manjunath.hadli at ti.com Tue May 24 09:03:03 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Tue, 24 May 2011 19:33:03 +0530 Subject: [PATCH v18 5/6] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1306245783-3483-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 | 23 +++++++++++++++++++++++ drivers/media/video/davinci/Makefile | 2 ++ 2 files changed, 25 insertions(+), 0 deletions(-) delete mode 100644 drivers/staging/vme/bridges/Module.symvers diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..60a456e 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,26 @@ 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" + depends on ARCH_DAVINCI_DM644x + 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" + depends on ARCH_DAVINCI_DM644x + select VIDEO_DM644X_VPBE + help + Enables VPBE V4L2 Display driver on a DM644x 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 diff --git a/drivers/staging/vme/bridges/Module.symvers b/drivers/staging/vme/bridges/Module.symvers deleted file mode 100644 index e69de29..0000000 -- 1.6.2.4 From manjunath.hadli at ti.com Tue May 24 09:03:18 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Tue, 24 May 2011 19:33:18 +0530 Subject: [PATCH v18 6/6] davinci vpbe: Readme text for Dm6446 vpbe Message-ID: <1306245798-3529-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 sshtylyov at mvista.com Tue May 24 09:58:34 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 24 May 2011 18:58:34 +0400 Subject: [PATCH v18 5/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1306245783-3483-1-git-send-email-manjunath.hadli@ti.com> References: <1306245783-3483-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4DDBC79A.5000302@mvista.com> Hello. Manjunath Hadli wrote: > 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 | 23 +++++++++++++++++++++++ > drivers/media/video/davinci/Makefile | 2 ++ > 2 files changed, 25 insertions(+), 0 deletions(-) > delete mode 100644 drivers/staging/vme/bridges/Module.symvers [...] > diff --git a/drivers/staging/vme/bridges/Module.symvers b/drivers/staging/vme/bridges/Module.symvers > deleted file mode 100644 > index e69de29..0000000 Looks like a stray file got added to the patch... WBR, Sergei From nsekhar at ti.com Tue May 24 11:27:51 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 24 May 2011 21:57:51 +0530 Subject: [PATCH] DA850: Move oscillator input frequency to board specific files. In-Reply-To: <1306245755-16531-1-git-send-email-christian.riesch@omicron.at> References: <1306245755-16531-1-git-send-email-christian.riesch@omicron.at> Message-ID: On Tue, May 24, 2011 at 19:32:35, Christian Riesch wrote: > > Signed-off-by: Christian Riesch > --- > > Hi again, > how about this solution? I just took a look how this is solved on other > ARM platforms. This approach looks good to me. Just curious as to which other platform used this procedure? Can you please resend this patch with description put in and also CC the Linux ARM Kernel mailing list (linux-arm-kernel at lists.infradead.org)? Plus, the subject needs to begin with "davinci: .." Thanks, Sekhar From michael.williamson at criticallink.com Tue May 24 11:37:15 2011 From: michael.williamson at criticallink.com (Mike Williamson) Date: Tue, 24 May 2011 12:37:15 -0400 Subject: [PATCH] DA850: Move oscillator input frequency to board specific files. In-Reply-To: <1306245755-16531-1-git-send-email-christian.riesch@omicron.at> References: <1306245755-16531-1-git-send-email-christian.riesch@omicron.at> Message-ID: On Tue, May 24, 2011 at 10:02 AM, Christian Riesch wrote: > > Signed-off-by: Christian Riesch > --- > > Hi again, > how about this solution? I just took a look how this is solved on other > ARM platforms. > Regards, Christian > > > ?arch/arm/mach-davinci/board-da850-evm.c ? ? | ? ?4 +++- > ?arch/arm/mach-davinci/board-mityomapl138.c ?| ? ?4 +++- > ?arch/arm/mach-davinci/board-omapl138-hawk.c | ? ?4 +++- > ?arch/arm/mach-davinci/da850.c ? ? ? ? ? ? ? | ? ?7 +++---- > ?arch/arm/mach-davinci/include/mach/da8xx.h ?| ? ?2 +- > ?5 files changed, 13 insertions(+), 8 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index a7b41bf..8984096 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -42,6 +42,8 @@ > ?#include > ?#include > > +#define DA850_EVM_REF_FREQ ? ? ? ? ? ? 24000000 > + > ?#define DA850_EVM_PHY_ID ? ? ? ? ? ? ? "0:00" > ?#define DA850_LCD_PWR_PIN ? ? ? ? ? ? ?GPIO_TO_PIN(2, 8) > ?#define DA850_LCD_BL_PIN ? ? ? ? ? ? ? GPIO_TO_PIN(2, 15) > @@ -1252,7 +1254,7 @@ console_initcall(da850_evm_console_init); > > ?static void __init da850_evm_map_io(void) > ?{ > - ? ? ? da850_init(); > + ? ? ? da850_init(DA850_EVM_REF_FREQ); > ?} > > ?MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") > diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c > index 606a6f2..58fdabb 100644 > --- a/arch/arm/mach-davinci/board-mityomapl138.c > +++ b/arch/arm/mach-davinci/board-mityomapl138.c > @@ -29,6 +29,8 @@ > ?#include > ?#include > > +#define MITYOMAPL138_REF_FREQ ?24000000 > + > ?#define MITYOMAPL138_PHY_ID ? ? ? ? ? ?"" > > ?#define FACTORY_CONFIG_MAGIC ? 0x012C0138 > @@ -561,7 +563,7 @@ console_initcall(mityomapl138_console_init); > > ?static void __init mityomapl138_map_io(void) > ?{ > - ? ? ? da850_init(); > + ? ? ? da850_init(MITYOMAPL138_REF_FREQ); > ?} > > ?MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") > diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c > index 67c38d0..27dd43c 100644 > --- a/arch/arm/mach-davinci/board-omapl138-hawk.c > +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c > @@ -21,6 +21,8 @@ > ?#include > ?#include > > +#define HAWKBOARD_REF_FREQ ? ? ? ? ? ? 24000000 > + > ?#define HAWKBOARD_PHY_ID ? ? ? ? ? ? ? "0:07" > ?#define DA850_HAWK_MMCSD_CD_PIN ? ? ? ? ? ? ? ?GPIO_TO_PIN(3, 12) > ?#define DA850_HAWK_MMCSD_WP_PIN ? ? ? ? ? ? ? ?GPIO_TO_PIN(3, 13) > @@ -334,7 +336,7 @@ console_initcall(omapl138_hawk_console_init); > > ?static void __init omapl138_hawk_map_io(void) > ?{ > - ? ? ? da850_init(); > + ? ? ? da850_init(HAWKBOARD_REF_FREQ); > ?} > > ?MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 133aac4..0bcdc34 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -39,8 +39,6 @@ > ?#define DA850_TIMER64P2_BASE ? 0x01f0c000 > ?#define DA850_TIMER64P3_BASE ? 0x01f0d000 > > -#define DA850_REF_FREQ ? ? ? ? 24000000 > - > ?#define CFGCHIP3_ASYNC3_CLKSRC BIT(4) > ?#define CFGCHIP3_PLL1_MASTER_LOCK ? ? ?BIT(5) > ?#define CFGCHIP0_PLL_MASTER_LOCK ? ? ? BIT(4) > @@ -57,7 +55,6 @@ static struct pll_data pll0_data = { > > ?static struct clk ref_clk = { > ? ? ? ?.name ? ? ? ? ? = "ref_clk", > - ? ? ? .rate ? ? ? ? ? = DA850_REF_FREQ, > ?}; > > ?static struct clk pll0_clk = { > @@ -1104,10 +1101,12 @@ static struct davinci_soc_info davinci_soc_info_da850 = { > ? ? ? ?.reset_device ? ? ? ? ? = &da8xx_wdt_device, > ?}; > > -void __init da850_init(void) > +void __init da850_init(unsigned long ref_clk_rate) > ?{ > ? ? ? ?unsigned int v; > > + ? ? ? ref_clk.rate = ref_clk_rate; > + > ? ? ? ?davinci_common_init(&davinci_soc_info_da850); > > ? ? ? ?da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index ad64da7..e8e704e 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h > @@ -70,7 +70,7 @@ extern unsigned int da850_max_speed; > ?#define DA8XX_ARM_RAM_BASE ? ? 0xffff0000 > > ?void __init da830_init(void); > -void __init da850_init(void); > +void __init da850_init(unsigned long ref_clk_rate); > > ?int da830_register_edma(struct edma_rsv_info *rsv); > ?int da850_register_edma(struct edma_rsv_info *rsv[2]); > -- > 1.7.0.4 > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > It seems to me if you alter the reference clock rate you'll need to ensure that CONFIG_CPU_FREQ is turned off as all the OPP's in da850.c are based on a 24 MHz oscillator input. To support CONFIG_CPU_FREQ with a variable input frequency will likely mean reworking some of the OPP logic to ensure you stay within specification of the PLLMs as well as report the correct frequency of operation. -Mike From bengardiner at nanometrics.ca Tue May 24 13:50:14 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 24 May 2011 14:50:14 -0400 Subject: [PATCH 0/6] ASoC: davinci-pcm: some fixes and convert to BATCH Message-ID: This patchset is a collection of changes for the davinci-pcm driver that were accumulated during previous forays into the davinci-mcasp and davinci-pcm drivers [1]. The first three patches are minor changes that cleanup the ping-pong DMA params setup, expand the davinci-pcm reported format and also its maximum channels. The fourth patch in the series corrects an audible glitch that can be heard on da850evm's McASP when ping-pong buffers are enabled. The glitch occurs only on the second and susequent playbacks, not the first. Reversing the order in which the dma channels are started and moving the start to the trigger function from the prepare function fixes the glitch. The fifth and sixth patches in the series convert the davinci-pcm driver to BATCH mode. I noticed that the davinci-pcm pointer function is returning a value that is retrieved from within the running EDMA channel's state -- which is contrary to the warning in the comments of arch/arm/mach-davinci/dma.c:edma_get_position(): " * Returns current source and destination addresses for a particular * parameter RAM slot. Its channel should not be active when this is called. " To that end, we begin with some refactoring to centralize the modification of the davinci_runtime_data.period member. Followed by wholesale replacement of davinci_pcm_pointer() function so that it returns the position as reported by the last update of the period member -- along with corresponding changes to davinci_pcm_enqueue_dma() davinci_pcm_dma_irq() davinci_pcm_trigger() and reporting SNDRV_PCM_INFO_BATCH on playback and capture. The series has been tested with and without ping-pong buffers enabled [1]; furthermore, the pointer behaviour was inspected using both XRUN_DEBUG_PERIODUPDATE and XRUN_DEBUG_HWPTRUPDATE as well as the audio checked with headphones. Playback was tested with: echo 24 > /proc/asound/EVM/pcm0p/xrun_debug aplay -d 1 -t wav -D "default:CARD=EVM" test.wav echo 0 > /proc/asound/EVM/pcm0p/xrun_debug and capture was tested with (while playing test.wav on my PC into the capture input on the da850evm): #!/bin/bash echo 24 > /proc/asound/EVM/pcm0c/xrun_debug arecord -d 2 -t wav -D "default:CARD=EVM" -f dat - |aplay -t wav echo 0 > /proc/asound/EVM/pcm0c/xrun_debug Playback behaviour before (similar both with and without ping-pong buffers) ========================= hwptr_update: pcmC0D0p:0: pos=42/2048/32768, hwptr=42/0/42/0 hwptr_update: pcmC0D0p:0: pos=369/2048/32768, hwptr=327/42/369/0 hwptr_update: pcmC0D0p:0: pos=709/2048/32768, hwptr=340/369/709/0 hwptr_update: pcmC0D0p:0: pos=994/2048/32768, hwptr=285/709/994/0 hwptr_update: pcmC0D0p:0: pos=1367/2048/32768, hwptr=373/994/1367/0 hwptr_update: pcmC0D0p:0: pos=1729/2048/32768, hwptr=362/1367/1729/0 hwptr_update: pcmC0D0p:0: pos=2037/2048/32768, hwptr=308/1729/2037/0 period_update: pcmC0D0p:0: pos=2335/2048/32768, hwptr=298/2037/2335/0 hwptr_update: pcmC0D0p:0: pos=2762/2048/32768, hwptr=427/2335/2762/0 hwptr_update: pcmC0D0p:0: pos=3147/2048/32768, hwptr=385/2762/3147/0 hwptr_update: pcmC0D0p:0: pos=3456/2048/32768, hwptr=309/3147/3456/0 hwptr_update: pcmC0D0p:0: pos=3851/2048/32768, hwptr=395/3456/3851/0 period_update: pcmC0D0p:0: pos=4150/2048/32768, hwptr=299/3851/4150/0 [...] Playback behaviour after (identical both with and without ping-pong buffers) ======================== hwptr_update: pcmC0D0p:0: pos=0/2048/32768, hwptr=0/0/0/0 period_update: pcmC0D0p:0: pos=2048/2048/32768, hwptr=2048/0/2048/0 hwptr_update: pcmC0D0p:0: pos=2048/2048/32768, hwptr=0/2048/2048/0 period_update: pcmC0D0p:0: pos=4096/2048/32768, hwptr=2048/2048/4096/0 [...] [Capture behaviour was very similarly modified by this patchset] The playback behaviour before shows inconsistent increments of the pointers in syscall and interrupt context -- this is due to the pointer function peeking into the parameters of the dma channel. The playback behaviour afterwards shows regular increments from interrupt context. Which is to be expected since the driver now reports an incremented pointer after each dma completion. It seems that the conversion to BATCH mode is beneficial -- other than obeying the warning comment in dma.c -- since there appear to be fewer syscalls per execution of aplay. [1] http://article.gmane.org/gmane.linux.alsa.devel/85122 Ben Gardiner (6): ASoC: davinci-pcm: trivial: make ping-pong params setup symmetrical ASoC: davinci-pcm: expand the .formats ASoC: davinci-pcm: increase the maximum channels ASoC: davinci-pcm: fix audible glitch on 2nd ping-pong playback ASoC: davinci-pcm: extract period elapsed functions ASoC: davinci-pcm: convert to BATCH mode sound/soc/davinci/davinci-pcm.c | 127 ++++++++++++++++++++------------------- 1 files changed, 65 insertions(+), 62 deletions(-) -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 24 13:50:15 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 24 May 2011 14:50:15 -0400 Subject: [PATCH 1/6] ASoC: davinci-pcm: trivial: make ping-pong params setup symmetrical In-Reply-To: References: Message-ID: The setup of the pong channel uses EDMA_CHAN_SLOT instead of & 0x3f as the setup of the ping channel does. Make the setup of ping and pong symmetric. There is no functional change introduced by this patch. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi --- sound/soc/davinci/davinci-pcm.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 9d35b8c..0d04e0c 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -425,7 +425,8 @@ static int request_ping_pong(struct snd_pcm_substream *substream, edma_read_slot(link, &prtd->asp_params); prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); - prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f); + prtd->asp_params.opt |= TCCHEN | + EDMA_TCC(prtd->ram_channel & 0x3f); edma_write_slot(link, &prtd->asp_params); /* pong */ @@ -439,7 +440,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream, prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); /* interrupt after every pong completion */ prtd->asp_params.opt |= TCINTEN | TCCHEN | - EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel)); + EDMA_TCC(prtd->ram_channel & 0x3f); edma_write_slot(link, &prtd->asp_params); /* ram */ -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 24 13:50:16 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 24 May 2011 14:50:16 -0400 Subject: [PATCH 2/6] ASoC: davinci-pcm: expand the .formats In-Reply-To: References: Message-ID: Based on the data_type test in ping_pong_dma_setup, davinci-pcm is capable of handling data of width up to and including 32bits. " if ((data_type == 0) || (data_type > 4)) { printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); return -EINVAL; } " Update the .format member of the snd_pcm_hardware instances it registers to reflect this capability. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi --- sound/soc/davinci/davinci-pcm.c | 20 ++++++++++++++++++-- 1 files changed, 18 insertions(+), 2 deletions(-) diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 0d04e0c..1e47267 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -46,11 +46,27 @@ static void print_buf_info(int slot, char *name) } #endif +#define DAVINCI_PCM_FMTBITS (\ + SNDRV_PCM_FMTBIT_S8 |\ + SNDRV_PCM_FMTBIT_U8 |\ + SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_U16_BE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_U24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_S32_BE |\ + SNDRV_PCM_FMTBIT_U32_LE |\ + SNDRV_PCM_FMTBIT_U32_BE) + static struct snd_pcm_hardware pcm_hardware_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | @@ -72,7 +88,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 24 13:50:19 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 24 May 2011 14:50:19 -0400 Subject: [PATCH 5/6] ASoC: davinci-pcm: extract period elapsed functions In-Reply-To: References: Message-ID: <370afaeca1dbc55213db8a8df1f8b0b46c076af3.1306258759.git.bengardiner@nanometrics.ca> Extract functions that modify the prtd->period member in preparation for conversion to BATCH mode playback. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi --- sound/soc/davinci/davinci-pcm.c | 22 ++++++++++++++++++---- 1 files changed, 18 insertions(+), 4 deletions(-) diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 5d9269a..fedca81 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -155,6 +155,22 @@ struct davinci_runtime_data { struct edmacc_param ram_params; }; +static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + + prtd->period++; + if (unlikely(prtd->period >= runtime->periods)) + prtd->period = 0; +} + +static void davinci_pcm_period_reset(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + + prtd->period = 0; +} /* * Not used with ping/pong */ @@ -216,9 +232,7 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) edma_set_transfer_params(link, acnt, fifo_level, count, fifo_level, ABSYNC); - prtd->period++; - if (unlikely(prtd->period >= runtime->periods)) - prtd->period = 0; + davinci_pcm_period_elapsed(substream); } static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) @@ -591,7 +605,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) return 0; } - prtd->period = 0; + davinci_pcm_period_reset(substream); davinci_pcm_enqueue_dma(substream); /* Copy self-linked parameter RAM entry into master channel */ -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 24 13:50:20 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 24 May 2011 14:50:20 -0400 Subject: [PATCH 6/6] ASoC: davinci-pcm: convert to BATCH mode In-Reply-To: References: Message-ID: <0f61ef7bb618fc7895a339f4eac55abb2092353c.1306258759.git.bengardiner@nanometrics.ca> The davinci-pcm driver's snd_pcm_ops pointer function currently calls into the edma controller driver to read the current positions of the edma channels to determine pos to return to the ALSA framework. In particular, davinci_pcm_pointer() calls edma_get_position() and the latter has a comment indicating that "Its channel should not be active when this is called" whereas the channel is surely active when snd_pcm_ops.pointer is called. The operation of davinci-pcm in capture and playback appears to follow close the other pcm drivers who export SNDRV_PCM_INFO_BATCH except that davinci-pcm does not report it's positions from pointer() using the last transferred chunk. Instead it peeks directly into the edma controller to determine the current position as discussed above. Convert the davinci-pcm driver to BATCH mode: count the periods elapsed in the prtd->period member and use its value to report the 'pos' to the alsa framework in the davinci_pcm_pointer function. There is a phase offset of 2 periods between the position used by dma setup and the position reported in the pointer function. Either +2 in the dma setup or -2 in the pointer function (with wrapping, both) accounts for this offset -- I opted for the latter since it makes the first-time setup clearer. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi --- sound/soc/davinci/davinci-pcm.c | 67 +++++++++++---------------------------- 1 files changed, 19 insertions(+), 48 deletions(-) diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index fedca81..fa8fc61 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -65,7 +65,8 @@ static void print_buf_info(int slot, char *name) static struct snd_pcm_hardware pcm_hardware_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME| + SNDRV_PCM_INFO_BATCH), .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | @@ -87,7 +88,8 @@ static struct snd_pcm_hardware pcm_hardware_playback = { static struct snd_pcm_hardware pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH), .formats = DAVINCI_PCM_FMTBITS, .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | @@ -231,8 +233,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) else edma_set_transfer_params(link, acnt, fifo_level, count, fifo_level, ABSYNC); - - davinci_pcm_period_elapsed(substream); } static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) @@ -247,12 +247,13 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) return; if (snd_pcm_running(substream)) { + spin_lock(&prtd->lock); if (prtd->ram_channel < 0) { /* No ping/pong must fix up link dma data*/ - spin_lock(&prtd->lock); davinci_pcm_enqueue_dma(substream); - spin_unlock(&prtd->lock); } + davinci_pcm_period_elapsed(substream); + spin_unlock(&prtd->lock); snd_pcm_period_elapsed(substream); } } @@ -588,6 +589,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; + davinci_pcm_period_reset(substream); if (prtd->ram_channel >= 0) { int ret = ping_pong_dma_setup(substream); if (ret < 0) @@ -603,15 +605,19 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) print_buf_info(prtd->asp_link[0], "asp_link[0]"); print_buf_info(prtd->asp_link[1], "asp_link[1]"); + davinci_pcm_period_elapsed(substream); + davinci_pcm_period_elapsed(substream); + return 0; } - davinci_pcm_period_reset(substream); davinci_pcm_enqueue_dma(substream); + davinci_pcm_period_elapsed(substream); /* Copy self-linked parameter RAM entry into master channel */ edma_read_slot(prtd->asp_link[0], &prtd->asp_params); edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); + davinci_pcm_period_elapsed(substream); return 0; } @@ -623,51 +629,16 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) struct davinci_runtime_data *prtd = runtime->private_data; unsigned int offset; int asp_count; - dma_addr_t asp_src, asp_dst; + unsigned int period_size = snd_pcm_lib_period_bytes(substream); spin_lock(&prtd->lock); - if (prtd->ram_channel >= 0) { - int ram_count; - int mod_ram; - dma_addr_t ram_src, ram_dst; - unsigned int period_size = snd_pcm_lib_period_bytes(substream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* reading ram before asp should be safe - * as long as the asp transfers less than a ping size - * of bytes between the 2 reads - */ - edma_get_position(prtd->ram_channel, - &ram_src, &ram_dst); - edma_get_position(prtd->asp_channel, - &asp_src, &asp_dst); - asp_count = asp_src - prtd->asp_params.src; - ram_count = ram_src - prtd->ram_params.src; - mod_ram = ram_count % period_size; - mod_ram -= asp_count; - if (mod_ram < 0) - mod_ram += period_size; - else if (mod_ram == 0) { - if (snd_pcm_running(substream)) - mod_ram += period_size; - } - ram_count -= mod_ram; - if (ram_count < 0) - ram_count += period_size * runtime->periods; - } else { - edma_get_position(prtd->ram_channel, - &ram_src, &ram_dst); - ram_count = ram_dst - prtd->ram_params.dst; - } - asp_count = ram_count; - } else { - edma_get_position(prtd->asp_channel, &asp_src, &asp_dst); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - asp_count = asp_src - runtime->dma_addr; - else - asp_count = asp_dst - runtime->dma_addr; - } + asp_count = prtd->period - 2; spin_unlock(&prtd->lock); + if (asp_count < 0) + asp_count += runtime->periods; + asp_count *= period_size; + offset = bytes_to_frames(runtime, asp_count); if (offset >= runtime->buffer_size) offset = 0; -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 24 13:50:18 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 24 May 2011 14:50:18 -0400 Subject: [PATCH 4/6] ASoC: davinci-pcm: fix audible glitch on 2nd ping-pong playback In-Reply-To: References: Message-ID: The release of the dma channels was being performed in prepare and there was a edma_resume call for the asp-channel only being executed on START, RESUME and PAUSE_RELEASE. The mcasp on da850evm with ping-pong buffers enabled was exhibiting an audible glitch on every playback after the first. It was determined through trial and error that the following two changes fix this problem: 1) Move the edma_start calls from prepare to trigger and 2) reverse the order of starting the asp and ram channels. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi CC: Troy Kisky --- sound/soc/davinci/davinci-pcm.c | 13 +++++++------ 1 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 9b5a9bf..5d9269a 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -544,6 +544,13 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: + edma_start(prtd->asp_channel); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + prtd->ram_channel >= 0) { + /* copy 1st iram buffer */ + edma_start(prtd->ram_channel); + } + break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: edma_resume(prtd->asp_channel); @@ -582,11 +589,6 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) print_buf_info(prtd->asp_link[0], "asp_link[0]"); print_buf_info(prtd->asp_link[1], "asp_link[1]"); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* copy 1st iram buffer */ - edma_start(prtd->ram_channel); - } - edma_start(prtd->asp_channel); return 0; } prtd->period = 0; @@ -596,7 +598,6 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) edma_read_slot(prtd->asp_link[0], &prtd->asp_params); edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); - edma_start(prtd->asp_channel); return 0; } -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 24 13:50:17 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 24 May 2011 14:50:17 -0400 Subject: [PATCH 3/6] ASoC: davinci-pcm: increase the maximum channels In-Reply-To: References: Message-ID: <0627d11625ae11c83601978eb0c2a9dceef69652.1306258759.git.bengardiner@nanometrics.ca> Based on the registration of davinci-mcasp.1 in the davinci-evm platform setup for da830 and dm6467, davinci-pcm can handle more than the currently reported maximum channels of 2. Increase the maximum channels to 384 to match the maximum reported by davinci-mcasp.1. Signed-off-by: Ben Gardiner Reviewed-by: Steven Faludi --- sound/soc/davinci/davinci-pcm.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 1e47267..9b5a9bf 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -75,7 +75,7 @@ static struct snd_pcm_hardware pcm_hardware_playback = { .rate_min = 8000, .rate_max = 96000, .channels_min = 2, - .channels_max = 2, + .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, @@ -97,7 +97,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = { .rate_min = 8000, .rate_max = 96000, .channels_min = 2, - .channels_max = 2, + .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, -- 1.7.4.1 From christian.riesch at omicron.at Wed May 25 03:22:53 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Wed, 25 May 2011 10:22:53 +0200 Subject: [PATCH] DA850: Move oscillator input frequency to board specific files. In-Reply-To: References: <1306245755-16531-1-git-send-email-christian.riesch@omicron.at> Message-ID: <95DC1AA8EC908B48939B72CF375AA5E3011CDD3BDB@alice.at.omicron.at> From Mike Williamson [mailto:michael.williamson at criticallink.com] >It seems to me if you alter the reference clock rate you'll need to >ensure that CONFIG_CPU_FREQ is turned off as all the OPP's in da850.c >are based on a 24 MHz oscillator input. To support CONFIG_CPU_FREQ with >a variable input frequency will likely mean reworking some of the OPP >logic to ensure you stay within specification of the PLLMs as well as >report the correct frequency of operation. Thanks for the hint. In my configuration CONFIG_CPU_FREQ will not be used, so I should be fine. IMHO the definition of the OPPs should be moved to the board specific files as well in a separate patch. The ones defined in da850.c are not the only possible configurations, other boards may as well use different OPPs. Anyway, my patch does not break CONFIG_CPU_FREQ for the boards that have a 24 MHz oscillator. Christian From christian.riesch at omicron.at Wed May 25 03:36:12 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Wed, 25 May 2011 10:36:12 +0200 Subject: [PATCH] DA850: Move oscillator input frequency to board specific files. In-Reply-To: References: <1306245755-16531-1-git-send-email-christian.riesch@omicron.at> Message-ID: <95DC1AA8EC908B48939B72CF375AA5E3011CDD3C00@alice.at.omicron.at> From Nori, Sekhar [mailto:nsekhar at ti.com] >On Tue, May 24, 2011 at 19:32:35, Christian Riesch wrote: >> >> Signed-off-by: Christian Riesch >> --- >> >> Hi again, >> how about this solution? I just took a look how this is solved on >> other ARM platforms. > >This approach looks good to me. Just curious as to which other platform >used this procedure? mach-at91 (see arch/arm/mach-at91/at91sam9g45.c) mach-mx5 (see arch/arm/mach-mx5/board-mx53_evk.c) >Can you please resend this patch with description put in and also CC the >Linux ARM Kernel mailing list (linux-arm-kernel at lists.infradead.org)? > >Plus, the subject needs to begin with "davinci: .." Ok, I'll resend the patch. Thanks, Christian From christian.riesch at omicron.at Wed May 25 03:37:02 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Wed, 25 May 2011 10:37:02 +0200 Subject: [PATCH] davinci: da850: move input frequency to board specific files. Message-ID: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> Currently the input frequency of the SoC is hardcoded in the SoC specific da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide range from 12 to 50 MHz, boards with different oscillator/crystal frequencies may be built. This patch moves the definition of the input frequency to the board specific files to support boards with oscillator/crystal frequencies other than 24 MHz. Signed-off-by: Christian Riesch --- arch/arm/mach-davinci/board-da850-evm.c | 4 +++- arch/arm/mach-davinci/board-mityomapl138.c | 4 +++- arch/arm/mach-davinci/board-omapl138-hawk.c | 4 +++- arch/arm/mach-davinci/da850.c | 7 +++---- arch/arm/mach-davinci/include/mach/da8xx.h | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..8984096 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -42,6 +42,8 @@ #include #include +#define DA850_EVM_REF_FREQ 24000000 + #define DA850_EVM_PHY_ID "0:00" #define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8) #define DA850_LCD_BL_PIN GPIO_TO_PIN(2, 15) @@ -1252,7 +1254,7 @@ console_initcall(da850_evm_console_init); static void __init da850_evm_map_io(void) { - da850_init(); + da850_init(DA850_EVM_REF_FREQ); } MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 606a6f2..58fdabb 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -29,6 +29,8 @@ #include #include +#define MITYOMAPL138_REF_FREQ 24000000 + #define MITYOMAPL138_PHY_ID "" #define FACTORY_CONFIG_MAGIC 0x012C0138 @@ -561,7 +563,7 @@ console_initcall(mityomapl138_console_init); static void __init mityomapl138_map_io(void) { - da850_init(); + da850_init(MITYOMAPL138_REF_FREQ); } MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 67c38d0..27dd43c 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -21,6 +21,8 @@ #include #include +#define HAWKBOARD_REF_FREQ 24000000 + #define HAWKBOARD_PHY_ID "0:07" #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) #define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) @@ -334,7 +336,7 @@ console_initcall(omapl138_hawk_console_init); static void __init omapl138_hawk_map_io(void) { - da850_init(); + da850_init(HAWKBOARD_REF_FREQ); } MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..0bcdc34 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -39,8 +39,6 @@ #define DA850_TIMER64P2_BASE 0x01f0c000 #define DA850_TIMER64P3_BASE 0x01f0d000 -#define DA850_REF_FREQ 24000000 - #define CFGCHIP3_ASYNC3_CLKSRC BIT(4) #define CFGCHIP3_PLL1_MASTER_LOCK BIT(5) #define CFGCHIP0_PLL_MASTER_LOCK BIT(4) @@ -57,7 +55,6 @@ static struct pll_data pll0_data = { static struct clk ref_clk = { .name = "ref_clk", - .rate = DA850_REF_FREQ, }; static struct clk pll0_clk = { @@ -1104,10 +1101,12 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .reset_device = &da8xx_wdt_device, }; -void __init da850_init(void) +void __init da850_init(unsigned long ref_clk_rate) { unsigned int v; + ref_clk.rate = ref_clk_rate; + davinci_common_init(&davinci_soc_info_da850); da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..e8e704e 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -70,7 +70,7 @@ extern unsigned int da850_max_speed; #define DA8XX_ARM_RAM_BASE 0xffff0000 void __init da830_init(void); -void __init da850_init(void); +void __init da850_init(unsigned long ref_clk_rate); int da830_register_edma(struct edma_rsv_info *rsv); int da850_register_edma(struct edma_rsv_info *rsv[2]); -- 1.7.0.4 From broonie at opensource.wolfsonmicro.com Wed May 25 06:14:41 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 25 May 2011 19:14:41 +0800 Subject: [PATCH 0/6] ASoC: davinci-pcm: some fixes and convert to BATCH In-Reply-To: References: Message-ID: <20110525111440.GB10797@opensource.wolfsonmicro.com> On Tue, May 24, 2011 at 02:50:14PM -0400, Ben Gardiner wrote: > This patchset is a collection of changes for the davinci-pcm driver that were > accumulated during previous forays into the davinci-mcasp and davinci-pcm > drivers [1]. Applied all, thanks. From bengardiner at nanometrics.ca Wed May 25 07:17:00 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 25 May 2011 08:17:00 -0400 Subject: [PATCH 6/6] ASoC: davinci-pcm: convert to BATCH mode In-Reply-To: <4DDCD16C.1080702@ti.com> References: <0f61ef7bb618fc7895a339f4eac55abb2092353c.1306258759.git.bengardiner@nanometrics.ca> <4DDCD16C.1080702@ti.com> Message-ID: Hi Liam, On Wed, May 25, 2011 at 5:52 AM, Liam Girdwood wrote: > On 24/05/11 19:50, Ben Gardiner wrote: >> [...] >> @@ -603,15 +605,19 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) >> ? ? ? ? ? ? ? print_buf_info(prtd->asp_link[0], "asp_link[0]"); >> ? ? ? ? ? ? ? print_buf_info(prtd->asp_link[1], "asp_link[1]"); >> >> + ? ? ? ? ? ? davinci_pcm_period_elapsed(substream); >> + ? ? ? ? ? ? davinci_pcm_period_elapsed(substream); > > I assume these are to do with the 2 period phase offset you have so it's probably better to comment this here too. That's correct and yes I really should have commented this magic a little. I will send an incremental patch that can be folded-in. Thanks again. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From manjunath.hadli at ti.com Wed May 25 07:19:16 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 25 May 2011 17:49:16 +0530 Subject: [PATCH v19 0/6] davinci vpbe: dm6446 v4l2 driver Message-ID: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> fixed a wrong file inclusion in one of the patches Manjunath Hadli (6): 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 Documentation/video4linux/README.davinci-vpbe | 93 ++ drivers/media/video/davinci/Kconfig | 23 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 864 ++++++++++++ drivers/media/video/davinci/vpbe_display.c | 1860 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1231 ++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ include/media/davinci/vpbe.h | 184 +++ include/media/davinci/vpbe_display.h | 147 ++ include/media/davinci/vpbe_osd.h | 394 ++++++ include/media/davinci/vpbe_types.h | 91 ++ include/media/davinci/vpbe_venc.h | 45 + 14 files changed, 6041 insertions(+), 0 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 Wed May 25 07:19:22 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 25 May 2011 17:49:22 +0530 Subject: [PATCH v19 6/6] davinci vpbe: Readme text for Dm6446 vpbe In-Reply-To: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> References: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1306325962-19299-7-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 Wed May 25 07:19:21 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 25 May 2011 17:49:21 +0530 Subject: [PATCH v19 5/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> References: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1306325962-19299-6-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 | 23 +++++++++++++++++++++++ drivers/media/video/davinci/Makefile | 2 ++ 2 files changed, 25 insertions(+), 0 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..60a456e 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,26 @@ 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" + depends on ARCH_DAVINCI_DM644x + 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" + depends on ARCH_DAVINCI_DM644x + select VIDEO_DM644X_VPBE + help + Enables VPBE V4L2 Display driver on a DM644x 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 Wed May 25 07:19:20 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 25 May 2011 17:49:20 +0530 Subject: [PATCH v19 4/6] davinci vpbe: VENC( Video Encoder) implementation In-Reply-To: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> References: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1306325962-19299-5-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 | 566 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 ++++++++ include/media/davinci/vpbe_venc.h | 45 ++ 3 files changed, 788 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..03a3e5c --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,566 @@ +/* + * 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) +{ + 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: + return -EINVAL; + } + + return 0; +} + +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 ret; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + 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; + + /* 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..426c205 --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,45 @@ +/* + * 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 +#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_version 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 Wed May 25 07:19:18 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 25 May 2011 17:49:18 +0530 Subject: [PATCH v19 2/6] davinci vpbe: VPBE display driver In-Reply-To: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> References: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1306325962-19299-3-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 | 864 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 184 ++++++++ 2 files changed, 1048 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..d773d30 --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,864 @@ +/* + * 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 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; + int 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; + int 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; + int 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; + int 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 encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + struct vpbe_config *cfg = vpbe_dev->cfg; + int enc_out_index; + int sd_index; + int ret = 0; + + 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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 ret = 0; + int i; + + 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 out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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 j = 0; + int i; + + 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 out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int 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) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + 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_enc_mode_info *preset_mode = NULL; + struct vpbe_config *cfg = vpbe_dev->cfg; + struct v4l2_dv_preset dv_preset; + struct osd_state *osd_device; + int out_index = vpbe_dev->current_out_index; + int ret = 0; + int i; + + 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); + + osd_device = vpbe_dev->osd_device; + 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); + struct vpbe_device *vpbe_dev = data; + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_dev->osd_device = platform_get_drvdata(pdev); + + 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; + struct osd_state *osd_device; + struct i2c_adapter *i2c_adap; + int output_index; + int num_encoders; + int ret = 0; + int err; + int i; + + /* + * 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, vpbe_dev, + 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 */ + osd_device = vpbe_dev->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; + /* disable 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_device *vpbe_dev; + struct vpbe_config *cfg; + 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..8b11fb0 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,184 @@ +/* + * 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 +#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; + /* osd_device pointer */ + struct osd_state *osd_device; + /* + * 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 Wed May 25 07:19:19 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 25 May 2011 17:49:19 +0530 Subject: [PATCH v19 3/6] davinci vpbe: OSD(On Screen Display) block In-Reply-To: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> References: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1306325962-19299-4-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 | 1231 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 ++++++++ include/media/davinci/vpbe_osd.h | 394 +++++++++ 3 files changed, 1989 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..5352884 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1231 @@ +/* + * 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) +{ + static const int map_2bpp[] = { 0, 5, 10, 15 }; + static const int map_1bpp[] = { 0, 15 }; + int bmp_offset; + int bmp_shift; + int bmp_mask; + int bmp_reg; + + 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]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + 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]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + 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; + + /* 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; + unsigned long flags; + int reject_config; + + 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_platform_data *pdata; + struct osd_state *osd; + struct resource *res; + 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_version)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..d7e397a --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,394 @@ +/* + * 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 + +#include + +#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; +}; + +/* 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_version 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_version vpbe_type; + int field_inv_wa_enable; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Wed May 25 07:19:17 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 25 May 2011 17:49:17 +0530 Subject: [PATCH v19 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> References: <1306325962-19299-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1306325962-19299-2-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 | 1860 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 147 +++ include/media/davinci/vpbe_types.h | 91 ++ 3 files changed, 2098 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..7f1d83a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,1860 @@ +/* + * 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 "vpbe_venc_regs.h" + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +module_param(debug, int, 0644); + +static int venc_is_second_field(struct vpbe_display *disp_dev) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int ret; + 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; +} + +static void vpbe_isr_even_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct timespec timevalue; + + if (layer->cur_frm == layer->next_frm) + return; + ktime_get_ts(&timevalue); + layer->cur_frm->ts.tv_sec = timevalue.tv_sec; + layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC; + 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; +} + +static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct osd_state *osd_device = disp_obj->osd_device; + unsigned long addr; + + spin_lock(&disp_obj->dma_queue_lock); + if (list_empty(&layer->dma_queue) || + (layer->cur_frm != layer->next_frm)) { + spin_unlock(&disp_obj->dma_queue_lock); + return; + } + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + 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); + spin_unlock(&disp_obj->dma_queue_lock); + /* 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_obj->cbcr_ofst); +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + struct vpbe_display *disp_dev = (struct vpbe_display *)arg; + struct vpbe_layer *layer; + static unsigned last_event; + unsigned event = 0; + int fid; + int i; + + if ((NULL == arg) || (NULL == disp_dev->dev[0])) + return IRQ_HANDLED; + + if (venc_is_second_field(disp_dev)) + 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; + + 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; + + if (layer->layer_first_int) { + layer->layer_first_int = 0; + continue; + } + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & VENC_END_OF_FRAME)) { + /* Progressive mode */ + + vpbe_isr_even_field(disp_dev, layer); + vpbe_isr_odd_field(disp_dev, layer); + } else { + /* Interlaced mode */ + + layer->field_id ^= 1; + if (event & VENC_FIRST_FIELD) + fid = 0; + else + fid = 1; + + /* + * If field id does not match with store + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + layer->field_id = fid; + continue; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) + vpbe_isr_even_field(disp_dev, layer); + else /* odd field */ + vpbe_isr_odd_field(disp_dev, layer); + } + } + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < VPBE_DEFAULT_NUM_BUFS) + *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS; + + 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_layer *layer = fh->layer; + struct vpbe_display *disp = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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_layer* +_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev, + struct vpbe_layer *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_osd_display_params(struct vpbe_display *disp_dev, + struct vpbe_layer *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + 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_layer *otherlayer = + _vpbe_display_get_other_win_layer(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_layer *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; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int calculated_xsize; + int h_exp = 0; + int v_exp = 0; + int h_scale; + int v_scale; + + 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)) { + calculated_xsize = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (calculated_xsize <= expected_xsize) { + h_exp = 1; + cfg->xsize = calculated_xsize; + } + } + } + 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)) { + calculated_xsize = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (calculated_xsize <= expected_ysize) { + v_exp = 1; + cfg->ysize = calculated_xsize; + } + } + } + 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_layer *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + cfg->xpos = min((unsigned int)left, + vpbe_dev->current_timings.xres - cfg->xsize); + cfg->ypos = min((unsigned int)top, + vpbe_dev->current_timings.yres - cfg->ysize); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + if ((c->width == 0) || + ((c->width + c->left) > vpbe_dev->current_timings.xres)) + c->width = vpbe_dev->current_timings.xres - c->left; + + if ((c->height == 0) || ((c->height + c->top) > + vpbe_dev->current_timings.yres)) + c->height = vpbe_dev->current_timings.yres - c->top; + + /* window height must be even for interlaced display */ + if (vpbe_dev->current_timings.interlaced) + c->height &= (~0x01); + +} + +/** + * 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. + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int min_height = 1; + int min_width = 32; + int max_height; + int max_width; + int bpp; + + 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_INTERLACED) && + (pixfmt->field != V4L2_FIELD_NONE)) { + 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->width < min_width) || + (pixfmt->width > max_width)) { + pixfmt->width = vpbe_dev->current_timings.xres; + } + + if (!pixfmt->height || (pixfmt->height < min_height) || + (pixfmt->height > max_height)) { + pixfmt->height = vpbe_dev->current_timings.yres; + } + + if (pixfmt->bytesperline < (pixfmt->width * bpp)) + pixfmt->bytesperline = pixfmt->width * bpp; + + /* Make the bytesperline 32 byte aligned */ + pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + 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_layer *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_layer *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_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + cap->version = VPBE_DISPLAY_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); + strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card)); + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (rect->top < 0) + rect->top = 0; + if (rect->left < 0) + rect->left = 0; + + vpbe_disp_check_window_params(disp_dev, rect); + + 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); + + return 0; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + 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; + + return 0; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + /* Fill in the information about format */ + fmt->fmt.pix = layer->pix_fmt; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + 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 > 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 { + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + 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; + } + /* 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; + + if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat) + cfg->pixfmt = PIXFMT_YCbCrI; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_layer *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win_layer(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); + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + +} + +/** + * 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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; + } + } else { + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL == vpbe_dev->ops.enum_outputs) + return -EINVAL; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) + return -EINVAL; + + 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 0; +} + +/** + * 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + if (NULL == vpbe_dev->ops.enum_dv_presets) + return -EINVAL; + + 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 0; +} + +/** + * 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) + return -EINVAL; + + 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 0; +} + +/** + * 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) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + int ret; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + 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_osd_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->layer_first_int = 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + 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_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + 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) +{ + struct vpbe_fh *fh = filep->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned int err = 0; + + 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; +} + +/* + * 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) +{ + struct vpbe_fh *fh = NULL; + struct vpbe_layer *layer = video_drvdata(file); + struct vpbe_display *disp_dev = layer->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int err; + + /* 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) { + + /* 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"); + kfree(fh); + 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_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + + /* 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_layer *otherlayer; + otherlayer = + _vpbe_display_get_other_win_layer(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); + struct vpbe_display *vpbe_disp = data; + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_disp->osd_device = platform_get_drvdata(pdev); + + return 0; +} + +static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, + struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer = NULL; + struct video_device *vbd = NULL; + + /* Allocate memory for four plane display objects */ + + disp_dev->dev[i] = + kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL); + + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + return -ENOMEM; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + vbd = &vpbe_display_layer->video_dev; + /* Initialize field of video device */ + vbd->release = video_device_release_empty; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (disp_dev->vpbe_dev->current_timings.timings_type & + VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + disp_dev->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); + + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + return 0; +} + +static __devinit int register_device(struct vpbe_layer *vpbe_display_layer, + struct vpbe_display *disp_dev, + struct platform_device *pdev) { + int err; + + v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&disp_dev->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, + -1); + if (err) + return -ENODEV; + + vpbe_display_layer->disp_dev = disp_dev; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(&vpbe_display_layer->video_dev, + vpbe_display_layer); + + 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) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev; + struct resource *res = NULL; + int k; + int i; + int err; + 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; + } + + spin_lock_init(&disp_dev->dma_queue_lock); + /* + * 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, disp_dev, + vpbe_device_get); + if (err < 0) + return err; + /* Initialize the vpbe display controller */ + if (NULL != disp_dev->vpbe_dev->ops.initialize) { + err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, + disp_dev->vpbe_dev); + if (err) { + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, + "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (init_vpbe_layer(i, disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&disp_dev->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(&disp_dev->vpbe_dev->v4l2_dev, + "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (register_device(disp_dev->dev[i], disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; + +probe_out: + free_irq(res->start, disp_dev); + for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + if (vpbe_display_layer) { + video_unregister_device( + &vpbe_display_layer->video_dev); + kfree(disp_dev->dev[k]); + } + } + kfree(disp_dev); + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct resource *res; + int i; + + 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); + + } + 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; + + 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 DM644x/DM355/DM365 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..dbf6b37 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,147 @@ +/* + * 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 + +/* 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_layer { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* Pointer to the vpbe_display */ + struct vpbe_display *disp_dev; + /* 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; + u8 layer_first_int; +}; + +/* 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_layer *dev[VPBE_DISPLAY_MAX_DEVICES]; + struct vpbe_device *vpbe_dev; + struct osd_state *osd_device; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_layer *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]; +}; + +#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..727f551 --- /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_version { + 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 bengardiner at nanometrics.ca Wed May 25 08:27:22 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 25 May 2011 09:27:22 -0400 Subject: [PATCH] ASoC: davinci-pcm: comments for the conversion to BATCH mode In-Reply-To: <4DDCD16C.1080702@ti.com> References: <4DDCD16C.1080702@ti.com> Message-ID: <1306330042-16714-1-git-send-email-bengardiner@nanometrics.ca> In the previous commit 'ASoC: davinci-pcm: convert to BATCH mode', the phase offset of 2 was mentioned in the commit message but not well commented in the source. Add descriptive comments of the phase offset with and without ping-pong buffers enabled. Signed-off-by: Ben Gardiner --- sound/soc/davinci/davinci-pcm.c | 19 +++++++++++++++++++ 1 files changed, 19 insertions(+), 0 deletions(-) diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index fa8fc61..c9e0320 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -605,6 +605,18 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream) print_buf_info(prtd->asp_link[0], "asp_link[0]"); print_buf_info(prtd->asp_link[1], "asp_link[1]"); + /* + * There is a phase offset of 2 periods between the position + * used by dma setup and the position reported in the pointer + * function. + * + * The phase offset, when not using ping-pong buffers, is due to + * the two consecutive calls to davinci_pcm_enqueue_dma() below. + * + * Whereas here, with ping-pong buffers, the phase is due to + * there being an entire buffer transfer complete before the + * first dma completion event triggers davinci_pcm_dma_irq(). + */ davinci_pcm_period_elapsed(substream); davinci_pcm_period_elapsed(substream); @@ -631,6 +643,13 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) int asp_count; unsigned int period_size = snd_pcm_lib_period_bytes(substream); + /* + * There is a phase offset of 2 periods between the position used by dma + * setup and the position reported in the pointer function. Either +2 in + * the dma setup or -2 here in the pointer function (with wrapping, + * both) accounts for this offset -- choose the latter since it makes + * the first-time setup clearer. + */ spin_lock(&prtd->lock); asp_count = prtd->period - 2; spin_unlock(&prtd->lock); -- 1.7.4.1 From broonie at opensource.wolfsonmicro.com Wed May 25 10:00:44 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 25 May 2011 23:00:44 +0800 Subject: [PATCH] ASoC: davinci-pcm: comments for the conversion to BATCH mode In-Reply-To: <1306330042-16714-1-git-send-email-bengardiner@nanometrics.ca> References: <4DDCD16C.1080702@ti.com> <1306330042-16714-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <20110525150043.GA12816@opensource.wolfsonmicro.com> On Wed, May 25, 2011 at 09:27:22AM -0400, Ben Gardiner wrote: > In the previous commit 'ASoC: davinci-pcm: convert to BATCH mode', the phase > offset of 2 was mentioned in the commit message but not well commented in the > source. Applied, thanks. From khilman at ti.com Wed May 25 16:07:32 2011 From: khilman at ti.com (Kevin Hilman) Date: Wed, 25 May 2011 14:07:32 -0700 Subject: [GIT PULL] Pull request for 2.6.40 In-Reply-To: (Sekhar Nori's message of "Tue, 24 May 2011 15:51:53 +0530") References: Message-ID: <87boyqtror.fsf@ti.com> Hi Sekhar, "Nori, Sekhar" writes: > Hi Russell, > > I have been tracking these four patches for inclusion in 2.6.40 > can you please pull them? > > These are clean-up rather than consolidation patches and all > of them have zero or negative net diffstat. > The commits in this branch aren't quite right. All except the last one are missing your signoff. As you're on the delivery path, be sure to include your signoff on all the patches. Thanks, Kevin > > The following changes since commit 0ee5623f9a6e52df90a78bd21179f8ab370e102e: > Linus Torvalds (1): > Linux 2.6.39-rc6 > > are available in the git repository at: > > git://gitorious.org/linux-davinci/linux-davinci.git davinci-next > > Manjunath Hadli (1): > davinci: move DM64XX_VDD3P3V_PWDN to devices.c > > Sergei Shtylyov (3): > DA8xx: kill duplicate #define DA8XX_GPIO_BASE > DA8xx: kill duplicate #define DA8XX_PLL1_BASE > DA8xx: move base address #define's to their proper place > > arch/arm/mach-davinci/da850.c | 2 +- > arch/arm/mach-davinci/devices-da8xx.c | 16 +++++++++------- > arch/arm/mach-davinci/devices.c | 3 +++ > arch/arm/mach-davinci/include/mach/da8xx.h | 4 ---- > arch/arm/mach-davinci/include/mach/hardware.h | 3 --- > 5 files changed, 13 insertions(+), 15 deletions(-) > > _______________________________________________ > 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 nsekhar at ti.com Thu May 26 00:30:17 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 26 May 2011 11:00:17 +0530 Subject: [GIT PULL] Pull request for 2.6.40 In-Reply-To: <87boyqtror.fsf@ti.com> References: <87boyqtror.fsf@ti.com> Message-ID: Hi Kevin, On Thu, May 26, 2011 at 02:37:32, Hilman, Kevin wrote: > Hi Sekhar, > > "Nori, Sekhar" writes: > > > Hi Russell, > > > > I have been tracking these four patches for inclusion in 2.6.40 > > can you please pull them? > > > > These are clean-up rather than consolidation patches and all > > of them have zero or negative net diffstat. > > > > The commits in this branch aren't quite right. All except the last one > are missing your signoff. As you're on the delivery path, be sure to > include your signoff on all the patches. Thanks for pointing out. I have fixed that now. The same branch has been updated. Will take care next time on. Unfortunately the commit times have been updated as well, so they like recent commits now. Regards, Sekhar From linux at arm.linux.org.uk Thu May 26 02:27:07 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Thu, 26 May 2011 08:27:07 +0100 Subject: [GIT PULL] Pull request for 2.6.40 In-Reply-To: References: <87boyqtror.fsf@ti.com> Message-ID: <20110526072707.GE24876@n2100.arm.linux.org.uk> On Thu, May 26, 2011 at 11:00:17AM +0530, Nori, Sekhar wrote: > Thanks for pointing out. I have fixed that now. The same branch > has been updated. Will take care next time on. > > Unfortunately the commit times have been updated as well, so they > like recent commits now. And the other problem is I've already pulled it into my devel-stable branch and pushed it out, so I'm not re-pulling it. From christian.riesch at omicron.at Fri May 27 04:20:53 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Fri, 27 May 2011 11:20:53 +0200 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> Message-ID: <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> From: Bob Dunlop Currently the input frequency of the SoC is hardcoded in the SoC specific da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide range from 12 to 50 MHz, boards with different oscillator/crystal frequencies may be built. This patch allows setting a different input frequency in the board specific files to support boards with oscillator/crystal frequencies other than 24 MHz. Signed-off-by: Bob Dunlop Signed-off-by: Christian Riesch --- Hi, in private email Bob Dunlop suggested to pass a pointer to a small structure instead of the frequency value to allow future expansions, e.g., for the OPP list. Therefore I submit his patch as V2. Christian arch/arm/mach-davinci/board-da850-evm.c | 2 +- arch/arm/mach-davinci/board-mityomapl138.c | 2 +- arch/arm/mach-davinci/board-omapl138-hawk.c | 2 +- arch/arm/mach-davinci/da850.c | 5 ++++- arch/arm/mach-davinci/include/mach/da8xx.h | 6 +++++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..231ff87 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1252,7 +1252,7 @@ console_initcall(da850_evm_console_init); static void __init da850_evm_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 606a6f2..362770c 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -561,7 +561,7 @@ console_initcall(mityomapl138_console_init); static void __init mityomapl138_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 67c38d0..c43a6c3 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -334,7 +334,7 @@ console_initcall(omapl138_hawk_console_init); static void __init omapl138_hawk_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..ebd0603 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1104,10 +1104,13 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .reset_device = &da8xx_wdt_device, }; -void __init da850_init(void) +void __init da850_init(struct da850_init_board_info *board) { unsigned int v; + if (board && board->ref_clk_rate) + ref_clk.rate = board->ref_clk_rate; + davinci_common_init(&davinci_soc_info_da850); da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..66efc5d 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -69,8 +69,12 @@ extern unsigned int da850_max_speed; #define DA8XX_AEMIF_CTL_BASE 0x68000000 #define DA8XX_ARM_RAM_BASE 0xffff0000 +struct da850_init_board_info { + unsigned long ref_clk_rate; +}; + void __init da830_init(void); -void __init da850_init(void); +void __init da850_init(struct da850_init_board_info *board); int da830_register_edma(struct edma_rsv_info *rsv); int da850_register_edma(struct edma_rsv_info *rsv[2]); -- 1.7.0.4 From aj4manu at gmail.com Sat May 28 00:10:52 2011 From: aj4manu at gmail.com (Ajay Cyril) Date: Sat, 28 May 2011 10:40:52 +0530 Subject: Help required for USB interfacin with EVM DM365. Message-ID: Hi I have a spectrum digital EVM for the DM365. It now runs the IPNC program. I dont know whether that is the default program that is supposed to run? Anyways, i need to configure the board to use it to stream video input through a USB connection(only,not Ethernet) on my laptop. So,if i follow the Getting Started Guide, install all the necessary softwares, that will remove the IPNC already loaded right? In that case,is there any demo which can stream video over USB? Please throw some light on this,i am completely new to working on this. I have a project to finish and I am stuck with no ideas as to how to proceed. Thanks. Ajay -------------- next part -------------- An HTML attachment was scrubbed... URL: From ravibabu at ti.com Sat May 28 04:17:00 2011 From: ravibabu at ti.com (B, Ravi) Date: Sat, 28 May 2011 14:47:00 +0530 Subject: Help required for USB interfacin with EVM DM365. In-Reply-To: References: Message-ID: AjayCyril If I understand your use case correctly, you want connect the DM365 EVM to laptop over USB, DM365 acts as USB camera/video stream device and stream the video data to laptop. If so you are looking for UVC (USB Video class) gadget driver on DM365 which stream the video data to the PC/Laptop over USB. Let me know my understanding correct ? Regards Ravi B ________________________________ From: davinci-linux-open-source-bounces+ravibabu=ti.com at linux.davincidsp.com [mailto:davinci-linux-open-source-bounces+ravibabu=ti.com at linux.davincidsp.com] On Behalf Of Ajay Cyril Sent: Saturday, May 28, 2011 10:41 AM To: davinci-linux-open-source at linux.davincidsp.com Subject: Help required for USB interfacin with EVM DM365. Hi I have a spectrum digital EVM for the DM365. It now runs the IPNC program. I dont know whether that is the default program that is supposed to run? Anyways, i need to configure the board to use it to stream video input through a USB connection(only,not Ethernet) on my laptop. So,if i follow the Getting Started Guide, install all the necessary softwares, that will remove the IPNC already loaded right? In that case,is there any demo which can stream video over USB? Please throw some light on this,i am completely new to working on this. I have a project to finish and I am stuck with no ideas as to how to proceed. Thanks. Ajay -------------- next part -------------- An HTML attachment was scrubbed... URL: From christian.riesch at omicron.at Mon May 30 03:23:35 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Mon, 30 May 2011 10:23:35 +0200 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> Message-ID: <95DC1AA8EC908B48939B72CF375AA5E3011CDD4483@alice.at.omicron.at> From Menon, Nishanth [mailto:nm at ti.com] > On Fri, May 27, 2011 at 02:20, Christian Riesch > wrote: >> From: Bob Dunlop >> >> Currently the input frequency of the SoC is hardcoded in the SoC >> specific da850.c file to 24 MHz. Since the SoC accepts input >> frequencies in a wide range from 12 to 50 MHz, boards with different >> oscillator/crystal frequencies may be built. >> >> This patch allows setting a different input frequency in the board >> specific files to support boards with oscillator/crystal frequencies >> other than 24 MHz. >Curious question: have you considered Documentation/power/opp.txt? Not >entirely sure if that will help, but I am curious to know if there are >reasons why it did not scale for you I took a look at it but I got the impression that the code in mach-davinci does not use your OPP library. Instead the structs that are used for the OPPs are defined locally in arch/arm/mach-davinci/da850.c. The frequency multiplier and divider values are hardcoded there and do not scale with the input clock frequency. Christian From subhasish at mistralsolutions.com Mon May 30 08:25:12 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 30 May 2011 18:55:12 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <20110524134338.GA13491@kroah.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105231730.06564.arnd@arndb.de> <7E10264BABCB4C1788DCD9715461BDE8@subhasishg> <201105241440.34854.arnd@arndb.de> <20110524134338.GA13491@kroah.com> Message-ID: <45A0E7B350CB4DF685D18EFE6EB1CB76@subhasishg> Greg, > There really are only 3 rules: > - proper license > - self-contained in a drivers/staging/DRIVER_NAME/ directory > - must properly build > >> Mainly, this includes having a patch that adds a single >> directory with all the driver files under drivers/staging. >> The driver must be able to compile without errors and you need >> a TODO file listing the remaining issues that prevent you >> from having a non-staging driver. > > Ah, forgot the TODO on the list of rules, I'll have to add that next > time. > How do I handle the headers. I have two header files in the include/linux/mfd. Should I submit them as a separate patch into mfd. These headers are also used by pruss uart implementation. From arnd at arndb.de Mon May 30 09:04:37 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Mon, 30 May 2011 16:04:37 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <45A0E7B350CB4DF685D18EFE6EB1CB76@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <20110524134338.GA13491@kroah.com> <45A0E7B350CB4DF685D18EFE6EB1CB76@subhasishg> Message-ID: <201105301604.38026.arnd@arndb.de> On Monday 30 May 2011, Subhasish Ghosh wrote: > > Ah, forgot the TODO on the list of rules, I'll have to add that next > > time. > > > How do I handle the headers. I have two header files in the > include/linux/mfd. > > Should I submit them as a separate patch into mfd. > > These headers are also used by pruss uart implementation. During the time when the drivers are in staging, all files need to be in the same directory, including the header and the drivers using it. They can get moved at the time when the driver gets turned into a regular non-staging driver. Arnd From subhasish at mistralsolutions.com Mon May 30 09:13:07 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 30 May 2011 19:43:07 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <201105301604.38026.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <20110524134338.GA13491@kroah.com> <45A0E7B350CB4DF685D18EFE6EB1CB76@subhasishg> <201105301604.38026.arnd@arndb.de> Message-ID: <2AF43CEDFDB649AE946E8F9F98A5AE48@subhasishg> > On Monday 30 May 2011, Subhasish Ghosh wrote: >> > Ah, forgot the TODO on the list of rules, I'll have to add that next >> > time. >> > >> How do I handle the headers. I have two header files in the >> include/linux/mfd. >> >> Should I submit them as a separate patch into mfd. >> >> These headers are also used by pruss uart implementation. > > During the time when the drivers are in staging, all files need to > be in the same directory, including the header and the drivers using it. > They can get moved at the time when the driver gets turned into > a regular non-staging driver. OK, so the UART implementation needs to be moved into staging as well ? And what about the boardfile changes, array of mfd_cells implementation etc. From greg at kroah.com Mon May 30 09:02:31 2011 From: greg at kroah.com (Greg KH) Date: Mon, 30 May 2011 22:02:31 +0800 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <45A0E7B350CB4DF685D18EFE6EB1CB76@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105231730.06564.arnd@arndb.de> <7E10264BABCB4C1788DCD9715461BDE8@subhasishg> <201105241440.34854.arnd@arndb.de> <20110524134338.GA13491@kroah.com> <45A0E7B350CB4DF685D18EFE6EB1CB76@subhasishg> Message-ID: <20110530140231.GA11831@kroah.com> On Mon, May 30, 2011 at 06:55:12PM +0530, Subhasish Ghosh wrote: > Greg, > > >There really are only 3 rules: > >- proper license > >- self-contained in a drivers/staging/DRIVER_NAME/ directory > >- must properly build > > > >>Mainly, this includes having a patch that adds a single > >>directory with all the driver files under drivers/staging. > >>The driver must be able to compile without errors and you need > >>a TODO file listing the remaining issues that prevent you > >>from having a non-staging driver. > > > >Ah, forgot the TODO on the list of rules, I'll have to add that next > >time. > > > How do I handle the headers. I have two header files in the > include/linux/mfd. Why would they be in there for a single driver? > Should I submit them as a separate patch into mfd. > > These headers are also used by pruss uart implementation. Then put them in the staging directory for your driver, why would anything outside of your driver need a .h file? thanks, greg k-h From subhasish at mistralsolutions.com Mon May 30 09:38:03 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 30 May 2011 20:08:03 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <20110530140231.GA11831@kroah.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105231730.06564.arnd@arndb.de> <7E10264BABCB4C1788DCD9715461BDE8@subhasishg> <201105241440.34854.arnd@arndb.de> <20110524134338.GA13491@kroah.com> <45A0E7B350CB4DF685D18EFE6EB1CB76@subhasishg> <20110530140231.GA11831@kroah.com> Message-ID: <98F790F0AABD4248A3DBFF4BA7D926B1@subhasishg> Greg, >> > >> How do I handle the headers. I have two header files in the >> include/linux/mfd. > > Why would they be in there for a single driver? > >> Should I submit them as a separate patch into mfd. >> >> These headers are also used by pruss uart implementation. > > Then put them in the staging directory for your driver, why would > anything outside of your driver need a .h file? > There are two header files in the include/linux/mfd/pruss.h & pruss_core.h the file pruss.h is included by the pruss_suart_api.c (tty uart driver) for some data structures, defines and function prototypes. From arnd at arndb.de Mon May 30 09:43:35 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Mon, 30 May 2011 16:43:35 +0200 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <2AF43CEDFDB649AE946E8F9F98A5AE48@subhasishg> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105301604.38026.arnd@arndb.de> <2AF43CEDFDB649AE946E8F9F98A5AE48@subhasishg> Message-ID: <201105301643.35997.arnd@arndb.de> On Monday 30 May 2011, Subhasish Ghosh wrote: > > > > During the time when the drivers are in staging, all files need to > > be in the same directory, including the header and the drivers using it. > > They can get moved at the time when the driver gets turned into > > a regular non-staging driver. > > OK, so the UART implementation needs to be moved into staging as well ? > > And what about the boardfile changes, array of mfd_cells implementation etc. All of that, too. Note that you are citing exactly the points that are the reason for the discussion we are having. The mfd_cells are the main concern for the cases where the information is not static. Arnd From subhasish at mistralsolutions.com Mon May 30 10:28:35 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Mon, 30 May 2011 20:58:35 +0530 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver In-Reply-To: <201105301643.35997.arnd@arndb.de> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <201105301604.38026.arnd@arndb.de> <2AF43CEDFDB649AE946E8F9F98A5AE48@subhasishg> <201105301643.35997.arnd@arndb.de> Message-ID: <4FF0051BF59A4F94A5CB49DFD97F9D4A@subhasishg> > On Monday 30 May 2011, Subhasish Ghosh wrote: >> > >> > During the time when the drivers are in staging, all files need to >> > be in the same directory, including the header and the drivers using >> > it. >> > They can get moved at the time when the driver gets turned into >> > a regular non-staging driver. >> >> OK, so the UART implementation needs to be moved into staging as well ? >> >> And what about the boardfile changes, array of mfd_cells implementation >> etc. > > All of that, too. > > Note that you are citing exactly the points that are the reason for the > discussion we are having. The mfd_cells are the main concern for the cases > where the information is not static. Yes, but by destroying the structure that I have currently in the platform files, I will be basically going backwards. If only the platform_data is what is going to change with some additions of sysfs attributes, then will it be correct to have the platform data as an empty pointer and keep the rest as is ? That way, I can keep the platform changes untouched and move just the drivers into staging. I cannot destroy the complete platform code as there are other drivers (PRUSS UIO) depending on them. From subhasish at mistralsolutions.com Tue May 31 02:45:38 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 31 May 2011 13:15:38 +0530 Subject: [PATCH v5 0/1] pruss mfd drivers. Message-ID: <1306827939-4133-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 5: ========= MFD: * Moved the MFD drivers into staging. * Added v4 code comments. 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 (1): drivers:staging:pruss: add pruss staging mfd driver. drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/pruss/Kconfig | 16 ++ drivers/staging/pruss/Makefile | 5 + drivers/staging/pruss/TODO | 14 + drivers/staging/pruss/pruss.c | 483 ++++++++++++++++++++++++++++++++++++ drivers/staging/pruss/pruss.h | 130 ++++++++++ drivers/staging/pruss/pruss_core.h | 132 ++++++++++ 8 files changed, 783 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/pruss/Kconfig create mode 100644 drivers/staging/pruss/Makefile create mode 100644 drivers/staging/pruss/TODO create mode 100644 drivers/staging/pruss/pruss.c create mode 100644 drivers/staging/pruss/pruss.h create mode 100644 drivers/staging/pruss/pruss_core.h -- 1.7.2.3 From subhasish at mistralsolutions.com Tue May 31 02:45:39 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Tue, 31 May 2011 13:15:39 +0530 Subject: [PATCH v5 1/1] drivers:staging:pruss: add pruss staging mfd driver. In-Reply-To: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> References: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1306827939-4133-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/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/pruss/Kconfig | 16 ++ drivers/staging/pruss/Makefile | 5 + drivers/staging/pruss/TODO | 14 + drivers/staging/pruss/pruss.c | 483 ++++++++++++++++++++++++++++++++++++ drivers/staging/pruss/pruss.h | 130 ++++++++++ drivers/staging/pruss/pruss_core.h | 132 ++++++++++ 8 files changed, 783 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/pruss/Kconfig create mode 100644 drivers/staging/pruss/Makefile create mode 100644 drivers/staging/pruss/TODO create mode 100644 drivers/staging/pruss/pruss.c create mode 100644 drivers/staging/pruss/pruss.h create mode 100644 drivers/staging/pruss/pruss_core.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5c8fcfc..9cbf201 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -179,5 +179,7 @@ source "drivers/staging/cptm1217/Kconfig" source "drivers/staging/ste_rmi4/Kconfig" +source "drivers/staging/pruss/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index d538863..2296c34 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ +obj-$(CONFIG_MFD_DA8XX_PRUSS) += pruss/ diff --git a/drivers/staging/pruss/Kconfig b/drivers/staging/pruss/Kconfig new file mode 100644 index 0000000..f426b67 --- /dev/null +++ b/drivers/staging/pruss/Kconfig @@ -0,0 +1,16 @@ +# +# TI's pruss staging drivers +# + +menu "Texas Instruments pruss drivers" + +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. +endmenu diff --git a/drivers/staging/pruss/Makefile b/drivers/staging/pruss/Makefile new file mode 100644 index 0000000..a9c657b --- /dev/null +++ b/drivers/staging/pruss/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for TI's pruss drivers +# + +obj-$(CONFIG_MFD_DA8XX_PRUSS) += pruss.o diff --git a/drivers/staging/pruss/TODO b/drivers/staging/pruss/TODO new file mode 100644 index 0000000..38a8ec7 --- /dev/null +++ b/drivers/staging/pruss/TODO @@ -0,0 +1,14 @@ +TODO: + +0. Functionality wise, everything works. + +1. Currently the plan is to add sysfs attributes for + a. prux/load + b. prux/unload + c. prux/run + d. prux/halt + These will add to a more dynamic firmware management for the PRU. + +2. But, not sure how the fdt will effect these entries. + +Please send patches to Greg Kroah-Hartman . diff --git a/drivers/staging/pruss/pruss.c b/drivers/staging/pruss/pruss.c new file mode 100644 index 0000000..8f1455b --- /dev/null +++ b/drivers/staging/pruss/pruss.c @@ -0,0 +1,483 @@ +/* + * 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 "pruss.h" +#include +#include +#include +#include + +struct pruss_priv { + struct device *dev; + spinlock_t lock; + struct resource *res; + struct clk *clk; + void __iomem *ioaddr; +}; + +int 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); + +int 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; + int 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 */ + for (i = 0; i < PRUSS_PRU0_RAM_SZ; i++) + iowrite32(0x0, &pruss_mmap->dram[pruss_num].dram_dt[i]); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_enable); + +/* Load the specified PRU with code */ +int 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; + int i; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + pruss_iram = (u32 __iomem *)&pruss_mmap->iram[pruss_num].iram_dt; + + 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); + +int 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); + +int 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); + +void pruss_writeb(struct device *dev, u32 offset, u8 datatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite8(datatowrite, addresstowrite); +} +EXPORT_SYMBOL_GPL(pruss_writeb); + +void pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addr; + u32 preg_data; + + addr = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread8(addr); + preg_data &= ~mask; + preg_data |= val; + iowrite8(preg_data, addr); + spin_unlock(&pruss->lock); +} +EXPORT_SYMBOL_GPL(pruss_rmwb); + +void pruss_readb(struct device *dev, u32 offset, u8 *datatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addrtoread; + + addrtoread = pruss->ioaddr + offset ; + *datatoread = ioread8(addrtoread); +} +EXPORT_SYMBOL_GPL(pruss_readb); + +void pruss_readb_multi(struct device *dev, u32 offset, + u8 *datatoread, u16 bytestoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u8 __iomem *addrtoread; + int i; + + addrtoread = pruss->ioaddr + offset; + + for (i = 0; i < bytestoread; i++) + *datatoread++ = ioread8(addrtoread++); +} +EXPORT_SYMBOL_GPL(pruss_readb_multi); + +void pruss_writel(struct device *dev, u32 offset, + u32 datatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite32(datatowrite, addresstowrite); +} +EXPORT_SYMBOL_GPL(pruss_writel); + +void pruss_writel_multi(struct device *dev, u32 offset, + u32 *datatowrite, u16 wordstowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *addresstowrite; + int i; + + addresstowrite = pruss->ioaddr + offset; + + for (i = 0; i < wordstowrite; i++) + iowrite32(*datatowrite++, addresstowrite++); +} +EXPORT_SYMBOL_GPL(pruss_writel_multi); + +void pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addr; + u32 preg_data; + + addr = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread32(addr); + preg_data &= ~mask; + preg_data |= val; + iowrite32(preg_data, addr); + spin_unlock(&pruss->lock); +} +EXPORT_SYMBOL_GPL(pruss_rmwl); + +void pruss_readl(struct device *dev, u32 offset, u32 *datatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addrtoread; + + addrtoread = pruss->ioaddr + offset; + *datatoread = ioread32(addrtoread); +} +EXPORT_SYMBOL_GPL(pruss_readl); + +void pruss_readl_multi(struct device *dev, u32 offset, + u32 *datatoread, u16 wordstoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *addrtoread; + int i; + + addrtoread = pruss->ioaddr + offset; + for (i = 0; i < wordstoread; i++) + *datatoread++ = ioread32(addrtoread++); +} +EXPORT_SYMBOL_GPL(pruss_readl_multi); + +void pruss_writew(struct device *dev, u32 offset, u16 datatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite16(datatowrite, addresstowrite); +} +EXPORT_SYMBOL_GPL(pruss_writew); + +void pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addr; + u32 preg_data; + + addr = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread16(addr); + preg_data &= ~mask; + preg_data |= val; + iowrite16(preg_data, addr); + spin_unlock(&pruss->lock); +} +EXPORT_SYMBOL_GPL(pruss_rmww); + +void pruss_readw(struct device *dev, u32 offset, u16 *datatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addrtoread; + + addrtoread = pruss->ioaddr + offset; + *datatoread = ioread16(addrtoread); +} +EXPORT_SYMBOL_GPL(pruss_readw); + +void pruss_idx_writel(struct device *dev, u32 offset, u32 value) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite32(value, addresstowrite); +} +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, num_devices = 0; + int i; + + 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/drivers/staging/pruss/pruss.h b/drivers/staging/pruss/pruss.h new file mode 100644 index 0000000..dd3b2d6 --- /dev/null +++ b/drivers/staging/pruss/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) + +int pruss_enable(struct device *dev, u8 pruss_num); + +int pruss_load(struct device *dev, u8 pruss_num, + u32 *pruss_code, u32 code_size_in_words); + +int pruss_run(struct device *dev, u8 pruss_num); + +int pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout); + +int pruss_disable(struct device *dev, u8 pruss_num); + +void pruss_writeb(struct device *dev, u32 offset, u8 pdatatowrite); + +void pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val); + +void pruss_readb(struct device *dev, u32 offset, u8 *pdatatoread); + +void pruss_readb_multi(struct device *dev, u32 offset, + u8 *pdatatoread, u16 bytestoread); + +void pruss_readl(struct device *dev, u32 offset, u32 *pdatatoread); + +void pruss_readl_multi(struct device *dev, u32 offset, + u32 *pdatatoread, u16 wordstoread); + +void pruss_writel(struct device *dev, u32 offset, u32 pdatatowrite); + +void pruss_writel_multi(struct device *dev, u32 offset, + u32 *pdatatowrite, u16 wordstowrite); + +void pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val); + +void pruss_idx_writel(struct device *dev, u32 offset, u32 value); + +void pruss_writew(struct device *dev, u32 offset, u16 datatowrite); + +void pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val); + +void pruss_readw(struct device *dev, u32 offset, u16 *pdatatoread); + +#endif /* End _PRUSS_H_ */ diff --git a/drivers/staging/pruss/pruss_core.h b/drivers/staging/pruss/pruss_core.h new file mode 100644 index 0000000..8ecb0e5 --- /dev/null +++ b/drivers/staging/pruss/pruss_core.h @@ -0,0 +1,132 @@ +/* + * 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_dram { + u8 dram_dt[512]; + u8 res[7680]; +}; + +struct pruss_iram { + u8 iram_dt[4096]; + u8 res[12288]; +}; + +struct pruss_map { + struct pruss_dram dram[2]; + struct pruss_intc_regs intc; + struct prusscore_regs core[2]; + struct pruss_iram iram[2]; +}; +#endif -- 1.7.2.3 From aj4manu at gmail.com Tue May 31 09:52:07 2011 From: aj4manu at gmail.com (Ajay Cyril) Date: Tue, 31 May 2011 20:22:07 +0530 Subject: Hi,UVC gadget on a DM365 EVM Message-ID: Hi Hello Everyone I am a student and only recently started working on a DM365 EVM. I seek your guidance and help . I am trying to achieve USB video streaming from a composite video in to my laptop over USB. Any help in this regard is much appreciated. I am thinking on the lines of using the UVC gadget in this regard. WIll that help me . Kindly give me some help . Any ideas,tips,general overview welcome. Only a beginner,please guide me. Thank You Ajay Cyril -------------- next part -------------- An HTML attachment was scrubbed... URL: From nsekhar at ti.com Tue May 31 12:09:48 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 31 May 2011 22:39:48 +0530 Subject: [RFC PATCH v4] Consolidate SRAM support In-Reply-To: <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> References: <20110512174546.GB8633@n2100.arm.linux.org.uk> <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> Message-ID: Hi Jean-Christophe, On Thu, May 26, 2011 at 06:32:57, Jean-Christophe PLAGNIOL-VILLARD wrote: > From: Russell King - ARM Linux > > 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. > > For this use the generic allocator and the newly introduce > gen_pool_add_virt and gen_pool_virt_to_phys > > 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 > Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD > Cc: Sekhar Nori > Cc: Kevin Hilman > Cc: Tony Lindgren > Cc: Sascha Hauer I tested this on DA850 using suspend-to-RAM and audio, it works well! > arch/arm/Kconfig | 2 + > 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 | 21 +++---- > arch/arm/mach-davinci/sram.c | 57 +++++-------------- > arch/arm/plat-mxc/include/mach/iram.h | 28 ++++++++-- > .../plat-mxc/{include/mach/iram.h => iram_alloc.c} | 40 ++++++++------ I had trouble applying the plat-mxc stuff on v3.0-rc1. Can you please check? I also found some check patch errors. Feel free to merge the attached patch. With these, please add my: Acked-by: Sekhar Nori Thanks, Sekhar ----8<------- diff --git a/arch/arm/plat-mxc/include/mach/iram.h b/arch/arm/plat-mxc/include/mach/iram.h index 8279c47..aae5e35 100644 --- a/arch/arm/plat-mxc/include/mach/iram.h +++ b/arch/arm/plat-mxc/include/mach/iram.h @@ -31,7 +31,7 @@ static inline void *iram_alloc(size_t size, phys_addr_t *phys) *phys = gen_pool_virt_to_phys(iram_pool, addr); - return (void*)addr; + return (void *) addr; } static inline void iram_free(void *addr, size_t size) diff --git a/arch/arm/plat-omap/include/plat/sram.h b/arch/arm/plat-omap/include/plat/sram.h index 52b9b5c..cc99397 100644 --- a/arch/arm/plat-omap/include/plat/sram.h +++ b/arch/arm/plat-omap/include/plat/sram.h @@ -27,7 +27,7 @@ extern struct gen_pool *omap_gen_pool; size_t _sz = size; \ void *_sram = gen_pool_alloc(omap_gen_pool, _sz); \ _res = (_sram ? fncpy(_sram, &(funcp), _sz) : NULL); \ - if (!_res) \ + if (!_res) \ pr_err("Not enough space in SRAM\n"); \ _res; \ }) diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index db486cc..444176e 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -153,7 +153,7 @@ static int __devinit pruss_probe(struct platform_device *dev) goto out_free; } - gdev->sram_vaddr = (void*)gen_pool_alloc(davinci_gen_pool, sram_pool_sz); + gdev->sram_vaddr = (void *)gen_pool_alloc(davinci_gen_pool, sram_pool_sz); if (!gdev->sram_vaddr) { dev_err(&dev->dev, "Could not allocate SRAM pool\n"); goto out_free; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index dd305a2..1c383ab 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -239,7 +239,7 @@ static int allocate_sram(struct snd_pcm_substream *substream, unsigned size, return 0; ppcm->period_bytes_max = size; - iram_virt = (void*)gen_pool_alloc(davinci_gen_pool, size); + iram_virt = (void *)gen_pool_alloc(davinci_gen_pool, size); if (!iram_virt) goto exit1; iram_phys = gen_pool_virt_to_phys(davinci_gen_pool, From linux at arm.linux.org.uk Tue May 31 16:21:42 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Tue, 31 May 2011 22:21:42 +0100 Subject: [RFC PATCH v4] Consolidate SRAM support In-Reply-To: References: <20110512174546.GB8633@n2100.arm.linux.org.uk> <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> Message-ID: <20110531212142.GA6700@n2100.arm.linux.org.uk> On Tue, May 31, 2011 at 10:39:48PM +0530, Nori, Sekhar wrote: > I also found some check patch errors. Feel free to merge > the attached patch. And this introduces another style issue... > diff --git a/arch/arm/plat-mxc/include/mach/iram.h b/arch/arm/plat-mxc/include/mach/iram.h > index 8279c47..aae5e35 100644 > --- a/arch/arm/plat-mxc/include/mach/iram.h > +++ b/arch/arm/plat-mxc/include/mach/iram.h > @@ -31,7 +31,7 @@ static inline void *iram_alloc(size_t size, phys_addr_t *phys) > > *phys = gen_pool_virt_to_phys(iram_pool, addr); > > - return (void*)addr; > + return (void *) addr; The style here is: return (void *)addr; From bengardiner at nanometrics.ca Tue May 31 16:46:40 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 31 May 2011 17:46:40 -0400 Subject: [PATCH v2 0/3] davinci: ioremap SRAM instead of iotables Message-ID: The davinci platforms are mapping their io regions using iotables. This patch series converts them to mapping using ioremap. This version of the series is based on-top-of '[RFC PATCH v4] Consolidate SRAM support' from Russell King and Jean-Christophe PLAGNIOL-VILLARD. The davinci sram init is first changed to ioremap the regions specified by each of the soc_infos; then the iotables are each removed; then the SRAM_VIRT definition is removed. Finally, the da850's sram region is changed from the ARM local RAM region to the Shared RAM region. This change is needed to support mcasp ping-pong buffers on da850. Suspend was tested with rtcwake and was found to work. Ben Gardiner (2): [v2] davinci: sram: ioremap the davinci_soc_info specified sram regions [v2] davinci: da850-dm646x: remove the SRAM_VIRT iotable entry Subhasish Ghosh (1): [v2] davinci: da850: changed SRAM allocator to shared ram. arch/arm/mach-davinci/da850.c | 10 ++-------- arch/arm/mach-davinci/dm355.c | 6 ------ arch/arm/mach-davinci/dm365.c | 6 ------ arch/arm/mach-davinci/dm644x.c | 6 ------ arch/arm/mach-davinci/dm646x.c | 6 ------ arch/arm/mach-davinci/include/mach/common.h | 2 -- arch/arm/mach-davinci/include/mach/da8xx.h | 1 + arch/arm/mach-davinci/sram.c | 10 ++++++++-- 8 files changed, 11 insertions(+), 36 deletions(-) -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 31 16:46:41 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 31 May 2011 17:46:41 -0400 Subject: [PATCH 1/3] [v2] davinci: sram: ioremap the davinci_soc_info specified sram regions In-Reply-To: References: Message-ID: <3406118d1598e5e279108dd94c63ced1fa8d02bc.1306877621.git.bengardiner@nanometrics.ca> The current davinci init sets up SRAM in iotables. There has been an observed failure to boot a da850 with 128K specified in the iotable. Make the davinci sram allocator -- now based on RMK's consolidated SRAM support -- do an ioremap of the region specified by the entries in davinci_soc_info before registering with pv_pool_create(). This commit breaks runtime of davinci boards since the regions that the sram init is now trying to ioremap have been iomapped by their iotable entries. The iotable entries will be removed in the patches to come. Signed-off-by: Ben Gardiner CC: Sekhar Nori --- Changes since v1: * return -ENOMEM if ioremap fails (Sekhar Nori) --- arch/arm/mach-davinci/sram.c | 10 ++++++++-- 1 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c index 2c53db2..68b05d5 100644 --- a/arch/arm/mach-davinci/sram.c +++ b/arch/arm/mach-davinci/sram.c @@ -8,6 +8,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +#include #include #include @@ -25,6 +26,7 @@ EXPORT_SYMBOL_GPL(davinci_gen_pool); */ static int __init sram_init(void) { + void *addr; unsigned len = davinci_soc_info.sram_len; if (!len) @@ -36,8 +38,12 @@ static int __init sram_init(void) if (!davinci_gen_pool) return -ENOMEM; - WARN_ON(gen_pool_add_virt(davinci_gen_pool, SRAM_VIRT, - davinci_soc_info.sram_phys, len, -1)); + addr = ioremap(davinci_soc_info.sram_phys, len); + if (!addr) + return -ENOMEM; + if (WARN_ON(gen_pool_add_virt(davinci_gen_pool, addr, + davinci_soc_info.sram_phys, len, -1))) + iounmap(addr); return 0; } -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 31 16:46:42 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 31 May 2011 17:46:42 -0400 Subject: [PATCH 2/3] [v2] davinci: da850-dm646x: remove the SRAM_VIRT iotable entry In-Reply-To: References: Message-ID: The sram regions defined for da850-dm646x in their iotable entries are also defined in their davinci_soc_info's. Remove this duplicate information which is now uneccessary since sram init will ioremap the regions defined by their davinci_soc_info's. Since this removal completely removes all uses of SRAM_VIRT, also remove the SRAM_VIRT definition. Signed-off-by: Ben Gardiner Reviewed-by: Sergei Shtylyov --- Changes since v1: * squashed patches 3-8 in v1 series into this patch (Sergei Shtylyov) --- arch/arm/mach-davinci/da850.c | 6 ------ arch/arm/mach-davinci/dm355.c | 6 ------ arch/arm/mach-davinci/dm365.c | 6 ------ arch/arm/mach-davinci/dm644x.c | 6 ------ arch/arm/mach-davinci/dm646x.c | 6 ------ arch/arm/mach-davinci/include/mach/common.h | 2 -- 6 files changed, 0 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 5c2bf3b..904ede9 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -725,12 +725,6 @@ static struct map_desc da850_io_desc[] = { .length = DA8XX_CP_INTC_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(DA8XX_ARM_RAM_BASE), - .length = SZ_8K, - .type = MT_DEVICE - }, }; static u32 da850_psc_bases[] = { DA8XX_PSC0_BASE, DA8XX_PSC1_BASE }; diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 9bda687..94f44d3 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -757,12 +757,6 @@ static struct map_desc dm355_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index d306034..58f5b0a 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -970,12 +970,6 @@ static struct map_desc dm365_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; static struct resource dm365_ks_resources[] = { diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 3949ed7..11e6481 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -663,12 +663,6 @@ static struct map_desc dm644x_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00008000), - .length = SZ_16K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index a4365f7..57d697e 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -747,12 +747,6 @@ static struct map_desc dm646x_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index 665d049..16b5ec5 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -86,8 +86,6 @@ extern struct davinci_soc_info davinci_soc_info; extern void davinci_common_init(struct davinci_soc_info *soc_info); extern void davinci_init_ide(void); -/* standard place to map on-chip SRAMs; they *may* support DMA */ -#define SRAM_VIRT 0xfffe0000 #define SRAM_SIZE SZ_128K #endif /* __ARCH_ARM_MACH_DAVINCI_COMMON_H */ -- 1.7.4.1 From bengardiner at nanometrics.ca Tue May 31 16:46:43 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 31 May 2011 17:46:43 -0400 Subject: [PATCH 3/3] [v2] davinci: da850: changed SRAM allocator to shared ram. In-Reply-To: References: Message-ID: <4bae1323133ac01c9e19ceefb366aa9a46365840.1306877621.git.bengardiner@nanometrics.ca> From: Subhasish Ghosh This patch modifies the sram allocator to allocate memory from the DA8XX shared RAM. Signed-off-by: Subhasish Ghosh [rebased onto consolidated SRAM patches] Signed-off-by: Ben Gardiner Reviewed-by: Sergei Shtylyov --- Changes since v1: * re-sorted the new DA8XX_SHARED_RAM_BASE definition in da8xx.h to between DA8XX_AEMIF_CTL_BASE and DA8XX_ARM_RAM_BASE (Sergei Shtylyov). --- arch/arm/mach-davinci/da850.c | 4 ++-- arch/arm/mach-davinci/include/mach/da8xx.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 904ede9..4781230 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1093,8 +1093,8 @@ 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_phys = DA8XX_ARM_RAM_BASE, - .sram_len = SZ_8K, + .sram_phys = DA8XX_SHARED_RAM_BASE, + .sram_len = SZ_128K, .reset_device = &da8xx_wdt_device, }; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..b67499f 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -67,6 +67,7 @@ extern unsigned int da850_max_speed; #define DA8XX_AEMIF_CS2_BASE 0x60000000 #define DA8XX_AEMIF_CS3_BASE 0x62000000 #define DA8XX_AEMIF_CTL_BASE 0x68000000 +#define DA8XX_SHARED_RAM_BASE 0x80000000 #define DA8XX_ARM_RAM_BASE 0xffff0000 void __init da830_init(void); -- 1.7.4.1