diff options
Diffstat (limited to 'drivers')
33 files changed, 1620 insertions, 241 deletions
diff --git a/drivers/dma/pxp/pxp_dma_v2.c b/drivers/dma/pxp/pxp_dma_v2.c index 87b8f558ae8c..b74f62cac032 100644 --- a/drivers/dma/pxp/pxp_dma_v2.c +++ b/drivers/dma/pxp/pxp_dma_v2.c @@ -264,6 +264,9 @@ static void pxp_set_ctrl(struct pxps *pxp) case PXP_PIX_FMT_YUV422P: fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV422; break; + case PXP_PIX_FMT_UYVY: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__UYVY1P422; + break; default: fmt_ctrl = 0; } @@ -654,7 +657,7 @@ static void pxp_set_csc(struct pxps *pxp) /* CSC1 - YUV->RGB */ __raw_writel(0x84ab01f0, pxp->base + HW_PXP_CSC1_COEF0); - __raw_writel(0x01230204, pxp->base + HW_PXP_CSC1_COEF1); + __raw_writel(0x01980204, pxp->base + HW_PXP_CSC1_COEF1); __raw_writel(0x0730079c, pxp->base + HW_PXP_CSC1_COEF2); /* CSC2 - Bypass */ diff --git a/drivers/media/video/mxc/capture/Kconfig b/drivers/media/video/mxc/capture/Kconfig index a7b14cb23b9b..febe9d874fb8 100644 --- a/drivers/media/video/mxc/capture/Kconfig +++ b/drivers/media/video/mxc/capture/Kconfig @@ -13,7 +13,7 @@ config VIDEO_MXC_EMMA_CAMERA default y config VIDEO_MXC_CSI_CAMERA - tristate "MX25 CSI camera support" + tristate "CSI camera support" depends on !VIDEO_MXC_EMMA_CAMERA config VIDEO_MXC_CSI_DMA diff --git a/drivers/media/video/mxc/capture/csi_v4l2_capture.c b/drivers/media/video/mxc/capture/csi_v4l2_capture.c index eb824ddf5eb3..ada6d5e8afbd 100644 --- a/drivers/media/video/mxc/capture/csi_v4l2_capture.c +++ b/drivers/media/video/mxc/capture/csi_v4l2_capture.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -15,7 +15,7 @@ * @file drivers/media/video/mxc/capture/csi_v4l2_capture.c * This file is derived from mxc_v4l2_capture.c * - * @brief MX25 Video For Linux 2 driver + * @brief Video For Linux 2 capture driver * * @ingroup MXC_V4L2_CAPTURE */ @@ -61,6 +61,198 @@ static struct v4l2_int_device csi_v4l2_int_device = { }, }; +/* Callback function triggered after PxP receives an EOF interrupt */ +static void pxp_dma_done(void *arg) +{ + struct pxp_tx_desc *tx_desc = to_tx_desc(arg); + struct dma_chan *chan = tx_desc->txd.chan; + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + cam_data *cam = pxp_chan->client; + + /* This call will signal wait_for_completion_timeout() */ + complete(&cam->pxp_tx_cmpl); +} + +static bool chan_filter(struct dma_chan *chan, void *arg) +{ + if (imx_dma_is_pxp(chan)) + return true; + else + return false; +} + +/* Function to request PXP DMA channel */ +static int pxp_chan_init(cam_data *cam) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + + /* Request a free channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + chan = dma_request_channel(mask, chan_filter, NULL); + if (!chan) { + pr_err("Unsuccessfully request channel!\n"); + return -EBUSY; + } + + cam->pxp_chan = to_pxp_channel(chan); + cam->pxp_chan->client = cam; + + init_completion(&cam->pxp_tx_cmpl); + + return 0; +} + +/* + * Function to call PxP DMA driver and send our new V4L2 buffer + * through the PxP and PxP will process this buffer in place. + * Note: This is a blocking call, so upon return the PxP tx should be complete. + */ +static int pxp_process_update(cam_data *cam) +{ + dma_cookie_t cookie; + struct scatterlist *sg = cam->sg; + struct dma_chan *dma_chan; + struct pxp_tx_desc *desc; + struct dma_async_tx_descriptor *txd; + struct pxp_config_data *pxp_conf = &cam->pxp_conf; + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + int i, ret; + int length; + + pr_debug("Starting PxP Send Buffer\n"); + + /* First, check to see that we have acquired a PxP Channel object */ + if (cam->pxp_chan == NULL) { + /* + * PxP Channel has not yet been created and initialized, + * so let's go ahead and try + */ + ret = pxp_chan_init(cam); + if (ret) { + /* + * PxP channel init failed, and we can't use the + * PxP until the PxP DMA driver has loaded, so we abort + */ + pr_err("PxP chan init failed\n"); + return -ENODEV; + } + } + + /* + * Init completion, so that we can be properly informed of + * the completion of the PxP task when it is done. + */ + init_completion(&cam->pxp_tx_cmpl); + + dma_chan = &cam->pxp_chan->dma_chan; + + txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg, 2, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT); + if (!txd) { + pr_err("Error preparing a DMA transaction descriptor.\n"); + return -EIO; + } + + txd->callback_param = txd; + txd->callback = pxp_dma_done; + + /* + * Configure PxP for processing of new v4l2 buf + */ + pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_UYVY; + pxp_conf->s0_param.color_key = -1; + pxp_conf->s0_param.color_key_enable = false; + pxp_conf->s0_param.width = cam->v2f.fmt.pix.width; + pxp_conf->s0_param.height = cam->v2f.fmt.pix.height; + + pxp_conf->ol_param[0].combine_enable = false; + + proc_data->srect.top = 0; + proc_data->srect.left = 0; + proc_data->srect.width = pxp_conf->s0_param.width; + proc_data->srect.height = pxp_conf->s0_param.height; + + proc_data->drect.top = 0; + proc_data->drect.left = 0; + proc_data->drect.width = proc_data->srect.width; + proc_data->drect.height = proc_data->srect.height; + proc_data->scaling = 0; + proc_data->hflip = 0; + proc_data->vflip = 0; + proc_data->rotate = 0; + proc_data->bgcolor = 0; + + pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_RGB565; + pxp_conf->out_param.width = proc_data->drect.width; + pxp_conf->out_param.height = proc_data->drect.height; + + if (cam->rotation >= IPU_ROTATE_90_RIGHT) + pxp_conf->out_param.stride = pxp_conf->out_param.height; + else + pxp_conf->out_param.stride = pxp_conf->out_param.width; + + desc = to_tx_desc(txd); + length = desc->len; + for (i = 0; i < length; i++) { + if (i == 0) {/* S0 */ + memcpy(&desc->proc_data, proc_data, + sizeof(struct pxp_proc_data)); + pxp_conf->s0_param.paddr = sg_dma_address(&sg[0]); + memcpy(&desc->layer_param.s0_param, &pxp_conf->s0_param, + sizeof(struct pxp_layer_param)); + } else if (i == 1) { + pxp_conf->out_param.paddr = sg_dma_address(&sg[1]); + memcpy(&desc->layer_param.out_param, + &pxp_conf->out_param, + sizeof(struct pxp_layer_param)); + } + + desc = desc->next; + } + + /* Submitting our TX starts the PxP processing task */ + cookie = txd->tx_submit(txd); + if (cookie < 0) { + pr_err("Error sending FB through PxP\n"); + return -EIO; + } + + cam->txd = txd; + + /* trigger PxP */ + dma_async_issue_pending(dma_chan); + + return 0; +} + +static int pxp_complete_update(cam_data *cam) +{ + int ret; + /* + * Wait for completion event, which will be set + * through our TX callback function. + */ + ret = wait_for_completion_timeout(&cam->pxp_tx_cmpl, HZ / 10); + if (ret <= 0) { + pr_warning("PxP operation failed due to %s\n", + ret < 0 ? "user interrupt" : "timeout"); + dma_release_channel(&cam->pxp_chan->dma_chan); + cam->pxp_chan = NULL; + return ret ? : -ETIMEDOUT; + } + + dma_release_channel(&cam->pxp_chan->dma_chan); + cam->pxp_chan = NULL; + + pr_debug("TX completed\n"); + + return 0; +} + /*! * Camera V4l2 callback function. * @@ -666,6 +858,23 @@ static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) buf->flags = frame->buffer.flags; buf->m = cam->frame[frame->index].buffer.m; + /* + * Note: + * If want to do preview on LCD, use PxP CSC to convert from UYVY + * to RGB565; but for encoding, usually we don't use RGB format. + */ + if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { + /* PxP processes it in place */ + sg_dma_address(&cam->sg[0]) = buf->m.offset; + sg_dma_address(&cam->sg[1]) = buf->m.offset; + retval = pxp_process_update(cam); + if (retval) { + pr_err("Unable to submit PxP update task.\n"); + return retval; + } + pxp_complete_update(cam); + } + return retval; } @@ -1186,8 +1395,8 @@ static void init_camera_struct(cam_data *cam) /* Default everything to 0 */ memset(cam, 0, sizeof(cam_data)); - init_MUTEX(&cam->param_lock); - init_MUTEX(&cam->busy_lock); + sema_init(&cam->param_lock, 1); + sema_init(&cam->busy_lock, 1); cam->video_dev = video_device_alloc(); if (cam->video_dev == NULL) @@ -1382,6 +1591,7 @@ static void csi_v4l2_master_detach(struct v4l2_int_device *slave) */ static __init int camera_init(void) { + struct scatterlist *sg; u8 err = 0; /* Register the device driver structure. */ @@ -1430,6 +1640,11 @@ static __init int camera_init(void) pr_debug(" Video device registered: %s #%d\n", g_cam->video_dev->name, g_cam->video_dev->minor); + g_cam->pxp_chan = NULL; + /* Initialize Scatter-gather list containing 2 buffer addresses. */ + sg = g_cam->sg; + sg_init_table(sg, 2); + return err; } diff --git a/drivers/media/video/mxc/capture/fsl_csi.c b/drivers/media/video/mxc/capture/fsl_csi.c index dba35c4499e2..33a82242e95e 100644 --- a/drivers/media/video/mxc/capture/fsl_csi.c +++ b/drivers/media/video/mxc/capture/fsl_csi.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -20,6 +20,7 @@ */ #include <linux/types.h> #include <linux/init.h> +#include <linux/platform_device.h> #include <linux/device.h> #include <linux/err.h> #include <linux/interrupt.h> @@ -27,11 +28,14 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/clk.h> +#include <linux/sched.h> #include <mach/clock.h> #include "mxc_v4l2_capture.h" #include "fsl_csi.h" +void __iomem *csi_regbase; +static int irq_nr; static bool g_csi_mclk_on; static csi_irq_callback_t g_callback; static void *g_callback_data; @@ -164,7 +168,7 @@ void csi_start_callback(void *data) { cam_data *cam = (cam_data *) data; - if (request_irq(MXC_INT_CSI, csi_irq_handler, 0, "csi", cam) < 0) + if (request_irq(irq_nr, csi_irq_handler, 0, "csi", cam) < 0) pr_debug("CSI error: irq request fail\n"); } @@ -174,7 +178,7 @@ void csi_stop_callback(void *data) { cam_data *cam = (cam_data *) data; - free_irq(MXC_INT_CSI, cam); + free_irq(irq_nr, cam); } EXPORT_SYMBOL(csi_stop_callback); @@ -254,10 +258,32 @@ void csi_mclk_disable(void) __raw_writel(__raw_readl(CSI_CSICR1) & ~BIT_MCLKEN, CSI_CSICR1); } -int32_t __init csi_init_module(void) +static int __devinit csi_probe(struct platform_device *pdev) { int ret = 0; struct clk *per_clk; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "No csi irq found.\n"); + ret = -ENODEV; + goto err; + } + irq_nr = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No csi base address found.\n"); + ret = -ENODEV; + goto err; + } + csi_regbase = ioremap(res->start, resource_size(res)); + if (!csi_regbase) { + dev_err(&pdev->dev, "ioremap failed with csi base\n"); + ret = -ENOMEM; + goto err; + } csihw_reset(); csi_init_interface(); @@ -271,12 +297,34 @@ int32_t __init csi_init_module(void) clk_enable(per_clk); csi_mclk_recalc(&csi_mclk); +err: return ret; } -void __exit csi_cleanup_module(void) +static int __devexit csi_remove(struct platform_device *pdev) { clk_disable(&csi_mclk); + iounmap(csi_regbase); + + return 0; +} + +static struct platform_driver csi_driver = { + .driver = { + .name = "fsl_csi", + }, + .probe = csi_probe, + .remove = __devexit_p(csi_remove), +}; + +int32_t __init csi_init_module(void) +{ + return platform_driver_register(&csi_driver); +} + +void __exit csi_cleanup_module(void) +{ + platform_driver_unregister(&csi_driver); } module_init(csi_init_module); diff --git a/drivers/media/video/mxc/capture/fsl_csi.h b/drivers/media/video/mxc/capture/fsl_csi.h index 00c389892224..8dfce286fe53 100644 --- a/drivers/media/video/mxc/capture/fsl_csi.h +++ b/drivers/media/video/mxc/capture/fsl_csi.h @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -102,23 +102,22 @@ #define CSI_MCLK_I2C 8 #endif -#define CSI_CSICR1 (IO_ADDRESS(CSI_BASE_ADDR)) -#define CSI_CSICR2 (IO_ADDRESS(CSI_BASE_ADDR + 0x4)) -#define CSI_CSICR3 (IO_ADDRESS(CSI_BASE_ADDR + 0x8)) -#define CSI_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0xC)) -#define CSI_CSIRXFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x10)) -#define CSI_CSIRXCNT (IO_ADDRESS(CSI_BASE_ADDR + 0x14)) -#define CSI_CSISR (IO_ADDRESS(CSI_BASE_ADDR + 0x18)) - -#define CSI_CSIDBG (IO_ADDRESS(CSI_BASE_ADDR + 0x1C)) -#define CSI_CSIDMASA_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x20)) -#define CSI_CSIDMATS_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x24)) -#define CSI_CSIDMASA_FB1 (IO_ADDRESS(CSI_BASE_ADDR + 0x28)) -#define CSI_CSIDMASA_FB2 (IO_ADDRESS(CSI_BASE_ADDR + 0x2C)) -#define CSI_CSIFBUF_PARA (IO_ADDRESS(CSI_BASE_ADDR + 0x30)) -#define CSI_CSIIMAG_PARA (IO_ADDRESS(CSI_BASE_ADDR + 0x34)) - -#define CSI_CSIRXFIFO_PHYADDR (CSI_BASE_ADDR + 0x10) +extern void __iomem *csi_regbase; +#define CSI_CSICR1 (csi_regbase) +#define CSI_CSICR2 (csi_regbase + 0x4) +#define CSI_CSICR3 (csi_regbase + 0x8) +#define CSI_STATFIFO (csi_regbase + 0xC) +#define CSI_CSIRXFIFO (csi_regbase + 0x10) +#define CSI_CSIRXCNT (csi_regbase + 0x14) +#define CSI_CSISR (csi_regbase + 0x18) + +#define CSI_CSIDBG (csi_regbase + 0x1C) +#define CSI_CSIDMASA_STATFIFO (csi_regbase + 0x20) +#define CSI_CSIDMATS_STATFIFO (csi_regbase + 0x24) +#define CSI_CSIDMASA_FB1 (csi_regbase + 0x28) +#define CSI_CSIDMASA_FB2 (csi_regbase + 0x2C) +#define CSI_CSIFBUF_PARA (csi_regbase + 0x30) +#define CSI_CSIIMAG_PARA (csi_regbase + 0x34) static inline void csi_clear_status(unsigned long status) { diff --git a/drivers/media/video/mxc/capture/mt9v111.c b/drivers/media/video/mxc/capture/mt9v111.c index 4305c56c82d9..3ae65db04486 100644 --- a/drivers/media/video/mxc/capture/mt9v111.c +++ b/drivers/media/video/mxc/capture/mt9v111.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -879,7 +879,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s) gpio_sensor_active(); - set_mclk_rate(&clock_rate); + set_mclk_rate(&clock_rate, 0); mt9v111_rate_cal(&reset_frame_rate, clock_rate); mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg); diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h index 27f8a78a93b3..034fd168290f 100644 --- a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h @@ -31,6 +31,10 @@ #include <linux/list.h> #include <linux/ipu.h> #include <linux/mxc_v4l2.h> +#include <linux/completion.h> +#include <linux/dmaengine.h> +#include <linux/pxp_dma.h> +#include <mach/dma.h> #include <mach/ipu-v3.h> #include <media/v4l2-dev.h> @@ -205,6 +209,14 @@ typedef struct _cam_data { struct v4l2_int_device *self; int sensor_index; void *ipu; + + /* v4l2 buf elements related to PxP DMA */ + struct completion pxp_tx_cmpl; + struct pxp_channel *pxp_chan; + struct pxp_config_data pxp_conf; + struct dma_async_tx_descriptor *txd; + dma_cookie_t cookie; + struct scatterlist sg[2]; } cam_data; struct sensor_data { @@ -232,11 +244,5 @@ struct sensor_data { void (*io_init)(void); }; -#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \ - || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \ - || defined(CONFIG_VIDEO_MXC_CSI_CAMERA) -void set_mclk_rate(uint32_t *p_mclk_freq); -#else void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi); -#endif #endif /* __MXC_V4L2_CAPTURE_H__ */ diff --git a/drivers/media/video/mxc/capture/ov2640.c b/drivers/media/video/mxc/capture/ov2640.c index a0a050bca2e9..24ebd5027ff1 100644 --- a/drivers/media/video/mxc/capture/ov2640.c +++ b/drivers/media/video/mxc/capture/ov2640.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -801,7 +801,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s) pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); - set_mclk_rate(&ov2640_data.mclk); + set_mclk_rate(&ov2640_data.mclk, 0); return ov2640_init_mode(sensor); } diff --git a/drivers/media/video/mxc/capture/sensor_clock.c b/drivers/media/video/mxc/capture/sensor_clock.c index 8004aeee5dc2..150659fa5dc0 100644 --- a/drivers/media/video/mxc/capture/sensor_clock.c +++ b/drivers/media/video/mxc/capture/sensor_clock.c @@ -26,31 +26,6 @@ #include <mach/hardware.h> #include <asm/mach-types.h> -#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \ - || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \ - || defined(CONFIG_VIDEO_MXC_CSI_CAMERA) -/* - * set_mclk_rate - * - * @param p_mclk_freq mclk frequence - * - */ -void set_mclk_rate(uint32_t *p_mclk_freq) -{ - struct clk *clk; - uint32_t freq = 0; - - clk = clk_get(NULL, "csi_clk"); - - freq = clk_round_rate(clk, *p_mclk_freq); - clk_set_rate(clk, freq); - - *p_mclk_freq = freq; - - clk_put(clk); - pr_debug("mclk frequency = %d\n", *p_mclk_freq); -} -#else /* * set_mclk_rate * @@ -81,6 +56,8 @@ void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi) pr_err("invalid csi num %d\n", csi); return; }; + } else if (cpu_is_mx25() || cpu_is_mx6sl()) { /* only has CSI0 */ + mclk = "csi_clk"; } else { if (csi == 0) { mclk = "csi_mclk1"; @@ -102,7 +79,6 @@ void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi) clk_put(clk); pr_debug("%s frequency = %d\n", mclk, *p_mclk_freq); } -#endif /* Exported symbols for modules. */ EXPORT_SYMBOL(set_mclk_rate); diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 366d27a92286..388c4b39570f 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -861,6 +861,7 @@ static int mxc_vout_open(struct file *file) vout->win_pos.x = 0; vout->win_pos.y = 0; + vout->release = true; } file->private_data = vout; @@ -973,6 +974,7 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout) int is_1080p_stream; size_t size; struct ipu_task *ipu_task = &vout->task; + struct ipu_crop *icrop = &ipu_task->input.crop; struct ipu_task *vdoa_task = &vout->vdoa_task; u32 deinterlace = 0; u32 in_fmt; @@ -981,15 +983,23 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout) deinterlace = 1; memset(vdoa_task, 0, sizeof(*vdoa_task)); - memcpy(&vdoa_task->input, &ipu_task->input, sizeof(ipu_task->input)); vdoa_task->output.format = IPU_PIX_FMT_NV12; - vdoa_task->output.width = ipu_task->input.crop.w; - vdoa_task->output.height = ipu_task->input.crop.h; - vdoa_task->output.crop.w = ipu_task->input.crop.w; - vdoa_task->output.crop.h = ipu_task->input.crop.h; - - size = PAGE_ALIGN(ipu_task->input.crop.w * - ipu_task->input.crop.h * + memcpy(&vdoa_task->input, &ipu_task->input, + sizeof(ipu_task->input)); + if ((icrop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (icrop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { + vdoa_task->input.crop.w = + ALIGN(icrop->w, IPU_PIX_FMT_TILED_NV12_MBALIGN); + vdoa_task->input.crop.h = + ALIGN(icrop->h, IPU_PIX_FMT_TILED_NV12_MBALIGN); + } + vdoa_task->output.width = vdoa_task->input.crop.w; + vdoa_task->output.height = vdoa_task->input.crop.h; + vdoa_task->output.crop.w = vdoa_task->input.crop.w; + vdoa_task->output.crop.h = vdoa_task->input.crop.h; + + size = PAGE_ALIGN(vdoa_task->input.crop.w * + vdoa_task->input.crop.h * fmt_to_bpp(vdoa_task->output.format)/8); if (size > vout->vdoa_work.size) { if (vout->vdoa_work.vaddr) @@ -1029,6 +1039,7 @@ static int mxc_vout_try_task(struct mxc_vout_output *vout) u32 o_height = 0; u32 ocrop_h = 0; bool tiled_fmt = false; + bool tiled_need_pp = false; vout->vdoa_1080p = CHECK_TILED_1080P_DISPLAY(vout); if (vout->vdoa_1080p) { @@ -1041,10 +1052,17 @@ static int mxc_vout_try_task(struct mxc_vout_output *vout) if ((IPU_PIX_FMT_TILED_NV12 == input->format) || (IPU_PIX_FMT_TILED_NV12F == input->format)) { - crop->w -= crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN; - crop->h -= crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN; - crop->pos.x -= crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN; - crop->pos.y -= crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN; + if ((input->width % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (input->height % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { + v4l2_err(vout->vfd->v4l2_dev, + "ERR: tiled fmt needs 16 pixel align.\n"); + return -EINVAL; + } + if ((crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) + tiled_need_pp = true; } else { crop->w -= crop->w % 8; crop->h -= crop->h % 8; @@ -1072,7 +1090,8 @@ static int mxc_vout_try_task(struct mxc_vout_output *vout) if ((IPU_PIX_FMT_TILED_NV12 == input->format) || (IPU_PIX_FMT_TILED_NV12F == input->format)) { /* check resize/rotate/flip, or csc task */ - if (!((IPU_ROTATE_NONE != output->rotate) || + if (!(tiled_need_pp || + (IPU_ROTATE_NONE != output->rotate) || (input->crop.w != output->crop.w) || (input->crop.h != output->crop.h) || (!vout->disp_support_csc && diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index e9322477ff6d..a07db1b0c070 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -41,6 +41,7 @@ #include <linux/mfd/mxc-hdmi-core.h> #include <linux/fsl_devices.h> #include <mach/hardware.h> +#include <linux/mfd/mxc-hdmi-core.h> struct mxc_hdmi_data { struct platform_device *pdev; @@ -72,22 +73,29 @@ static spinlock_t hdmi_audio_lock, hdmi_blank_state_lock, hdmi_cable_state_lock; unsigned int hdmi_set_cable_state(unsigned int state) { unsigned long flags; + struct snd_pcm_substream *substream = hdmi_audio_stream_playback; spin_lock_irqsave(&hdmi_cable_state_lock, flags); hdmi_cable_state = state; spin_unlock_irqrestore(&hdmi_cable_state_lock, flags); + if (check_hdmi_state() && substream) + substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); return 0; } unsigned int hdmi_set_blank_state(unsigned int state) { unsigned long flags; + struct snd_pcm_substream *substream = hdmi_audio_stream_playback; spin_lock_irqsave(&hdmi_blank_state_lock, flags); hdmi_blank_state = state; spin_unlock_irqrestore(&hdmi_blank_state_lock, flags); + if (check_hdmi_state() && substream) + substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); + return 0; } @@ -98,7 +106,7 @@ static void hdmi_audio_abort_stream(struct snd_pcm_substream *substream) snd_pcm_stream_lock_irqsave(substream, flags); if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); snd_pcm_stream_unlock_irqrestore(substream, flags); } @@ -114,7 +122,7 @@ int mxc_hdmi_abort_stream(void) return 0; } -static int check_hdmi_state(void) +int check_hdmi_state(void) { unsigned long flags1, flags2; unsigned int ret; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index e4cd11b97c24..9db650f00734 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -658,6 +658,9 @@ static int mmc_sdio_suspend(struct mmc_host *host) mmc_release_host(host); } + if (!err) + mmc_claim_host(host); + return err; } @@ -668,9 +671,6 @@ static int mmc_sdio_resume(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - /* Basic card reinitialization. */ - mmc_claim_host(host); - /* No need to reinitialize powered-resumed nonremovable cards */ if (mmc_card_is_removable(host) && !mmc_card_keep_power(host)) err = mmc_sdio_init_card(host, host->ocr, host->card, diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index f25b7c27b101..cc353ce5ae7e 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -873,7 +873,7 @@ static void block_mark_swapping(struct gpmi_nand_data *this, } static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -939,17 +939,20 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, mtd->ecc_stats.corrected += corrected; } - /* - * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() for - * details about our policy for delivering the OOB. - * - * We fill the caller's buffer with set bits, and then copy the block - * mark to th caller's buffer. Note that, if block mark swapping was - * necessary, it has already been done, so we can rely on the first - * byte of the auxiliary buffer to contain the block mark. - */ - memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + if (oob_required) { + /* + * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() + * for details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the + * block mark to th caller's buffer. Note that, if block mark + * swapping was necessary, it has already been done, so we can + * rely on the first byte of the auxiliary buffer to contain + * the block mark. + */ + memset(chip->oob_poi, ~0, mtd->oobsize); + chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + } read_page_swap_end(this, buf, mtd->writesize, this->payload_virt, this->payload_phys, @@ -959,8 +962,8 @@ exit_nfc: return ret; } -static void gpmi_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) +static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -1040,7 +1043,7 @@ static int gpmi_verify_buf(struct mtd_info *mtd, const uint8_t * buf, int len) { struct nand_chip *nand = mtd->priv; - gpmi_ecc_read_page(mtd, nand, verify_buf, len); + gpmi_ecc_read_page(mtd, nand, verify_buf, 0, len); if (memcmp(buf, verify_buf, len)) return -EFAULT; return 0; @@ -1353,7 +1356,7 @@ static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - chip->ecc.write_page_raw(mtd, chip, buffer); + chip->ecc.write_page_raw(mtd, chip, buffer, 0); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); /* Wait for the write to finish. */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e14f8284e79f..6367eac4461b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1051,12 +1051,13 @@ EXPORT_SYMBOL(nand_lock); * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ecc controllers, which use a special oob layout */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -1068,13 +1069,14 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * We need a special oob layout and handling even when OOB isn't used. */ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1111,10 +1113,11 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1124,7 +1127,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; - chip->ecc.read_page_raw(mtd, chip, buf, page); + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -1239,12 +1242,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ecc controllers which need a special oob layout */ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1284,6 +1288,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Hardware ECC for large page chips, require OOB to be read first. @@ -1294,7 +1299,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * overwriting the NAND manufacturer bad block markings. */ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1333,13 +1338,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1443,7 +1449,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int chipnr, page, realpage, col, bytes, aligned; + int chipnr, page, realpage, col, bytes, aligned, oob_required; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; @@ -1468,6 +1474,7 @@ int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = ops->datbuf; oob = ops->oobbuf; + oob_required = oob ? 1 : 0; while (1) { bytes = min(mtd->writesize - col, readlen); @@ -1485,13 +1492,15 @@ int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, - bufpoi, page); + bufpoi, + oob_required, + page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi, - page); + oob_required, page); if (ret < 0) break; @@ -1893,11 +1902,12 @@ out: * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * Not for syndrome calculating ecc controllers, which use a special oob layout */ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -1908,12 +1918,13 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * We need a special oob layout and handling even when ECC isn't checked. */ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1947,9 +1958,10 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1965,7 +1977,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, 1); } /** @@ -1973,9 +1985,10 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2001,12 +2014,14 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ static void nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2045,21 +2060,23 @@ static void nand_write_page_syndrome(struct mtd_info *mtd, * @mtd: MTD device structure * @chip: NAND chip descriptor * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB * @page: page number to write * @cached: cached programming * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + const uint8_t *buf, int oob_required, int page, + int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + chip->ecc.write_page(mtd, chip, buf, oob_required); /* * Cached progamming disabled for now, Not sure if its worth the @@ -2176,6 +2193,7 @@ int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; + int oob_required = oob ? 1 : 0; ops->retlen = 0; if (!writelen) @@ -2238,7 +2256,7 @@ int nand_do_write_ops(struct mtd_info *mtd, loff_t to, memset(chip->oob_poi, 0xff, mtd->oobsize); } - ret = chip->write_page(mtd, chip, wbuf, page, cached, + ret = chip->write_page(mtd, chip, wbuf, oob_required, page, cached, (ops->mode == MTD_OOB_RAW)); if (ret) break; diff --git a/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c b/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c index e09a46b3d7b5..36b7bcfab997 100644 --- a/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c +++ b/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c @@ -4282,7 +4282,7 @@ gckHARDWARE_SetFscaleValue( gctUINT32 clock; gctBOOL acquired = gcvFALSE; - gcmkHEADER_ARG("Hardware=0x%x FscaleVal=%d", Hardware, FscaleVal); + gcmkHEADER_ARG("Hardware=0x%x FscaleValue=%d", Hardware, FscaleValue); gcmkVERIFY_ARGUMENT(FscaleValue > 0 && FscaleValue <= 64); diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c index 8cf0509cfd92..e05a143f41c3 100644 --- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c +++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c @@ -30,6 +30,9 @@ #define _GC_OBJ_ZONE gcvZONE_COMMAND +#if gcdENABLE_FSCALE_VAL_ADJUST +extern int thermal_hot; +#endif /******************************************************************************\ ********************************* Support Code ********************************* \******************************************************************************/ @@ -1080,6 +1083,24 @@ gckCOMMAND_Commit( /* Extract the gckHARDWARE and gckEVENT objects. */ hardware = Command->kernel->hardware; +#if gcdENABLE_FSCALE_VAL_ADJUST + if(hardware->core == gcvCORE_MAJOR){ + static gctUINT orgFscale,minFscale,maxFscale; + static gctBOOL bAlreadyTooHot = gcvFALSE; + if((thermal_hot > 0) && (!bAlreadyTooHot)) { + gckHARDWARE_GetFscaleValue(hardware,&orgFscale,&minFscale, &maxFscale); + gckHARDWARE_SetFscaleValue(hardware, minFscale); + bAlreadyTooHot = gcvTRUE; + gckOS_Print("System is too hot. GPU3D will work at %d/64 clock.\n", minFscale); + } else if((!(thermal_hot > 0)) && bAlreadyTooHot) { + gckHARDWARE_SetFscaleValue(hardware, orgFscale); + gckOS_Print("Hot alarm is canceled. GPU3D clock will return to %d/64\n", orgFscale); + bAlreadyTooHot = gcvFALSE; + } + + } +#endif + /* Check wehther we need to copy the structures or not. */ gcmkONERROR(gckOS_QueryNeedCopy(Command->os, ProcessID, &needCopy)); diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c index 890d13e17c50..eba81b64be92 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c @@ -358,6 +358,15 @@ gckGALDEVICE_Construct( gckDebugFileSystemSetCurrentNode(device->dbgnode); } } + /*get gpu regulator*/ + device->gpu_regulator = regulator_get(NULL, "cpu_vddgpu"); + if (IS_ERR(device->gpu_regulator)) { + gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to get gpu regulator %s/%s \n", + __FUNCTION__, __LINE__, + PARENT_FILE, DEBUG_FILE); + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } /*Initialize the clock structure*/ if (IrqLine != -1) { diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h index 03f7f9b2201e..c15989894600 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h @@ -89,6 +89,9 @@ typedef struct _gckGALDEVICE struct clk *clk_2d_axi; struct clk *clk_vg_axi; + /*Power management.*/ + struct regulator *gpu_regulator; + } * gckGALDEVICE; diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h index 30fb13596dda..a6ed03ffc0f9 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h @@ -47,6 +47,7 @@ #if ENABLE_GPU_CLOCK_BY_DRIVER && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) #include <linux/clk.h> +#include <linux/regulator/consumer.h> #endif #define NTSTRSAFE_NO_CCH_FUNCTIONS diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c index 4d1a71dfa91a..4091ccdb61d4 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c @@ -6947,6 +6947,7 @@ gckOS_SetGPUPower( struct clk *clk_vg_axi = Os->device->clk_vg_axi; gctBOOL oldClockState = gcvFALSE; + gctBOOL oldPowerState = gcvFALSE; gcmkHEADER_ARG("Os=0x%X Core=%d Clock=%d Power=%d", Os, Core, Clock, Power); @@ -6956,15 +6957,20 @@ gckOS_SetGPUPower( if (Core == gcvCORE_VG) { oldClockState = Os->device->kernels[Core]->vg->hardware->clockState; + oldPowerState = Os->device->kernels[Core]->vg->hardware->powerState; } else { #endif oldClockState = Os->device->kernels[Core]->hardware->clockState; + oldPowerState = Os->device->kernels[Core]->hardware->powerState; #if gcdENABLE_VG } #endif } + if((Power == gcvTRUE) && (oldPowerState == gcvFALSE) && + !IS_ERR(Os->device->gpu_regulator)) + regulator_enable(Os->device->gpu_regulator); if (Clock == gcvTRUE) { if (oldClockState == gcvFALSE) { @@ -7007,8 +7013,10 @@ gckOS_SetGPUPower( } } } - - + if((Power == gcvFALSE) && (oldPowerState == gcvTRUE) && + !IS_ERR(Os->device->gpu_regulator)) + regulator_disable(Os->device->gpu_regulator); + /* TODO: Put your code here. */ gcmkFOOTER_NO(); return gcvSTATUS_OK; } diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c index d12ffdfc7847..be1725129ce2 100644 --- a/drivers/mxc/ipu3/ipu_device.c +++ b/drivers/mxc/ipu3/ipu_device.c @@ -340,6 +340,7 @@ struct ipu_alloc_list { dma_addr_t phy_addr; void *cpu_addr; u32 size; + void *file_index; }; static LIST_HEAD(ipu_alloc_list); @@ -350,6 +351,7 @@ static DEFINE_SPINLOCK(ipu_task_list_lock); static DECLARE_WAIT_QUEUE_HEAD(thread_waitq); static DECLARE_WAIT_QUEUE_HEAD(res_waitq); static atomic_t req_cnt; +static atomic_t file_index = ATOMIC_INIT(1); static int major; static int max_ipu_no; static int thread_id; @@ -3274,6 +3276,7 @@ EXPORT_SYMBOL_GPL(ipu_queue_task); static int mxc_ipu_open(struct inode *inode, struct file *file) { + file->private_data = (void *)atomic_inc_return(&file_index); return 0; } @@ -3330,6 +3333,7 @@ static long mxc_ipu_ioctl(struct file *file, kfree(mem); return -ENOMEM; } + mem->file_index = file->private_data; mutex_lock(&ipu_alloc_lock); list_add(&mem->list, &ipu_alloc_list); mutex_unlock(&ipu_alloc_lock); @@ -3413,6 +3417,26 @@ static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma) static int mxc_ipu_release(struct inode *inode, struct file *file) { + struct ipu_alloc_list *mem; + struct ipu_alloc_list *n; + + mutex_lock(&ipu_alloc_lock); + list_for_each_entry_safe(mem, n, &ipu_alloc_list, list) { + if ((mem->cpu_addr != 0) && + (file->private_data == mem->file_index)) { + list_del(&mem->list); + dma_free_coherent(ipu_dev, + mem->size, + mem->cpu_addr, + mem->phy_addr); + dev_dbg(ipu_dev, "rel-free %d bytes @ 0x%08X\n", + mem->size, mem->phy_addr); + kfree(mem); + } + } + mutex_unlock(&ipu_alloc_lock); + atomic_dec(&file_index); + return 0; } diff --git a/drivers/mxc/thermal/cooling.c b/drivers/mxc/thermal/cooling.c index 0feefeaa6008..7019d9949c8a 100644 --- a/drivers/mxc/thermal/cooling.c +++ b/drivers/mxc/thermal/cooling.c @@ -57,6 +57,7 @@ cpufreq, it minor 1, and when we promote cpufreq, it add 1, so if it is 0, mean we didn't change the cpufreq */ static int cpufreq_change_count; +extern int thermal_hot; int anatop_thermal_get_cpufreq_cur(void) { int ret = -EINVAL; @@ -237,6 +238,7 @@ imx_processor_set_cur_state(struct thermal_cooling_device *cdev, secondary CPUs that detached by thermal driver */ if (cooling_cpuhotplug) { if (!state) { + thermal_hot = 0; for (i = 1; i < 4; i++) { if (cpu_mask && (0x1 << i)) { anatop_thermal_cpu_hotplug(true); @@ -247,6 +249,7 @@ imx_processor_set_cur_state(struct thermal_cooling_device *cdev, } } else { if (!state) { + thermal_hot = 0; if (cpufreq_change_count < 0) anatop_thermal_cpufreq_up(); else if (cpufreq_change_count > 0) diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c index 92a1d0dc98c7..8178824ab80a 100644 --- a/drivers/mxc/vpu/mxc_vpu.c +++ b/drivers/mxc/vpu/mxc_vpu.c @@ -39,6 +39,7 @@ #include <linux/workqueue.h> #include <linux/sched.h> #include <linux/vmalloc.h> +#include <linux/regulator/consumer.h> #include <linux/page-flags.h> #include <linux/mm_types.h> #include <linux/types.h> @@ -110,6 +111,7 @@ static int vpu_jpu_irq; #endif static unsigned int regBk[64]; +static struct regulator *vpu_regulator; #define READ_REG(x) __raw_readl(vpu_base + x) #define WRITE_REG(val, x) __raw_writel(val, vpu_base + x) @@ -245,8 +247,12 @@ bool vpu_is_valid_phy_memory(u32 paddr) */ static int vpu_open(struct inode *inode, struct file *filp) { + mutex_lock(&vpu_data.lock); - open_count++; + + if (open_count++ == 0 && !IS_ERR(vpu_regulator)) + regulator_enable(vpu_regulator); + filp->private_data = (void *)(&vpu_data); mutex_unlock(&vpu_data.lock); return 0; @@ -531,6 +537,9 @@ static int vpu_release(struct inode *inode, struct file *filp) mutex_lock(&vpu_data.lock); if (open_count > 0 && !(--open_count)) { + if (!IS_ERR(vpu_regulator)) + regulator_disable(vpu_regulator); + vpu_free_buffers(); /* Free shared memory when vpu device is idle */ @@ -705,6 +714,12 @@ static int vpu_dev_probe(struct platform_device *pdev) (void *)(&vpu_data)); if (err) goto err_out_class; + vpu_regulator = regulator_get(NULL, "cpu_vddvpu"); + if (IS_ERR(vpu_regulator)) { + printk(KERN_ERR + "%s: failed to get vpu regulator\n", __func__); + goto err_out_class; + } #ifdef MXC_VPU_HAS_JPU vpu_jpu_irq = platform_get_irq_byname(pdev, "vpu_jpu_irq"); @@ -752,7 +767,8 @@ static int vpu_dev_remove(struct platform_device *pdev) iounmap(vpu_base); if (vpu_plat && vpu_plat->iram_enable && vpu_plat->iram_size) iram_free(iram.start, vpu_plat->iram_size); - + if (!IS_ERR(vpu_regulator)) + regulator_put(vpu_regulator); return 0; } @@ -762,6 +778,8 @@ static int vpu_suspend(struct platform_device *pdev, pm_message_t state) int i; unsigned long timeout; + if (!IS_ERR(vpu_regulator)) + regulator_enable(vpu_regulator); /* Wait for vpu go to idle state, suspect vpu cannot be changed to idle state after about 1 sec */ if (open_count > 0) { @@ -794,10 +812,14 @@ static int vpu_suspend(struct platform_device *pdev, pm_message_t state) if (vpu_plat->pg) vpu_plat->pg(1); + if (!IS_ERR(vpu_regulator)) + regulator_disable(vpu_regulator); return 0; out: clk_disable(vpu_clk); + if (!IS_ERR(vpu_regulator)) + regulator_disable(vpu_regulator); return -EAGAIN; } @@ -809,6 +831,8 @@ static int vpu_resume(struct platform_device *pdev) if (cpu_is_mx53()) goto recover_clk; + if (!IS_ERR(vpu_regulator)) + regulator_enable(vpu_regulator); if (vpu_plat->pg) vpu_plat->pg(0); @@ -869,6 +893,8 @@ recover_clk: /* Recover vpu clock */ for (i = 0; i < vpu_clk_usercount; i++) clk_enable(vpu_clk); + if (!IS_ERR(vpu_regulator)) + regulator_disable(vpu_regulator); return 0; } diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 5af378f391ee..86e4ea3404b3 100755 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -397,7 +397,12 @@ fec_timeout(struct net_device *ndev) ndev->stats.tx_errors++; + netif_device_detach(ndev); + fec_stop(ndev); + fec_restart(ndev, fep->full_duplex); + netif_device_attach(ndev); + ndev->trans_start = jiffies; /* prevent tx timeout */ if (fep->link && !fep->tx_full) netif_wake_queue(ndev); } @@ -1320,10 +1325,7 @@ fec_enet_close(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); - /* Don't know what to do yet. */ fep->opened = 0; - netif_stop_queue(ndev); - netif_carrier_off(ndev); if (fep->use_napi) napi_disable(&fep->napi); @@ -1760,7 +1762,10 @@ fec_stop(struct net_device *dev) if (fep->ptimer_present) fec_ptp_stop(fep->ptp_priv); writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); - netif_stop_queue(dev); + + if (netif_running(dev)) + netif_stop_queue(dev); + netif_carrier_off(dev); /* prevent tx timeout */ fep->link = 0; } @@ -1921,7 +1926,6 @@ fec_suspend(struct device *dev) if (netif_running(ndev)) { netif_device_detach(ndev); fec_stop(ndev); - netif_carrier_off(ndev); clk_disable(fep->clk); } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 6e53cce3c7fd..a24653787d1d 100755 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -222,7 +222,6 @@ config CHARGER_MAX8903 config SABRESD_MAX8903 tristate "Sabresd Board Battery DC-DC Charger for USB and Adapter Power" depends on GENERIC_HARDIRQS - depends on TOUCHSCREEN_MAX11801 help Say Y to enable support for the MAX8903 DC-DC charger and sysfs on sabresd board.The driver supports controlling charger and battery diff --git a/drivers/power/sabresd_battery.c b/drivers/power/sabresd_battery.c index c7318a6eb02c..648922139790 100755 --- a/drivers/power/sabresd_battery.c +++ b/drivers/power/sabresd_battery.c @@ -65,13 +65,11 @@ typedef struct { u32 voltage; u32 percent; } battery_capacity , *pbattery_capacity; - static int cpu_type_flag; static int offset_discharger; static int offset_charger; static int offset_usb_charger; - static battery_capacity chargingTable[] = { {4050, 99}, {4040, 98}, @@ -188,9 +186,9 @@ static enum power_supply_property max8903_battery_props[] = { POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CAPACITY_LEVEL, }; - +#ifdef CONFIG_TOUCHSCREEN_MAX11801 extern u32 max11801_read_adc(void); - +#endif static void max8903_charger_update_status(struct max8903_data *data) { if (data->usb_in || data->ta_in) { @@ -205,26 +203,35 @@ static void max8903_charger_update_status(struct max8903_data *data) } if (data->charger_online == 0 && data->usb_charger_online == 0) { data->battery_status = POWER_SUPPLY_STATUS_DISCHARGING; - } else { - if (gpio_get_value(data->pdata->chg) == 0) { - data->battery_status = POWER_SUPPLY_STATUS_CHARGING; - } else if (data->ta_in && - gpio_get_value(data->pdata->chg) == 1) { - if (data->percent >= 99) - data->battery_status = POWER_SUPPLY_STATUS_FULL; - else - data->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; - } - else if (data->usb_in && - gpio_get_value(data->pdata->chg) == 1) { - if (data->percent >= 99) - data->battery_status = POWER_SUPPLY_STATUS_FULL; - else - data->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; - } + } else { + if (gpio_get_value(data->pdata->chg) == 0) { + data->battery_status = POWER_SUPPLY_STATUS_CHARGING; + } else if (data->ta_in && gpio_get_value(data->pdata->chg) == 1) { + if (!data->pdata->feature_flag) { + if (data->percent >= 99) + data->battery_status = POWER_SUPPLY_STATUS_FULL; + else + data->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + data->battery_status = POWER_SUPPLY_STATUS_FULL; + } + } else if (data->usb_in && gpio_get_value(data->pdata->chg) == 1) { + if (!data->pdata->feature_flag) { + if (data->percent >= 99) + data->battery_status = POWER_SUPPLY_STATUS_FULL; + else + data->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + data->battery_status = POWER_SUPPLY_STATUS_FULL; + } } - pr_debug("chg: %d \n", gpio_get_value(data->pdata->chg)); + } + pr_debug("chg: %d\n", gpio_get_value(data->pdata->chg)); + pr_debug("dc: %d\n", gpio_get_value(data->pdata->dok)); + pr_debug("flt: %d\n", gpio_get_value(data->pdata->flt)); } + +#ifdef CONFIG_TOUCHSCREEN_MAX11801 static int cmp_func(const void *_a, const void *_b) { const int *a = _a, *b = _b; @@ -235,6 +242,7 @@ static int cmp_func(const void *_a, const void *_b) return -1; return 0; } + u32 calibration_voltage(struct max8903_data *data) { int volt[ADC_SAMPLE_COUNT]; @@ -275,51 +283,54 @@ u32 calibration_voltage(struct max8903_data *data) voltage_data = (volt[2] + volt[ADC_SAMPLE_COUNT - 3]) / 2; return voltage_data; } - +#endif static void max8903_battery_update_status(struct max8903_data *data) { - int temp; + int temp = 0; static int temp_last; bool changed_flag; changed_flag = false; mutex_lock(&data->work_lock); - temp = calibration_voltage(data); - if (temp_last == 0) { - data->voltage_uV = temp; - temp_last = temp; - } - if (data->charger_online == 0 && temp_last != 0) { - if (temp < temp_last) { - temp_last = temp; - data->voltage_uV = temp; - } else { - data->voltage_uV = temp_last; + if (!data->pdata->feature_flag) { +#ifdef CONFIG_TOUCHSCREEN_MAX11801 + temp = calibration_voltage(data); +#endif + if (temp_last == 0) { + data->voltage_uV = temp; + temp_last = temp; + } + if (data->charger_online == 0 && temp_last != 0) { + if (temp < temp_last) { + temp_last = temp; + data->voltage_uV = temp; + } else { + data->voltage_uV = temp_last; + } + } + if (data->charger_online == 1 || data->usb_charger_online == 1) { + data->voltage_uV = temp; + temp_last = temp; + } + data->percent = calibrate_battery_capability_percent(data); + if (data->percent != data->old_percent) { + data->old_percent = data->percent; + changed_flag = true; + } + if (changed_flag) { + changed_flag = false; + power_supply_changed(&data->bat); + } + /* + *because boot time gap between led framwork and charger + *framwork,when system boots with charger attatched, + *charger led framwork loses the first charger online event, + *add once extra power_supply_changed can fix this issure + */ + if (data->first_delay_count < 200) { + data->first_delay_count = data->first_delay_count + 1 ; + power_supply_changed(&data->bat); } } - if (data->charger_online == 1 || data->usb_charger_online == 1) { - data->voltage_uV = temp; - temp_last = temp; - } - data->percent = calibrate_battery_capability_percent(data); - if (data->percent != data->old_percent) { - data->old_percent = data->percent; - changed_flag = true; - } - if (changed_flag) { - changed_flag = false; - power_supply_changed(&data->bat); - } - /* - because boot time gap between led framwork and charger - framwork,when system boots with charger attatched, charger - led framwork loses the first charger online event,add once extra - power_supply_changed can fix this issure - */ - if (data->first_delay_count < 200) { - data->first_delay_count = data->first_delay_count + 1 ; - power_supply_changed(&data->bat); - } - mutex_unlock(&data->work_lock); } @@ -332,22 +343,27 @@ static int max8903_battery_get_property(struct power_supply *bat, switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = POWER_SUPPLY_STATUS_UNKNOWN; - if (gpio_get_value(di->pdata->chg) == 0) { - di->battery_status = POWER_SUPPLY_STATUS_CHARGING; - } else if (di->ta_in && - gpio_get_value(di->pdata->chg) == 1) { + if (gpio_get_value(di->pdata->chg) == 0) { + di->battery_status = POWER_SUPPLY_STATUS_CHARGING; + } else if (di->ta_in && gpio_get_value(di->pdata->chg) == 1) { + if (!di->pdata->feature_flag) { if (di->percent >= 99) di->battery_status = POWER_SUPPLY_STATUS_FULL; else di->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + di->battery_status = POWER_SUPPLY_STATUS_FULL; } - else if (di->usb_in && - gpio_get_value(di->pdata->chg) == 1) { + } else if (di->usb_in && gpio_get_value(di->pdata->chg) == 1) { + if (!di->pdata->feature_flag) { if (di->percent >= 99) di->battery_status = POWER_SUPPLY_STATUS_FULL; else - di->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + di->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + di->battery_status = POWER_SUPPLY_STATUS_FULL; } + } val->intval = di->battery_status; return 0; default: @@ -795,14 +811,13 @@ static __devinit int max8903_probe(struct platform_device *pdev) ret = request_threaded_irq(gpio_to_irq(pdata->chg), NULL, max8903_chg, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "MAX8903 Fault", data); + "MAX8903 Status", data); if (ret) { - dev_err(dev, "Cannot request irq %d for Fault (%d)\n", + dev_err(dev, "Cannot request irq %d for Status (%d)\n", gpio_to_irq(pdata->flt), ret); goto err_chg_irq; } } - ret = device_create_file(&pdev->dev, &max8903_discharger_dev_attr); if (ret) dev_err(&pdev->dev, "create device file failed!\n"); @@ -822,7 +837,6 @@ static __devinit int max8903_probe(struct platform_device *pdev) offset_charger = 1485; offset_usb_charger = 1285; } - max8903_charger_update_status(data); max8903_battery_update_status(data); return 0; @@ -888,10 +902,14 @@ static int max8903_suspend(struct platform_device *pdev, if (data) { struct max8903_pdata *pdata = data->pdata; if (pdata) { - irq = gpio_to_irq(pdata->dok); - enable_irq_wake(irq); - irq = gpio_to_irq(pdata->uok); - enable_irq_wake(irq); + if (pdata->dc_valid) { + irq = gpio_to_irq(pdata->dok); + enable_irq_wake(irq); + } + if (pdata->usb_valid) { + irq = gpio_to_irq(pdata->uok); + enable_irq_wake(irq); + } cancel_delayed_work(&data->work); } } @@ -923,10 +941,14 @@ static int max8903_resume(struct platform_device *pdev) max8903_charger_update_status(data); power_supply_changed(&data->usb); } - irq = gpio_to_irq(pdata->dok); - disable_irq_wake(irq); - irq = gpio_to_irq(pdata->uok); - disable_irq_wake(irq); + if (pdata->dc_valid) { + irq = gpio_to_irq(pdata->dok); + disable_irq_wake(irq); + } + if (pdata->usb_valid) { + irq = gpio_to_irq(pdata->uok); + disable_irq_wake(irq); + } schedule_delayed_work(&data->work, BATTERY_UPDATE_INTERVAL); } } diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c index c3a48307c918..b91cf9f82543 100755 --- a/drivers/usb/gadget/arcotg_udc.c +++ b/drivers/usb/gadget/arcotg_udc.c @@ -3323,10 +3323,13 @@ static int udc_suspend(struct fsl_udc *udc) * charge using usb */ if (pdata->pmflags == 0) { - if (!udc_can_wakeup_system()) + if (!udc_can_wakeup_system()) { dr_wake_up_enable(udc, false); - else + } else { + if (pdata->platform_phy_power_on) + pdata->platform_phy_power_on(); dr_wake_up_enable(udc, true); + } } /* diff --git a/drivers/usb/host/ehci-arc.c b/drivers/usb/host/ehci-arc.c index b872d94b3fc3..e09f4dfd05d9 100755 --- a/drivers/usb/host/ehci-arc.c +++ b/drivers/usb/host/ehci-arc.c @@ -678,6 +678,9 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev, usb_host_set_wakeup(hcd->self.controller, false); fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + } else { + if (pdata->platform_phy_power_on) + pdata->platform_phy_power_on(); } printk(KERN_DEBUG "host suspend ends\n"); diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig index f0afddc95bd3..8a4a0792724f 100644 --- a/drivers/video/mxc/Kconfig +++ b/drivers/video/mxc/Kconfig @@ -1,6 +1,6 @@ config FB_MXC tristate "MXC Framebuffer support" - depends on FB && (MXC_IPU || ARCH_MX21 || ARCH_MX27 || ARCH_MX25) + depends on FB && (MXC_IPU || ARCH_MX21 || ARCH_MX27 || ARCH_MX25 || ARCH_MX6) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -60,6 +60,10 @@ config FB_MXC_SII902X depends on FB_MXC_SYNC_PANEL && I2C tristate "Si Image SII9022 DVI/HDMI Interface Chip" +config FB_MXC_SII902X_ELCDIF + depends on FB_MXC_SYNC_PANEL && I2C + tristate "Si Image SII9022 DVI/HDMI Interface Chip for ELCDIF FB" + config FB_MXC_CH7026 depends on FB_MXC_SYNC_PANEL tristate "Chrontel CH7026 VGA Interface Chip" diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile index 21aef052ecd6..092d0aacba27 100644 --- a/drivers/video/mxc/Makefile +++ b/drivers/video/mxc/Makefile @@ -1,9 +1,10 @@ obj-$(CONFIG_FB_MXC_TVOUT_TVE) += tve.o obj-$(CONFIG_FB_MXC_SII902X) += mxcfb_sii902x.o +obj-$(CONFIG_FB_MXC_SII902X_ELCDIF) += mxcfb_sii902x_elcdif.o obj-$(CONFIG_FB_MXC_LDB) += ldb.o obj-$(CONFIG_FB_MXC_MIPI_DSI) += mipi_dsi.o obj-$(CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL) += mxcfb_hx8369_wvga.o -obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o mxc_dvi.o +obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o ifeq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27)$(CONFIG_ARCH_MX25),y) obj-$(CONFIG_FB_MXC_TVOUT) += fs453.o obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mx2fb.o mxcfb_modedb.o @@ -11,8 +12,10 @@ ifeq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27)$(CONFIG_ARCH_MX25),y) else ifeq ($(CONFIG_MXC_IPU_V1),y) obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxcfb.o mxcfb_modedb.o -else +endif +ifeq ($(CONFIG_MXC_IPU_V3),y) obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o + obj-$(CONFIG_FB_MXC_EDID) += mxc_dvi.o endif obj-$(CONFIG_FB_MXC_EPSON_PANEL) += mxcfb_epson.o obj-$(CONFIG_FB_MXC_EPSON_QVGA_PANEL) += mxcfb_epson_qvga.o diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c index 9c3b7b99a411..be521ddbdf9d 100644 --- a/drivers/video/mxc/mxc_elcdif_fb.c +++ b/drivers/video/mxc/mxc_elcdif_fb.c @@ -86,6 +86,12 @@ struct elcdif_signal_cfg { unsigned Vsync_pol:1; /* true = active high */ }; +struct mxcfb_mode { + int dev_mode; + int num_modes; + struct fb_videomode *mode; +}; + static int mxc_elcdif_fb_blank(int blank, struct fb_info *info); static int mxc_elcdif_fb_map_video_memory(struct fb_info *info); static int mxc_elcdif_fb_unmap_video_memory(struct fb_info *info); @@ -97,6 +103,15 @@ static bool g_elcdif_axi_clk_enable; static bool g_elcdif_pix_clk_enable; static struct clk *g_elcdif_axi_clk; static struct clk *g_elcdif_pix_clk; +static struct mxcfb_mode mxc_disp_mode; + +static void dump_fb_videomode(struct fb_videomode *m) +{ + pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + m->refresh, m->xres, m->yres, m->pixclock, m->left_margin, + m->right_margin, m->upper_margin, m->lower_margin, + m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); +} static inline void setup_dotclk_panel(u32 pixel_clk, u16 v_pulse_width, @@ -113,16 +128,39 @@ static inline void setup_dotclk_panel(u32 pixel_clk, int enable_present) { u32 val, rounded_pixel_clk; + u32 rounded_parent_clk; + struct clk *clk_parent; if (!g_elcdif_axi_clk_enable) { clk_enable(g_elcdif_axi_clk); g_elcdif_axi_clk_enable = true; } + /* Init clocking */ dev_dbg(g_elcdif_dev, "pixel clk = %d\n", pixel_clk); - rounded_pixel_clk = clk_round_rate(g_elcdif_pix_clk, pixel_clk); + + clk_parent = clk_get_parent(g_elcdif_pix_clk); + if (clk_parent == NULL) + dev_err(g_elcdif_dev, "%s Failed get clk parent\n", __func__); + + rounded_pixel_clk = pixel_clk * 2; + + rounded_parent_clk = clk_round_rate(clk_parent, + rounded_pixel_clk); + + while (rounded_pixel_clk < rounded_parent_clk) { + /* the max divider from parent to di is 8 */ + if (rounded_parent_clk / pixel_clk < 8) + rounded_pixel_clk += pixel_clk * 2; + } + + clk_set_rate(clk_parent, rounded_pixel_clk); + rounded_pixel_clk = + clk_round_rate(g_elcdif_pix_clk, pixel_clk); clk_set_rate(g_elcdif_pix_clk, rounded_pixel_clk); + msleep(5); + __raw_writel(BM_ELCDIF_CTRL_DATA_SHIFT_DIR, elcdif_base + HW_ELCDIF_CTRL_CLR); @@ -519,6 +557,35 @@ static inline void mxc_init_elcdif(void) return; } +void mxcfb_elcdif_register_mode(const struct fb_videomode *modedb, + int num_modes, int dev_mode) +{ + struct fb_videomode *mode; + + mode = kzalloc(num_modes * sizeof(struct fb_videomode), GFP_KERNEL); + + if (!mode) { + dev_err(g_elcdif_dev, "%s Failed to allocate mode data\n", __func__); + return; + } + + if (mxc_disp_mode.num_modes) + memcpy(mode, mxc_disp_mode.mode, + mxc_disp_mode.num_modes * sizeof(struct fb_videomode)); + if (modedb) + memcpy(mode + mxc_disp_mode.num_modes, modedb, + num_modes * sizeof(struct fb_videomode)); + + if (mxc_disp_mode.num_modes) + kfree(mxc_disp_mode.mode); + + mxc_disp_mode.mode = mode; + mxc_disp_mode.num_modes += num_modes; + mxc_disp_mode.dev_mode = dev_mode; + + return; +} + int mxc_elcdif_frame_addr_setup(dma_addr_t phys) { int ret = 0; @@ -1236,6 +1303,9 @@ static int mxc_elcdif_fb_probe(struct platform_device *pdev) struct resource *res; struct fb_info *fbi; struct mxc_fb_platform_data *pdata = pdev->dev.platform_data; + const struct fb_videomode *mode; + struct fb_videomode m; + int num; fbi = framebuffer_alloc(sizeof(struct mxc_elcdif_fb_data), &pdev->dev); if (fbi == NULL) { @@ -1296,20 +1366,63 @@ static int mxc_elcdif_fb_probe(struct platform_device *pdev) if (pdata && !data->output_pix_fmt) data->output_pix_fmt = pdata->interface_pix_fmt; + INIT_LIST_HEAD(&fbi->modelist); + if (pdata && pdata->mode && pdata->num_modes) fb_videomode_to_modelist(pdata->mode, pdata->num_modes, &fbi->modelist); + if (mxc_disp_mode.num_modes) { + int i; + mode = mxc_disp_mode.mode; + num = mxc_disp_mode.num_modes; + + for (i = 0; i < num; i++) { + /* + * FIXME now we do not support interlaced + * mode for ddc mode + */ + if ((mxc_disp_mode.dev_mode + & MXC_DISP_DDC_DEV) && + (mode[i].vmode & FB_VMODE_INTERLACED)) + continue; + else { + dev_dbg(&pdev->dev, "Added mode %d:", i); + dev_dbg(&pdev->dev, + "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n", + mode[i].xres, mode[i].yres, mode[i].refresh, mode[i].vmode, + mode[i].flag); + fb_add_videomode(&mode[i], &fbi->modelist); + } + } + } + if (!fb_mode && pdata && pdata->mode_str) fb_mode = pdata->mode_str; if (fb_mode) { + dev_dbg(&pdev->dev, "default video mode %s\n", fb_mode); ret = fb_find_mode(&fbi->var, fbi, fb_mode, NULL, 0, NULL, default_bpp); - if ((!ret || (ret > 2)) && pdata && pdata->mode && - pdata->num_modes) - fb_find_mode(&fbi->var, fbi, fb_mode, pdata->mode, + if ((ret == 1) || (ret == 2)) { + fb_var_to_videomode(&m, &fbi->var); + dump_fb_videomode(&m); + mode = fb_find_nearest_mode(&m, + &fbi->modelist); + fb_videomode_to_var(&fbi->var, mode); + } else if (pdata && pdata->mode && pdata->num_modes) { + ret = fb_find_mode(&fbi->var, fbi, fb_mode, pdata->mode, pdata->num_modes, NULL, default_bpp); + if (!ret) { + dev_err(fbi->device, + "No valid video mode found"); + goto err2; + } + } else { + dev_err(fbi->device, + "No valid video mode found"); + goto err2; + } } mxc_elcdif_fb_check_var(&fbi->var, fbi); @@ -1343,6 +1456,19 @@ static int mxc_elcdif_fb_probe(struct platform_device *pdev) */ clk_set_rate(g_elcdif_pix_clk, 25000000); + fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + ret = fb_set_var(fbi, &fbi->var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + if (data->cur_blank == FB_BLANK_UNBLANK) { + console_lock(); + fb_blank(fbi, FB_BLANK_UNBLANK); + console_unlock(); + } + ret = register_framebuffer(fbi); if (ret) goto err3; diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c index 0b3923c2cea6..b702788ae823 100644 --- a/drivers/video/mxc/mxc_epdc_fb.c +++ b/drivers/video/mxc/mxc_epdc_fb.c @@ -50,6 +50,7 @@ #include <linux/bitops.h> #include <mach/epdc.h> #include <mach/dma.h> +#include <asm/cacheflush.h> #include "epdc_regs.h" @@ -69,6 +70,10 @@ #define INVALID_LUT (-1) #define DRY_RUN_NO_LUT 100 +/* Maximum update buffer image width due to v2.0 and v2.1 errata ERR005313. */ +#define EPDC_V2_MAX_UPDATE_WIDTH 2047 +#define EPDC_V2_ROTATION_ALIGNMENT 8 + #define DEFAULT_TEMP_INDEX 0 #define DEFAULT_TEMP 20 /* room temp in deg Celsius */ @@ -202,6 +207,8 @@ struct mxc_epdc_fb_data { bool updates_active; int pwrdown_delay; unsigned long tce_prevent; + bool restrict_width; /* work around rev >=2.0 width and + stride restriction */ /* FB elements related to PxP DMA */ struct completion pxp_tx_cmpl; @@ -262,6 +269,16 @@ static int pxp_complete_update(struct mxc_epdc_fb_data *fb_data, u32 *hist_stat) static void draw_mode0(struct mxc_epdc_fb_data *fb_data); static bool is_free_list_full(struct mxc_epdc_fb_data *fb_data); +static void do_dithering_processing_Y1_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist); +static void do_dithering_processing_Y4_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist); #ifdef DEBUG static void dump_pxp_config(struct mxc_epdc_fb_data *fb_data, @@ -1930,10 +1947,10 @@ static int epdc_process_update(struct update_data_list *upd_data_list, * to copybuf in addition to the PxP structures */ mutex_lock(&fb_data->pxp_mutex); - if ((((width_unaligned || height_unaligned || input_unaligned) && + if (((((width_unaligned || height_unaligned || input_unaligned) && (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO)) - || line_overflow) && (fb_data->rev < 20)) { - + || line_overflow) && (fb_data->rev < 20)) || + fb_data->restrict_width) { dev_dbg(fb_data->dev, "Copying update before processing.\n"); /* Update to reflect what the new source buffer will be */ @@ -2137,7 +2154,8 @@ static int epdc_process_update(struct update_data_list *upd_data_list, } static int epdc_submit_merge(struct update_desc_list *upd_desc_list, - struct update_desc_list *update_to_merge) + struct update_desc_list *update_to_merge, + struct mxc_epdc_fb_data *fb_data) { struct mxcfb_update_data *a, *b; struct mxcfb_rect *arect, *brect; @@ -2195,6 +2213,19 @@ static int epdc_submit_merge(struct update_desc_list *upd_desc_list, (arect->top + arect->height - combine.top) : (brect->top + brect->height - combine.top); + /* Don't merge if combined width exceeds max width */ + if (fb_data->restrict_width) { + u32 max_width = EPDC_V2_MAX_UPDATE_WIDTH; + u32 combined_width = combine.width; + if (fb_data->epdc_fb_var.rotate != FB_ROTATE_UR) + max_width -= EPDC_V2_ROTATION_ALIGNMENT; + if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_CW) || + (fb_data->epdc_fb_var.rotate == FB_ROTATE_CCW)) + combined_width = combine.height; + if (combined_width > max_width) + return MERGE_FAIL; + } + *arect = combine; /* Use flags of the later update */ @@ -2226,6 +2257,7 @@ static void epdc_submit_work_func(struct work_struct *work) bool end_merge = false; bool is_transform; u32 update_addr; + int *err_dist; int ret; /* Protect access to buffer queues and to update HW */ @@ -2260,7 +2292,8 @@ static void epdc_submit_work_func(struct work_struct *work) break; } else { switch (epdc_submit_merge(upd_data_list->update_desc, - next_update->update_desc)) { + next_update->update_desc, + fb_data)) { case MERGE_OK: dev_dbg(fb_data->dev, "Update merged [collision]\n"); @@ -2330,7 +2363,7 @@ static void epdc_submit_work_func(struct work_struct *work) break; } else { switch (epdc_submit_merge(upd_data_list->update_desc, - next_desc)) { + next_desc, fb_data)) { case MERGE_OK: dev_dbg(fb_data->dev, "Update merged [queue]\n"); @@ -2371,12 +2404,14 @@ static void epdc_submit_work_func(struct work_struct *work) * PxP in versions 2.0 and earlier of EPDC. */ is_transform = upd_data_list->update_desc->upd_data.flags & - (EPDC_FLAG_ENABLE_INVERSION | - EPDC_FLAG_FORCE_MONOCHROME | EPDC_FLAG_USE_CMAP) ? - true : false; + (EPDC_FLAG_ENABLE_INVERSION | EPDC_FLAG_USE_DITHERING_Y1 | + EPDC_FLAG_USE_DITHERING_Y4 | EPDC_FLAG_FORCE_MONOCHROME | + EPDC_FLAG_USE_CMAP) ? true : false; + if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) && (fb_data->epdc_fb_var.grayscale == GRAYSCALE_8BIT) && - !is_transform && (fb_data->rev > 20)) { + !is_transform && (fb_data->rev > 20) && + !fb_data->restrict_width) { /* If needed, enable EPDC HW while ePxP is processing */ if ((fb_data->power_state == POWER_STATE_OFF) @@ -2391,7 +2426,6 @@ static void epdc_submit_work_func(struct work_struct *work) update_addr = fb_data->info.fix.smem_start + ((upd_region->top * fb_data->info.var.xres_virtual) + upd_region->left) * fb_data->info.var.bits_per_pixel/8; - upd_data_list->update_desc->epdc_stride = fb_data->info.var.xres_virtual * fb_data->info.var.bits_per_pixel/8; @@ -2457,6 +2491,45 @@ static void epdc_submit_work_func(struct work_struct *work) } /* + * Dithering Processing (Version 1.0 - for i.MX508 and i.MX6SL) + */ + if (upd_data_list->update_desc->upd_data.flags & + EPDC_FLAG_USE_DITHERING_Y1) { + + err_dist = kzalloc((fb_data->info.var.xres_virtual + 3) * 3 + * sizeof(int), GFP_KERNEL); + + /* Dithering Y8 -> Y1 */ + do_dithering_processing_Y1_v1_0( + (uint8_t *)(upd_data_list->virt_addr + + upd_data_list->update_desc->epdc_offs), + &adj_update_region, + (fb_data->rev < 20) ? + ALIGN(adj_update_region.width, 8) : + adj_update_region.width, + err_dist); + + kfree(err_dist); + } else if (upd_data_list->update_desc->upd_data.flags & + EPDC_FLAG_USE_DITHERING_Y4) { + + err_dist = kzalloc((fb_data->info.var.xres_virtual + 3) * 3 + * sizeof(int), GFP_KERNEL); + + /* Dithering Y8 -> Y1 */ + do_dithering_processing_Y4_v1_0( + (uint8_t *)(upd_data_list->virt_addr + + upd_data_list->update_desc->epdc_offs), + &adj_update_region, + (fb_data->rev < 20) ? + ALIGN(adj_update_region.width, 8) : + adj_update_region.width, + err_dist); + + kfree(err_dist); + } + + /* * If there are no LUTs available, * then we must wait for the resource to become free. * The IST will signal this event. @@ -2572,8 +2645,7 @@ static void epdc_submit_work_func(struct work_struct *work) mutex_unlock(&fb_data->queue_mutex); } - -int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, +static int mxc_epdc_fb_send_single_update(struct mxcfb_update_data *upd_data, struct fb_info *info) { struct mxc_epdc_fb_data *fb_data = info ? @@ -2868,6 +2940,63 @@ int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, mutex_unlock(&fb_data->queue_mutex); return 0; } + +int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data, + struct fb_info *info) +{ + struct mxc_epdc_fb_data *fb_data = info ? + (struct mxc_epdc_fb_data *)info:g_fb_data; + + if (!fb_data->restrict_width) { + /* No width restriction, send entire update region */ + return mxc_epdc_fb_send_single_update(upd_data, info); + } else { + int ret; + __u32 width, left; + __u32 marker; + __u32 *region_width, *region_left; + u32 max_upd_width = EPDC_V2_MAX_UPDATE_WIDTH; + + /* Further restrict max width due to pxp rotation + * alignment requirement + */ + if (fb_data->epdc_fb_var.rotate != FB_ROTATE_UR) + max_upd_width -= EPDC_V2_ROTATION_ALIGNMENT; + + /* Select split of width or height based on rotation */ + if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) || + (fb_data->epdc_fb_var.rotate == FB_ROTATE_UD)) { + region_width = &upd_data->update_region.width; + region_left = &upd_data->update_region.left; + } else { + region_width = &upd_data->update_region.height; + region_left = &upd_data->update_region.top; + } + + if (*region_width <= max_upd_width) + return mxc_epdc_fb_send_single_update(upd_data, info); + + width = *region_width; + left = *region_left; + marker = upd_data->update_marker; + upd_data->update_marker = 0; + + do { + *region_width = max_upd_width; + *region_left = left; + ret = mxc_epdc_fb_send_single_update(upd_data, info); + if (ret) + return ret; + width -= max_upd_width; + left += max_upd_width; + } while (width > max_upd_width); + + *region_width = width; + *region_left = left; + upd_data->update_marker = marker; + return mxc_epdc_fb_send_single_update(upd_data, info); + } +} EXPORT_SYMBOL(mxc_epdc_fb_send_update); int mxc_epdc_fb_wait_update_complete(struct mxcfb_update_marker_data *marker_data, @@ -3041,6 +3170,25 @@ static int mxc_epdc_fb_ioctl(struct fb_info *info, unsigned int cmd, ret = 0; break; } + + case MXCFB_GET_WORK_BUFFER: + { + /* copy the epdc working buffer to the user space */ + struct mxc_epdc_fb_data *fb_data = info ? + (struct mxc_epdc_fb_data *)info:g_fb_data; + flush_cache_all(); + outer_flush_all(); + if (copy_to_user((void __user *)arg, + (const void *) fb_data->working_buffer_virt, + fb_data->working_buffer_size)) + ret = -EFAULT; + else + ret = 0; + flush_cache_all(); + outer_flush_all(); + break; + } + default: break; } @@ -4076,7 +4224,8 @@ static void mxc_epdc_fb_fw_handler(const struct firmware *fw, &fb_data->info); if (ret < 0) dev_err(fb_data->dev, - "Wait for update complete failed. Error = 0x%x", ret); + "Wait for initial update complete failed." + " Error = 0x%x", ret); } static int mxc_epdc_fb_init_hw(struct fb_info *info) @@ -4417,6 +4566,8 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev) } else { fb_data->num_luts = EPDC_V2_NUM_LUTS; fb_data->max_num_updates = EPDC_V2_MAX_NUM_UPDATES; + if (vmode->xres > EPDC_V2_MAX_UPDATE_WIDTH) + fb_data->restrict_width = true; } fb_data->max_num_buffers = EPDC_MAX_NUM_BUFFERS; @@ -4454,8 +4605,9 @@ int __devinit mxc_epdc_fb_probe(struct platform_device *pdev) * be as big as the full-screen frame buffer */ fb_data->virt_addr_updbuf[i] = - dma_alloc_coherent(fb_data->info.device, fb_data->max_pix_size, - &fb_data->phys_addr_updbuf[i], GFP_DMA); + kmalloc(fb_data->max_pix_size, GFP_KERNEL); + fb_data->phys_addr_updbuf[i] = + virt_to_phys(fb_data->virt_addr_updbuf[i]); if (fb_data->virt_addr_updbuf[i] == NULL) { ret = -ENOMEM; goto out_upd_buffers; @@ -4737,10 +4889,7 @@ out_copybuffer: out_upd_buffers: for (i = 0; i < fb_data->max_num_buffers; i++) if (fb_data->virt_addr_updbuf[i] != NULL) - dma_free_writecombine(&pdev->dev, - fb_data->max_pix_size, - fb_data->virt_addr_updbuf[i], - fb_data->phys_addr_updbuf[i]); + kfree(fb_data->virt_addr_updbuf[i]); if (fb_data->virt_addr_updbuf != NULL) kfree(fb_data->virt_addr_updbuf); if (fb_data->phys_addr_updbuf != NULL) @@ -4785,9 +4934,7 @@ static int mxc_epdc_fb_remove(struct platform_device *pdev) for (i = 0; i < fb_data->max_num_buffers; i++) if (fb_data->virt_addr_updbuf[i] != NULL) - dma_free_writecombine(&pdev->dev, fb_data->max_pix_size, - fb_data->virt_addr_updbuf[i], - fb_data->phys_addr_updbuf[i]); + kfree(fb_data->virt_addr_updbuf[i]); if (fb_data->virt_addr_updbuf != NULL) kfree(fb_data->virt_addr_updbuf); if (fb_data->phys_addr_updbuf != NULL) @@ -5082,6 +5229,124 @@ static int pxp_complete_update(struct mxc_epdc_fb_data *fb_data, u32 *hist_stat) return 0; } +/* + * Different dithering algorithm can be used. We chose + * to implement Bill Atkinson's algorithm as an example + * Thanks Bill Atkinson for his dithering algorithm. + */ + +/* + * Dithering algorithm implementation - Y8->Y1 version 1.0 for i.MX + */ +static void do_dithering_processing_Y1_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist) +{ + + /* create a temp error distribution array */ + int bwPix; + int y; + int col; + int *err_dist_l0, *err_dist_l1, *err_dist_l2, distrib_error; + int width_3 = update_region->width + 3; + char *y8buf; + int x_offset = 0; + + /* prime a few elements the error distribution array */ + for (y = 0; y < update_region->height; y++) { + /* Dithering the Y8 in sbuf to BW suitable for A2 waveform */ + err_dist_l0 = err_dist + (width_3) * (y % 3); + err_dist_l1 = err_dist + (width_3) * ((y + 1) % 3); + err_dist_l2 = err_dist + (width_3) * ((y + 2) % 3); + + y8buf = update_region_ptr + x_offset; + + /* scan the line and convert the Y8 to BW */ + for (col = 1; col <= update_region->width; col++) { + bwPix = *(err_dist_l0 + col) + *y8buf; + + if (bwPix >= 128) { + *y8buf++ = 0xff; + distrib_error = (bwPix - 255) >> 3; + } else { + *y8buf++ = 0; + distrib_error = bwPix >> 3; + } + + /* modify the error distribution buffer */ + *(err_dist_l0 + col + 2) += distrib_error; + *(err_dist_l1 + col + 1) += distrib_error; + *(err_dist_l0 + col + 1) += distrib_error; + *(err_dist_l1 + col - 1) += distrib_error; + *(err_dist_l1 + col) += distrib_error; + *(err_dist_l2 + col) = distrib_error; + } + x_offset += update_region_stride; + } + + flush_cache_all(); + outer_flush_all(); +} + +/* + * Dithering algorithm implementation - Y8->Y4 version 1.0 for i.MX + */ + +static void do_dithering_processing_Y4_v1_0( + unsigned char *update_region_ptr, + struct mxcfb_rect *update_region, + unsigned long update_region_stride, + int *err_dist) +{ + + /* create a temp error distribution array */ + int gcPix; + int y; + int col; + int *err_dist_l0, *err_dist_l1, *err_dist_l2, distrib_error; + int width_3 = update_region->width + 3; + char *y8buf; + int x_offset = 0; + + /* prime a few elements the error distribution array */ + for (y = 0; y < update_region->height; y++) { + /* Dithering the Y8 in sbuf to Y4 */ + err_dist_l0 = err_dist + (width_3) * (y % 3); + err_dist_l1 = err_dist + (width_3) * ((y + 1) % 3); + err_dist_l2 = err_dist + (width_3) * ((y + 2) % 3); + + y8buf = update_region_ptr + x_offset; + + /* scan the line and convert the Y8 to Y4 */ + for (col = 1; col <= update_region->width; col++) { + gcPix = *(err_dist_l0 + col) + *y8buf; + + if (gcPix > 255) + gcPix = 255; + else if (gcPix < 0) + gcPix = 0; + + distrib_error = (*y8buf - (gcPix & 0xf0)) >> 3; + + *y8buf++ = gcPix & 0xf0; + + /* modify the error distribution buffer */ + *(err_dist_l0 + col + 2) += distrib_error; + *(err_dist_l1 + col + 1) += distrib_error; + *(err_dist_l0 + col + 1) += distrib_error; + *(err_dist_l1 + col - 1) += distrib_error; + *(err_dist_l1 + col) += distrib_error; + *(err_dist_l2 + col) = distrib_error; + } + x_offset += update_region_stride; + } + + flush_cache_all(); + outer_flush_all(); +} + static int __init mxc_epdc_fb_init(void) { return platform_driver_register(&mxc_epdc_fb_driver); diff --git a/drivers/video/mxc/mxcfb_sii902x_elcdif.c b/drivers/video/mxc/mxcfb_sii902x_elcdif.c new file mode 100644 index 000000000000..ceab3e66e91c --- /dev/null +++ b/drivers/video/mxc/mxcfb_sii902x_elcdif.c @@ -0,0 +1,560 @@ +/* + * Copyright (C) 2010-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! + * @defgroup Framebuffer Framebuffer Driver for SDC and ADC. + */ + +/*! + * @file mxcfb_sii902x_elcdif.c + * + * @brief MXC ELCDIF Frame buffer driver for SII902x + * + * @ingroup Framebuffer + */ + +/*! + * Include files + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/i2c.h> +#include <linux/mxcfb.h> +#include <linux/fsl_devices.h> +#include <linux/interrupt.h> +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <mach/mxc_edid.h> + +#define SII_EDID_LEN 512 +#define MXC_ENABLE 1 +#define MXC_DISABLE 2 + +struct sii902x_data { + struct platform_device *pdev; + struct i2c_client *client; + struct delayed_work det_work; + struct fb_info *fbi; + struct mxc_edid_cfg edid_cfg; + u8 cable_plugin; + u8 edid[SII_EDID_LEN]; + bool waiting_for_fb; +} sii902x; + +static void sii902x_poweron(void); +static void sii902x_poweroff(void); +static void (*sii902x_reset) (void); + +#ifdef DEBUG +static void dump_fb_videomode(struct fb_videomode *m) +{ + pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + m->refresh, m->xres, m->yres, m->pixclock, m->left_margin, + m->right_margin, m->upper_margin, m->lower_margin, + m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); +} +#else +static void dump_fb_videomode(struct fb_videomode *m) +{} +#endif + +static __attribute__ ((unused)) void dump_regs(u8 reg, int len) +{ + u8 buf[50]; + int i; + + i2c_smbus_read_i2c_block_data(sii902x.client, reg, len, buf); + for (i = 0; i < len; i++) + dev_dbg(&sii902x.client->dev, "reg[0x%02X]: 0x%02X\n", + i+reg, buf[i]); +} + +static ssize_t sii902x_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + strcpy(buf, sii902x.fbi->fix.id); + sprintf(buf+strlen(buf), "\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(fb_name, S_IRUGO, sii902x_show_name, NULL); + +static ssize_t sii902x_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (sii902x.cable_plugin == 0) + strcpy(buf, "plugout\n"); + else + strcpy(buf, "plugin\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(cable_state, S_IRUGO, sii902x_show_state, NULL); + +static ssize_t sii902x_show_edid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, j, len = 0; + + for (j = 0; j < SII_EDID_LEN/16; j++) { + for (i = 0; i < 16; i++) + len += sprintf(buf+len, "0x%02X ", + sii902x.edid[j*16 + i]); + len += sprintf(buf+len, "\n"); + } + + return len; +} + +static DEVICE_ATTR(edid, S_IRUGO, sii902x_show_edid, NULL); + +static void sii902x_setup(struct fb_info *fbi) +{ + u16 data[4]; + u32 refresh; + u8 *tmp; + int i; + + dev_dbg(&sii902x.client->dev, "Sii902x: setup..\n"); + + /* Power up */ + i2c_smbus_write_byte_data(sii902x.client, 0x1E, 0x00); + + /* set TPI video mode */ + data[0] = PICOS2KHZ(fbi->var.pixclock) / 10; + data[2] = fbi->var.hsync_len + fbi->var.left_margin + + fbi->var.xres + fbi->var.right_margin; + data[3] = fbi->var.vsync_len + fbi->var.upper_margin + + fbi->var.yres + fbi->var.lower_margin; + refresh = data[2] * data[3]; + refresh = (PICOS2KHZ(fbi->var.pixclock) * 1000) / refresh; + data[1] = refresh * 100; + tmp = (u8 *)data; + for (i = 0; i < 8; i++) + i2c_smbus_write_byte_data(sii902x.client, i, tmp[i]); + + /* input bus/pixel: full pixel wide (24bit), rising edge */ + i2c_smbus_write_byte_data(sii902x.client, 0x08, 0x70); + /* Set input format to RGB */ + i2c_smbus_write_byte_data(sii902x.client, 0x09, 0x00); + /* set output format to RGB */ + i2c_smbus_write_byte_data(sii902x.client, 0x0A, 0x00); + /* audio setup */ + i2c_smbus_write_byte_data(sii902x.client, 0x25, 0x00); + i2c_smbus_write_byte_data(sii902x.client, 0x26, 0x40); + i2c_smbus_write_byte_data(sii902x.client, 0x27, 0x00); +} + +#ifdef CONFIG_FB_MODE_HELPERS +static int sii902x_read_edid(struct fb_info *fbi) +{ + int old, dat, ret, cnt = 100; + unsigned short addr = 0x50; + + dev_dbg(&sii902x.pdev->dev, "%s\n", __func__); + + old = i2c_smbus_read_byte_data(sii902x.client, 0x1A); + + i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x4); + do { + cnt--; + msleep(10); + dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A); + } while ((!(dat & 0x2)) && cnt); + + if (!cnt) { + ret = -1; + goto done; + } + + i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x06); + + /* edid reading */ + ret = mxc_edid_read(sii902x.client->adapter, addr, + sii902x.edid, &sii902x.edid_cfg, fbi); + + cnt = 100; + do { + cnt--; + i2c_smbus_write_byte_data(sii902x.client, 0x1A, old & ~0x6); + msleep(10); + dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A); + } while ((dat & 0x6) && cnt); + + if (!cnt) + ret = -1; + +done: + + i2c_smbus_write_byte_data(sii902x.client, 0x1A, old); + return ret; +} +#else +static int sii902x_read_edid(struct fb_info *fbi) +{ + return -1; +} +#endif + +static void det_worker(struct work_struct *work) +{ + int dat; + char event_string[16]; + char *envp[] = { event_string, NULL }; + + dev_dbg(&sii902x.pdev->dev, "%s\n", __func__); + + dat = i2c_smbus_read_byte_data(sii902x.client, 0x3D); + if (dat & 0x1) { + /* cable connection changes */ + if (dat & 0x4) { + sii902x.cable_plugin = 1; + dev_dbg(&sii902x.pdev->dev, "EVENT=plugin\n"); + sprintf(event_string, "EVENT=plugin"); + + /* make sure fb is powerdown */ + console_lock(); + fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN); + console_unlock(); + + if (sii902x_read_edid(sii902x.fbi) < 0) + dev_err(&sii902x.client->dev, + "Sii902x: read edid fail\n"); + else { + if (sii902x.fbi->monspecs.modedb_len > 0) { + + int i; + const struct fb_videomode *mode; + struct fb_videomode m; + + fb_destroy_modelist(&sii902x.fbi->modelist); + + for (i = 0; i < sii902x.fbi->monspecs.modedb_len; i++) { + /*FIXME now we do not support interlaced mode */ + mode = &sii902x.fbi->monspecs.modedb[i]; + + if (!(mode->vmode & FB_VMODE_INTERLACED)) { + + dev_dbg(&sii902x.pdev->dev, "Added mode %d:", i); + dev_dbg(&sii902x.pdev->dev, + "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n", + mode->xres, mode->yres, mode->refresh, + mode->vmode, mode->flag); + + fb_add_videomode(mode, &sii902x.fbi->modelist); + } + } + + fb_var_to_videomode(&m, &sii902x.fbi->var); + dump_fb_videomode(&m); + + mode = fb_find_nearest_mode(&m, + &sii902x.fbi->modelist); + + fb_videomode_to_var(&sii902x.fbi->var, mode); + + sii902x.fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + sii902x.fbi->flags |= FBINFO_MISC_USEREVENT; + fb_set_var(sii902x.fbi, &sii902x.fbi->var); + sii902x.fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + } + + console_lock(); + fb_blank(sii902x.fbi, FB_BLANK_UNBLANK); + console_unlock(); + } + } else { + sii902x.cable_plugin = 0; + dev_dbg(&sii902x.pdev->dev, "EVENT=plugout\n"); + sprintf(event_string, "EVENT=plugout"); + console_lock(); + fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN); + console_unlock(); + } + kobject_uevent_env(&sii902x.pdev->dev.kobj, KOBJ_CHANGE, envp); + } + i2c_smbus_write_byte_data(sii902x.client, 0x3D, dat); + + dev_dbg(&sii902x.pdev->dev, "exit %s\n", __func__); + +} + +static irqreturn_t sii902x_detect_handler(int irq, void *data) +{ + if (sii902x.fbi) + schedule_delayed_work(&(sii902x.det_work), msecs_to_jiffies(20)); + else + sii902x.waiting_for_fb = true; + + return IRQ_HANDLED; +} + +static int sii902x_fb_event(struct notifier_block *nb, unsigned long val, void *v) +{ + struct fb_event *event = v; + struct fb_info *fbi = event->info; + + dev_dbg(&sii902x.pdev->dev, "%s\n", __func__); + + switch (val) { + case FB_EVENT_FB_REGISTERED: + if (sii902x.fbi == NULL) { + sii902x.fbi = fbi; + if (sii902x.waiting_for_fb) + det_worker(NULL); + } + fb_show_logo(fbi, 0); + break; + case FB_EVENT_MODE_CHANGE: + sii902x_setup(fbi); + break; + case FB_EVENT_BLANK: + if (*((int *)event->data) == FB_BLANK_UNBLANK) { + dev_dbg(&sii902x.pdev->dev, "FB_BLANK_UNBLANK\n"); + sii902x_poweron(); + } else { + dev_dbg(&sii902x.pdev->dev, "FB_BLANK_BLANK\n"); + sii902x_poweroff(); + } + break; + } + return 0; +} + +static struct notifier_block nb = { + .notifier_call = sii902x_fb_event, +}; + +static int __devinit sii902x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i, dat, ret; + struct fsl_mxc_lcd_platform_data *plat = client->dev.platform_data; + struct fb_info edid_fbi; + + dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);; + + sii902x.client = client; + + /* Claim HDMI pins */ + if (plat->get_pins) + if (!plat->get_pins()) + return -EACCES; + + if (plat->reset) { + sii902x_reset = plat->reset; + sii902x_reset(); + } + + /* Set 902x in hardware TPI mode on and jump out of D3 state */ + if (i2c_smbus_write_byte_data(sii902x.client, 0xc7, 0x00) < 0) { + dev_err(&sii902x.client->dev, + "Sii902x: cound not find device\n"); + return -ENODEV; + } + + /* read device ID */ + for (i = 10; i > 0; i--) { + dat = i2c_smbus_read_byte_data(sii902x.client, 0x1B); + printk(KERN_DEBUG "Sii902x: read id = 0x%02X", dat); + if (dat == 0xb0) { + dat = i2c_smbus_read_byte_data(sii902x.client, 0x1C); + printk(KERN_DEBUG "-0x%02X", dat); + dat = i2c_smbus_read_byte_data(sii902x.client, 0x1D); + printk(KERN_DEBUG "-0x%02X", dat); + dat = i2c_smbus_read_byte_data(sii902x.client, 0x30); + printk(KERN_DEBUG "-0x%02X\n", dat); + break; + } + } + if (i == 0) { + dev_err(&sii902x.client->dev, + "Sii902x: cound not find device\n"); + return -ENODEV; + } + + /* try to read edid */ + ret = sii902x_read_edid(&edid_fbi); + if (ret < 0) + dev_warn(&sii902x.client->dev, "Can not read edid\n"); + + if (ret >= 0) + mxcfb_elcdif_register_mode(edid_fbi.monspecs.modedb, + edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV); + + if (sii902x.client->irq) { + ret = request_irq(sii902x.client->irq, sii902x_detect_handler, + IRQF_TRIGGER_FALLING, + "SII902x_det", &sii902x); + if (ret < 0) + dev_warn(&sii902x.client->dev, + "Sii902x: cound not request det irq %d\n", + sii902x.client->irq); + else { + /*enable cable hot plug irq*/ + i2c_smbus_write_byte_data(sii902x.client, 0x3c, 0x01); + INIT_DELAYED_WORK(&(sii902x.det_work), det_worker); + } + ret = device_create_file(&sii902x.pdev->dev, &dev_attr_fb_name); + if (ret < 0) + dev_warn(&sii902x.client->dev, + "Sii902x: cound not create sys node for fb name\n"); + ret = device_create_file(&sii902x.pdev->dev, &dev_attr_cable_state); + if (ret < 0) + dev_warn(&sii902x.client->dev, + "Sii902x: cound not create sys node for cable state\n"); + ret = device_create_file(&sii902x.pdev->dev, &dev_attr_edid); + if (ret < 0) + dev_warn(&sii902x.client->dev, + "Sii902x: cound not create sys node for edid\n"); + + } + + fb_register_client(&nb); + + dev_dbg(&sii902x.pdev->dev, "%s exit\n", __func__);; + + return 0; +} + +static int __devexit sii902x_remove(struct i2c_client *client) +{ + struct fsl_mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data; + + dev_dbg(&sii902x.pdev->dev, "%s\n", __func__); + + fb_unregister_client(&nb); + sii902x_poweroff(); + + /* Release HDMI pins */ + if (plat->put_pins) + plat->put_pins(); + + return 0; +} + +static int sii902x_suspend(struct i2c_client *client, pm_message_t message) +{ + /*TODO*/ + return 0; +} + +static int sii902x_resume(struct i2c_client *client) +{ + /*TODO*/ + return 0; +} + +static void sii902x_poweron(void) +{ + struct fsl_mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data; + + dev_dbg(&sii902x.pdev->dev, "%s\n", __func__); + + /* Enable pins to HDMI */ + if (plat->enable_pins) + plat->enable_pins(); + + /* Turn on DVI or HDMI */ + if (sii902x.edid_cfg.hdmi_cap) + i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x01); + else + i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x00); + return; +} + +static void sii902x_poweroff(void) +{ + struct fsl_mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data; + + dev_dbg(&sii902x.pdev->dev, "%s\n", __func__); + + /* disable tmds before changing resolution */ + if (sii902x.edid_cfg.hdmi_cap) + i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x11); + else + i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x10); + + /* Disable pins to HDMI */ + if (plat->disable_pins) + plat->disable_pins(); + + return; +} + +static const struct i2c_device_id sii902x_id[] = { + { "sii902x", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sii902x_id); + +static struct i2c_driver sii902x_i2c_driver = { + .driver = { + .name = "sii902x", + }, + .probe = sii902x_probe, + .remove = sii902x_remove, + .suspend = sii902x_suspend, + .resume = sii902x_resume, + .id_table = sii902x_id, +}; + +static int __init sii902x_init(void) +{ + int ret; + + memset(&sii902x, 0, sizeof(sii902x)); + + sii902x.pdev = platform_device_register_simple("sii902x", 0, NULL, 0); + if (IS_ERR(sii902x.pdev)) { + printk(KERN_ERR + "Unable to register Sii902x as a platform device\n"); + ret = PTR_ERR(sii902x.pdev); + goto err; + } + + return i2c_add_driver(&sii902x_i2c_driver); +err: + return ret; +} + +static void __exit sii902x_exit(void) +{ + i2c_del_driver(&sii902x_i2c_driver); + platform_device_unregister(sii902x.pdev); +} + +module_init(sii902x_init); +module_exit(sii902x_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("SII902x DVI/HDMI driver"); +MODULE_LICENSE("GPL"); |