diff options
author | guoyin.chen <guoyin.chen@freescale.com> | 2013-07-15 15:01:21 +0800 |
---|---|---|
committer | guoyin.chen <guoyin.chen@freescale.com> | 2013-07-15 15:01:21 +0800 |
commit | 5793da66dd82a437c23d4a833674c0e09bd1aa07 (patch) | |
tree | 2f34bdc95dedb46d2a91d12837e58b1cf3a3e33f /drivers | |
parent | f128c5660150679f00e518da8e3f582e9a2f03a8 (diff) | |
parent | 9e268cc3e4386f1a5a31a62f7207e5a9b8420124 (diff) |
Merge remote-tracking branch 'fsl-linux-sdk/imx_3.0.35_4.1.0' into imx_3.0.35_android
Conflicts:
arch/arm/mach-mx6/Kconfig
arch/arm/mach-mx6/board-mx6q_arm2.c
arch/arm/mach-mx6/board-mx6q_arm2.h
arch/arm/mach-mx6/board-mx6q_hdmidongle.c
arch/arm/mach-mx6/board-mx6q_sabreauto.c
arch/arm/mach-mx6/board-mx6q_sabreauto.h
arch/arm/mach-mx6/board-mx6q_sabrelite.c
arch/arm/mach-mx6/board-mx6q_sabresd.c
arch/arm/mach-mx6/board-mx6q_sabresd.h
arch/arm/mach-mx6/clock.c
arch/arm/mach-mx6/pcie.c
arch/arm/plat-mxc/include/mach/iomux-mx6q.h
arch/arm/plat-mxc/include/mach/pcie.h
drivers/dma/imx-sdma.c
drivers/input/touchscreen/egalax_ts.c
drivers/media/video/mxc/capture/csi_v4l2_capture.c
drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
drivers/mxc/mlb/mxc_mlb150.c
drivers/mxc/thermal/thermal.c
drivers/net/fec.c
drivers/usb/host/ehci-arc.c
drivers/video/mxc/mxc_ipuv3_fb.c
include/linux/fec.h
sound/soc/imx/imx-wm8962.c
Diffstat (limited to 'drivers')
27 files changed, 1235 insertions, 535 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 9c2419e4fffb..9048f469337f 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -7,7 +7,7 @@ * * Based on code from Freescale: * - * Copyright 2004-2012 Freescale Semiconductor, Inc. + * Copyright 2004-2013 Freescale Semiconductor, Inc. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -33,11 +33,14 @@ #include <linux/platform_device.h> #include <linux/dmaengine.h> #include <linux/delay.h> +#include <linux/genalloc.h> #include <asm/irq.h> #include <mach/sdma.h> #include <mach/dma.h> #include <mach/hardware.h> +#include <mach/iram.h> + /* SDMA registers */ #define SDMA_H_C0PTR 0x000 @@ -328,6 +331,7 @@ struct sdma_engine { struct clk *clk; struct sdma_script_start_addrs *script_addrs; spinlock_t irq_reg_lock; + spinlock_t channel_0_lock; }; #define SDMA_H_CONFIG_DSPDMA (1 << 12) /* indicates if the DSPDMA is used */ @@ -335,6 +339,35 @@ struct sdma_engine { #define SDMA_H_CONFIG_ACR (1 << 4) /* indicates if AHB freq /core freq = 2 or 1 */ #define SDMA_H_CONFIG_CSM (3) /* indicates which context switch mode is selected*/ +#ifdef CONFIG_SDMA_IRAM +static unsigned long sdma_iram_paddr; +static void *sdma_iram_vaddr; +#define sdma_iram_phys_to_virt(p) (sdma_iram_vaddr + ((p) - sdma_iram_paddr)) +#define sdma_iram_virt_to_phys(v) (sdma_iram_paddr + ((v) - sdma_iram_vaddr)) +static struct gen_pool *sdma_iram_pool; + +/*! + * Allocates uncacheable buffer from IRAM + */ +void __iomem *sdma_iram_malloc(size_t size, unsigned long *buf) +{ + *buf = gen_pool_alloc(sdma_iram_pool, size); + if (!buf) + return NULL; + + return sdma_iram_phys_to_virt(*buf); +} + +void sdma_iram_free(unsigned long *buf, u32 size) +{ + if (!sdma_iram_pool) + return; + + gen_pool_free(sdma_iram_pool, buf, size); +} +#endif /*CONFIG_SDMA_IRAM */ + + static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event) { u32 chnenbl0 = (sdma->version == 2 ? SDMA_CHNENBL0_V2 : SDMA_CHNENBL0_V1); @@ -385,14 +418,23 @@ static int sdma_run_channel(struct sdma_channel *sdmac) { struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; + unsigned long timeout = 1000; int ret; - init_completion(&sdmac->done); + writel(1 << channel, sdma->regs + SDMA_H_START); - wmb(); - writel_relaxed(1 << channel, sdma->regs + SDMA_H_START); + while (!(ret = readl_relaxed(sdma->regs + SDMA_H_INTR) & 1)) { + if (timeout-- <= 0) + break; + udelay(1); + } - ret = wait_for_completion_timeout(&sdmac->done, HZ); + if (ret) { + /* Clear the interrupt status */ + writel_relaxed(ret, sdma->regs + SDMA_H_INTR); + } else { + dev_err(sdma->dev, "Timeout waiting for CH0 ready\n"); + } return ret ? 0 : -ETIMEDOUT; } @@ -403,14 +445,21 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd; void *buf_virt; dma_addr_t buf_phys; + unsigned long flags; int ret; +#ifdef CONFIG_SDMA_IRAM + buf_virt = sdma_iram_malloc(size, (unsigned long)&buf_phys); +#else buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); +#endif if (!buf_virt) return -ENOMEM; + spin_lock_irqsave(&sdma->channel_0_lock, flags); + bd0->mode.command = C0_SETPM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; bd0->mode.count = size / 2; @@ -421,7 +470,12 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, ret = sdma_run_channel(&sdma->channel[0]); + spin_unlock_irqrestore(&sdma->channel_0_lock, flags); +#ifdef CONFIG_SDMA_IRAM + sdma_iram_free(buf_phys, size); +#else dma_free_coherent(NULL, size, buf_virt, buf_phys); +#endif return ret; } @@ -518,10 +572,6 @@ static void mxc_sdma_handle_channel(struct sdma_channel *sdmac) { complete(&sdmac->done); - /* not interested in channel 0 interrupts */ - if (sdmac->channel == 0) - return; - switch (sdmac->mode) { case SDMA_MODE_LOOP: sdma_handle_channel_loop(sdmac); @@ -548,6 +598,8 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) spin_lock_irqsave(&sdma->irq_reg_lock, flag); stat = readl_relaxed(sdma->regs + SDMA_H_INTR); + /* not interested in channel 0 interrupts */ + stat &= ~1; writel_relaxed(stat, sdma->regs + SDMA_H_INTR); spin_unlock_irqrestore(&sdma->irq_reg_lock, flag); @@ -696,6 +748,7 @@ static int sdma_load_context(struct sdma_channel *sdmac) struct sdma_context_data *context = sdma->context; struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd; int ret; + unsigned long flags; if (sdmac->direction == DMA_DEV_TO_MEM) @@ -720,6 +773,7 @@ static int sdma_load_context(struct sdma_channel *sdmac) dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", sdmac->event_mask0); dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", sdmac->event_mask1); + spin_lock_irqsave(&sdma->channel_0_lock, flags); memset(context, 0, sizeof(*context)); context->channel_state.pc = load_address; @@ -736,6 +790,7 @@ static int sdma_load_context(struct sdma_channel *sdmac) ret = sdma_run_channel(&sdma->channel[0]); + spin_unlock_irqrestore(&sdma->channel_0_lock, flags); return ret; } @@ -882,7 +937,12 @@ static int sdma_request_channel(struct sdma_channel *sdmac) int channel = sdmac->channel; int ret = -EBUSY; +#ifdef CONFIG_SDMA_IRAM + sdmac->bd = sdma_iram_malloc(sizeof(sdmac->bd), + (unsigned long)&sdmac->bd_phys); +#else sdmac->bd = dma_alloc_noncached(NULL, PAGE_SIZE, &sdmac->bd_phys, GFP_KERNEL); +#endif if (!sdmac->bd) { ret = -ENOMEM; goto out; @@ -1045,8 +1105,11 @@ static void sdma_free_chan_resources(struct dma_chan *chan) sdma_set_channel_priority(sdmac, 0); +#ifdef CONFIG_SDMA_IRAM + sdma_iram_free(sdmac->bd_phys, sizeof(sdmac->bd)); +#else dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); - +#endif clk_disable(sdma->clk); } @@ -1430,10 +1493,22 @@ static int __init sdma_init(struct sdma_engine *sdma) /* Be sure SDMA has not started yet */ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); +#ifdef CONFIG_SDMA_IRAM + /* Allocate memory for SDMA channel and buffer descriptors */ + sdma_iram_vaddr = iram_alloc(SZ_4K, &sdma_iram_paddr); + sdma_iram_pool = gen_pool_create(PAGE_SHIFT/2, -1); + gen_pool_add(sdma_iram_pool, sdma_iram_paddr, SZ_4K, -1); + + sdma->channel_control = sdma_iram_malloc(MAX_DMA_CHANNELS * + sizeof(struct sdma_channel_control) + + sizeof(struct sdma_context_data), + &ccb_phys); +#else sdma->channel_control = dma_alloc_coherent(NULL, MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + sizeof(struct sdma_context_data), &ccb_phys, GFP_KERNEL); +#endif if (!sdma->channel_control) { ret = -ENOMEM; @@ -1501,6 +1576,8 @@ static int __init sdma_probe(struct platform_device *pdev) if (!sdma) return -ENOMEM; + spin_lock_init(&sdma->channel_0_lock); + sdma->dev = &pdev->dev; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/media/video/mxc/capture/csi_v4l2_capture.c b/drivers/media/video/mxc/capture/csi_v4l2_capture.c index 745440911a1f..7e108f7bac03 100644 --- a/drivers/media/video/mxc/capture/csi_v4l2_capture.c +++ b/drivers/media/video/mxc/capture/csi_v4l2_capture.c @@ -42,10 +42,13 @@ static int video_nr = -1; static cam_data *g_cam; +static int req_buf_number; static int csi_v4l2_master_attach(struct v4l2_int_device *slave); static void csi_v4l2_master_detach(struct v4l2_int_device *slave); static u8 camera_power(cam_data *cam, bool cameraOn); +struct v4l2_crop crop_current; +struct v4l2_window win_current; /*! Information about this driver. */ static struct v4l2_int_master csi_v4l2_master = { @@ -62,6 +65,37 @@ static struct v4l2_int_device csi_v4l2_int_device = { }, }; +static struct v4l2_queryctrl pxp_controls[] = { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Rotation", + .minimum = 0, + .maximum = 270, + .step = 90, + .default_value = 0, + .flags = 0, + }, +}; + /* Callback function triggered after PxP receives an EOF interrupt */ static void pxp_dma_done(void *arg) { @@ -108,7 +142,7 @@ static int pxp_chan_init(cam_data *cam) /* * Function to call PxP DMA driver and send our new V4L2 buffer - * through the PxP and PxP will process this buffer in place. + * through the PxP. * Note: This is a blocking call, so upon return the PxP tx should be complete. */ static int pxp_process_update(cam_data *cam) @@ -177,21 +211,41 @@ static int pxp_process_update(cam_data *cam) proc_data->srect.width = pxp_conf->s0_param.width; proc_data->srect.height = pxp_conf->s0_param.height; - proc_data->drect.top = 0; + if (crop_current.c.top != 0) + proc_data->srect.top = crop_current.c.top; + if (crop_current.c.left != 0) + proc_data->srect.left = crop_current.c.left; + if (crop_current.c.width != 0) + proc_data->srect.width = crop_current.c.width; + if (crop_current.c.height != 0) + proc_data->srect.height = crop_current.c.height; + proc_data->drect.left = 0; + proc_data->drect.top = 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; + + if (win_current.w.left != 0) + proc_data->drect.left = win_current.w.left; + if (win_current.w.top != 0) + proc_data->drect.top = win_current.w.top; + if (win_current.w.width != 0) + proc_data->drect.width = win_current.w.width; + if (win_current.w.height != 0) + proc_data->drect.height = win_current.w.height; + + pr_debug("srect l: %d, t: %d, w: %d, h: %d; " + "drect l: %d, t: %d, w: %d, h: %d\n", + proc_data->srect.left, proc_data->srect.top, + proc_data->srect.width, proc_data->srect.height, + proc_data->drect.left, proc_data->drect.top, + proc_data->drect.width, proc_data->drect.height); 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) + if (cam->rotation % 180) pxp_conf->out_param.stride = pxp_conf->out_param.height; else pxp_conf->out_param.stride = pxp_conf->out_param.width; @@ -742,7 +796,7 @@ static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) * camera can change. */ pr_debug("csi_v4l2_s_fmt size changed\n"); } - if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + if (cam->rotation % 180) { height = &f->fmt.pix.width; width = &f->fmt.pix.height; } else { @@ -824,6 +878,11 @@ static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) case V4L2_BUF_TYPE_VIDEO_OVERLAY: pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); cam->win = f->fmt.win; + win_current = f->fmt.win; + size = win_current.w.width * win_current.w.height * 2; + if (cam->v2f.fmt.pix.sizeimage < size) + cam->v2f.fmt.pix.sizeimage = size; + break; default: retval = -EINVAL; @@ -897,6 +956,39 @@ exit: return err; } +static int pxp_set_cstate(cam_data *cam, struct v4l2_control *vc) +{ + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + + if (vc->id == V4L2_CID_HFLIP) { + proc_data->hflip = vc->value; + } else if (vc->id == V4L2_CID_VFLIP) { + proc_data->vflip = vc->value; + } else if (vc->id == V4L2_CID_PRIVATE_BASE) { + if (vc->value % 90) + return -ERANGE; + proc_data->rotate = vc->value; + cam->rotation = vc->value; + } + + return 0; +} + +static int pxp_get_cstate(cam_data *cam, struct v4l2_control *vc) +{ + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + + if (vc->id == V4L2_CID_HFLIP) + vc->value = proc_data->hflip; + else if (vc->id == V4L2_CID_VFLIP) + vc->value = proc_data->vflip; + else if (vc->id == V4L2_CID_PRIVATE_BASE) + vc->value = proc_data->rotate; + + return 0; +} + + /*! * Dequeue one V4L capture buffer * @@ -959,9 +1051,9 @@ static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) * 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; + sg_dma_address(&cam->sg[1]) = + cam->frame[req_buf_number].paddress; retval = pxp_process_update(cam); if (retval) { pr_err("Unable to submit PxP update task.\n"); @@ -970,6 +1062,9 @@ static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) pxp_complete_update(cam); } up(&cam->busy_lock); + memcpy(cam->frame[buf->index].vaddress, + cam->frame[req_buf_number].vaddress, + cam->v2f.fmt.pix.sizeimage); return retval; } @@ -1246,9 +1341,33 @@ static long csi_v4l_do_ioctl(struct file *file, } case VIDIOC_S_CROP: - pr_debug(" case not supported\n"); + { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + retval = -EINVAL; + break; + } + crop->c.width -= crop->c.width % 8; + crop->c.height -= crop->c.height % 8; + + crop_current.c = crop->c; + break; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop *crop = arg; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + retval = -EINVAL; + break; + } + crop->c = crop_current.c; + + break; + + } case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; pr_debug(" case VIDIOC_REQBUFS\n"); @@ -1274,7 +1393,9 @@ static long csi_v4l_do_ioctl(struct file *file, INIT_LIST_HEAD(&cam->working_q); INIT_LIST_HEAD(&cam->done_q); if (req->memory & V4L2_MEMORY_MMAP) - retval = csi_allocate_frame_buf(cam, req->count); + retval = csi_allocate_frame_buf(cam, req->count + 1); + req_buf_number = req->count; + } break; } @@ -1401,16 +1522,63 @@ static long csi_v4l_do_ioctl(struct file *file, } case VIDIOC_S_CTRL: + { + struct v4l2_control *vc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (vc->id == pxp_controls[i].id) { + if (vc->value < pxp_controls[i].minimum || + vc->value > pxp_controls[i].maximum) { + retval = -ERANGE; + break; + } + retval = pxp_set_cstate(cam, vc); + break; + } + + if (i >= ARRAY_SIZE(pxp_controls)) + retval = -EINVAL; + break; + + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *vc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (vc->id == pxp_controls[i].id) { + retval = pxp_get_cstate(cam, vc); + break; + } + + if (i >= ARRAY_SIZE(pxp_controls)) + retval = -EINVAL; + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (qc->id && qc->id == pxp_controls[i].id) { + memcpy(qc, &(pxp_controls[i]), sizeof(*qc)); + break; + } + + if (i >= ARRAY_SIZE(pxp_controls)) + retval = -EINVAL; + break; + } case VIDIOC_G_STD: case VIDIOC_G_OUTPUT: case VIDIOC_S_OUTPUT: case VIDIOC_ENUMSTD: - case VIDIOC_G_CROP: case VIDIOC_CROPCAP: case VIDIOC_S_STD: - case VIDIOC_G_CTRL: case VIDIOC_TRY_FMT: - case VIDIOC_QUERYCTRL: case VIDIOC_ENUMINPUT: case VIDIOC_G_INPUT: case VIDIOC_S_INPUT: @@ -1510,8 +1678,14 @@ static struct video_device csi_v4l_template = { */ static void init_camera_struct(cam_data *cam) { + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; pr_debug("In MVC: %s\n", __func__); + proc_data->hflip = 0; + proc_data->vflip = 0; + proc_data->rotate = 0; + proc_data->bgcolor = 0; + /* Default everything to 0 */ memset(cam, 0, sizeof(cam_data)); @@ -1598,6 +1772,8 @@ static int __devinit csi_v4l2_probe(struct platform_device *pdev) err = -ENOMEM; goto out; } + memset(&crop_current, 0, sizeof(crop_current)); + memset(&win_current, 0, sizeof(win_current)); init_camera_struct(g_cam); platform_set_drvdata(pdev, (void *)g_cam); diff --git a/drivers/media/video/mxc/capture/ipu_fg_overlay_sdc.c b/drivers/media/video/mxc/capture/ipu_fg_overlay_sdc.c index 6d8d4399d7ad..856078a52b6f 100644 --- a/drivers/media/video/mxc/capture/ipu_fg_overlay_sdc.c +++ b/drivers/media/video/mxc/capture/ipu_fg_overlay_sdc.c @@ -396,6 +396,7 @@ static int foreground_start(void *private) fbvar.yres = cam->win.w.height; fbvar.yres_virtual = cam->win.w.height * 2; fbvar.yoffset = 0; + fbvar.vmode &= ~FB_VMODE_YWRAP; fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG; fbvar.activate |= FB_ACTIVATE_FORCE; fb_set_var(fbi, &fbvar); diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.c b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c index 175f9c6820f3..0b2e08c07775 100644 --- a/drivers/media/video/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c @@ -2677,7 +2677,7 @@ static void init_camera_struct(cam_data *cam, struct platform_device *pdev) cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); cam->self->module = THIS_MODULE; - sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi); + sprintf(cam->self->name, "mxc_v4l2_cap%d", pdev->id); cam->self->type = v4l2_int_type_master; cam->self->u.master = &mxc_v4l2_master; } diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 626bad59e151..452c5735e3ea 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -30,8 +30,8 @@ #define UYVY_BLACK (0x00800080) #define RGB_BLACK (0x0) -#define NV12_UV_BLACK (0x80) -#define NV12_Y_BLACK (0x0) +#define UV_BLACK (0x80) +#define Y_BLACK (0x0) #define MAX_FB_NUM 6 #define FB_BUFS 3 @@ -58,6 +58,16 @@ ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P)) +#define IS_PLANAR_PIXEL_FORMAT(format) \ + (format == IPU_PIX_FMT_NV12 || \ + format == IPU_PIX_FMT_YUV420P2 || \ + format == IPU_PIX_FMT_YUV420P || \ + format == IPU_PIX_FMT_YVU420P || \ + format == IPU_PIX_FMT_YUV422P || \ + format == IPU_PIX_FMT_YVU422P || \ + format == IPU_PIX_FMT_YUV444P) + +#define NSEC_PER_FRAME_30FPS (33333333) struct mxc_vout_fb { char *name; @@ -108,12 +118,12 @@ struct mxc_vout_output { struct dma_mem vdoa_output[VDOA_FB_BUFS]; bool timer_stop; - struct timer_list timer; + struct hrtimer timer; struct workqueue_struct *v4l_wq; struct work_struct disp_work; unsigned long frame_count; unsigned long vdi_frame_cnt; - unsigned long start_jiffies; + ktime_t start_ktime; int ctrl_rotate; int ctrl_vflip; @@ -121,7 +131,8 @@ struct mxc_vout_output { dma_addr_t disp_bufs[FB_BUFS]; - struct videobuf_buffer *pre_vb; + struct videobuf_buffer *pre1_vb; + struct videobuf_buffer *pre2_vb; }; struct mxc_vout_dev { @@ -499,29 +510,26 @@ static bool is_pp_bypass(struct mxc_vout_output *vout) static void setup_buf_timer(struct mxc_vout_output *vout, struct videobuf_buffer *vb) { - unsigned long timeout; + ktime_t expiry_time, now; /* if timestamp is 0, then default to 30fps */ - if ((vb->ts.tv_sec == 0) - && (vb->ts.tv_usec == 0) - && vout->start_jiffies) - timeout = - vout->start_jiffies + vout->frame_count * HZ / 30; + if ((vb->ts.tv_sec == 0) && (vb->ts.tv_usec == 0)) + expiry_time = ktime_add_ns(vout->start_ktime, + NSEC_PER_FRAME_30FPS * vout->frame_count); else - timeout = get_jiffies(&vb->ts); + expiry_time = timeval_to_ktime(vb->ts); - if (jiffies >= timeout) { + now = hrtimer_cb_get_time(&vout->timer); + if ((now.tv64 > expiry_time.tv64)) { v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "warning: timer timeout already expired.\n"); + expiry_time = now; } - if (mod_timer(&vout->timer, timeout)) { - v4l2_warn(vout->vfd->v4l2_dev, - "warning: timer was already set\n"); - } + hrtimer_start(&vout->timer, expiry_time, HRTIMER_MODE_ABS); - v4l2_dbg(1, debug, vout->vfd->v4l2_dev, - "timer handler next schedule: %lu\n", timeout); + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "timer handler next " + "schedule: %lldnsecs\n", expiry_time.tv64); } static int show_buf(struct mxc_vout_output *vout, int idx, @@ -598,10 +606,21 @@ static void disp_work_func(struct work_struct *work) } if (deinterlace_3_field(vout)) { if (list_is_singular(&vout->active_list)) { - v4l2_warn(vout->vfd->v4l2_dev, - "no enough entry for 3 fields deinterlacer\n"); - spin_unlock_irqrestore(q->irqlock, flags); - return; + if (list_empty(&vout->queue_list)) { + vout->timer_stop = true; + spin_unlock_irqrestore(q->irqlock, flags); + v4l2_warn(vout->vfd->v4l2_dev, + "no enough entry for 3 fields " + "deinterlacer\n"); + return; + } + + /* + * We need to use the next vb even if it is + * not on the active list. + */ + vb_next = list_first_entry(&vout->queue_list, + struct videobuf_buffer, queue); } else vb_next = list_first_entry(vout->active_list.next, struct videobuf_buffer, queue); @@ -719,21 +738,28 @@ vdi_frame_rate_double: list_del(&vb->queue); /* - * previous videobuf finish show, set VIDEOBUF_DONE state here - * to avoid tearing issue in pp bypass case, which make sure - * showing buffer will not be dequeue to write new data. It also - * bring side-effect that the last buffer can not be dequeue - * correctly, app need take care about it. + * The videobuf before the last one has been shown. Set + * VIDEOBUF_DONE state here to avoid tearing issue in ic bypass + * case, which makes sure a buffer being shown will not be + * dequeued to be overwritten. It also brings side-effect that + * the last 2 buffers can not be dequeued correctly, apps need + * to take care of it. */ - if (vout->pre_vb) { - vout->pre_vb->state = VIDEOBUF_DONE; - wake_up_interruptible(&vout->pre_vb->done); + if (vout->pre2_vb) { + vout->pre2_vb->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->pre2_vb->done); + vout->pre2_vb = NULL; } - if (vout->linear_bypass_pp) - vout->pre_vb = vb; - else { - vout->pre_vb = NULL; + if (vout->linear_bypass_pp) { + vout->pre2_vb = vout->pre1_vb; + vout->pre1_vb = vb; + } else { + if (vout->pre1_vb) { + vout->pre1_vb->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->pre1_vb->done); + vout->pre1_vb = NULL; + } vb->state = VIDEOBUF_DONE; wake_up_interruptible(&vb->done); } @@ -761,10 +787,11 @@ err: return; } -static void mxc_vout_timer_handler(unsigned long arg) +static enum hrtimer_restart mxc_vout_timer_handler(struct hrtimer *timer) { - struct mxc_vout_output *vout = - (struct mxc_vout_output *) arg; + struct mxc_vout_output *vout = container_of(timer, + struct mxc_vout_output, + timer); struct videobuf_queue *q = &vout->vbq; struct videobuf_buffer *vb; unsigned long flags = 0; @@ -777,7 +804,7 @@ static void mxc_vout_timer_handler(unsigned long arg) */ if (list_empty(&vout->queue_list)) { spin_unlock_irqrestore(q->irqlock, flags); - return; + return HRTIMER_NORESTART; } /* move videobuf from queued list to active list */ @@ -792,12 +819,14 @@ static void mxc_vout_timer_handler(unsigned long arg) list_del(&vb->queue); list_add(&vb->queue, &vout->queue_list); spin_unlock_irqrestore(q->irqlock, flags); - return; + return HRTIMER_NORESTART; } vb->state = VIDEOBUF_ACTIVE; spin_unlock_irqrestore(q->irqlock, flags); + + return HRTIMER_NORESTART; } /* Video buffer call backs */ @@ -850,21 +879,21 @@ static void mxc_vout_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) { struct mxc_vout_output *vout = q->priv_data; + struct videobuf_buffer *active_vb; list_add_tail(&vb->queue, &vout->queue_list); vb->state = VIDEOBUF_QUEUED; if (vout->timer_stop) { if (deinterlace_3_field(vout) && - list_empty(&vout->active_list)) { - vb = list_first_entry(&vout->queue_list, + !list_empty(&vout->active_list)) { + active_vb = list_first_entry(&vout->active_list, struct videobuf_buffer, queue); - list_del(&vb->queue); - list_add_tail(&vb->queue, &vout->active_list); + setup_buf_timer(vout, active_vb); } else { setup_buf_timer(vout, vb); - vout->timer_stop = false; } + vout->timer_stop = false; } } @@ -1379,6 +1408,14 @@ static int mxc_vidioc_s_crop(struct file *file, void *fh, /* stride line limitation */ crop->c.height -= crop->c.height % 8; crop->c.width -= crop->c.width % 8; + if ((crop->c.width <= 0) || (crop->c.height <= 0) || + ((crop->c.left + crop->c.width) > (b->left + b->width)) || + ((crop->c.top + crop->c.height) > (b->top + b->height))) { + v4l2_err(vout->vfd->v4l2_dev, "s_crop err: %d, %d, %d, %d", + crop->c.left, crop->c.top, + crop->c.width, crop->c.height); + return -EINVAL; + } /* the same setting, return */ if (vout->disp_support_windows) { @@ -1397,7 +1434,7 @@ static int mxc_vidioc_s_crop(struct file *file, void *fh, /* wait current work finish */ if (vout->vbq.streaming) - cancel_work_sync(&vout->disp_work); + flush_workqueue(vout->v4l_wq); mutex_lock(&vout->task_lock); @@ -1551,7 +1588,7 @@ static int mxc_vidioc_s_ctrl(struct file *file, void *fh, /* wait current work finish */ if (vout->vbq.streaming) - cancel_work_sync(&vout->disp_work); + flush_workqueue(vout->v4l_wq); mutex_lock(&vout->task_lock); switch (ctrl->id) { @@ -1751,7 +1788,10 @@ static int config_disp_output(struct mxc_vout_output *vout) "ERR:%s fb_set_var ret:%d\n", __func__, ret); return ret; } - display_buf_size = fbi->fix.line_length * fbi->var.yres; + if (vout->linear_bypass_pp || vout->tiled_bypass_pp) + display_buf_size = fbi->fix.line_length * fbi->var.yres_virtual; + else + display_buf_size = fbi->fix.line_length * fbi->var.yres; for (i = 0; i < fb_num; i++) vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; if (vout->tiled_bypass_pp) { @@ -1784,11 +1824,11 @@ static int config_disp_output(struct mxc_vout_output *vout) /* fill black when video config changed */ color = colorspaceofpixel(vout->task.output.format) == YUV_CS ? UYVY_BLACK : RGB_BLACK; - if (vout->task.output.format == IPU_PIX_FMT_NV12) { + if (IS_PLANAR_PIXEL_FORMAT(vout->task.output.format)) { size = display_buf_size * 8 / fmt_to_bpp(vout->task.output.format); - memset(fbi->screen_base, NV12_Y_BLACK, size); - memset(fbi->screen_base + size, NV12_UV_BLACK, + memset(fbi->screen_base, Y_BLACK, size); + memset(fbi->screen_base + size, UV_BLACK, display_buf_size - size); } else { pixel = (u32 *)fbi->screen_base; @@ -1812,6 +1852,22 @@ err: return ret; } +static inline void wait_for_vsync(struct mxc_vout_output *vout) +{ + struct fb_info *fbi = vout->fbi; + mm_segment_t old_fs; + + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC, + (unsigned long)NULL); + set_fs(old_fs); + } + + return; +} + static void release_disp_output(struct mxc_vout_output *vout) { struct fb_info *fbi = vout->fbi; @@ -1871,14 +1927,14 @@ static int mxc_vidioc_streamon(struct file *file, void *fh, goto done; } - init_timer(&vout->timer); + hrtimer_init(&vout->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); vout->timer.function = mxc_vout_timer_handler; - vout->timer.data = (unsigned long)vout; vout->timer_stop = true; - vout->start_jiffies = jiffies; + vout->start_ktime = hrtimer_cb_get_time(&vout->timer); - vout->pre_vb = NULL; + vout->pre1_vb = NULL; + vout->pre2_vb = NULL; ret = videobuf_streamon(q); done: @@ -1893,10 +1949,17 @@ static int mxc_vidioc_streamoff(struct file *file, void *fh, int ret = 0; if (q->streaming) { - cancel_work_sync(&vout->disp_work); flush_workqueue(vout->v4l_wq); - del_timer_sync(&vout->timer); + hrtimer_cancel(&vout->timer); + + /* + * Wait for 2 vsyncs to make sure + * frames are drained on triple + * buffer. + */ + wait_for_vsync(vout); + wait_for_vsync(vout); release_disp_output(vout); @@ -2048,9 +2111,13 @@ static int mxc_vout_probe(struct platform_device *pdev) return -ENOMEM; dev->dev = &pdev->dev; - dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), GFP_KERNEL); - *dev->dev->dma_mask = DMA_BIT_MASK(32); - dev->dev->coherent_dma_mask = DMA_BIT_MASK(32); + if (!dev->dev->dma_mask) { + dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), + GFP_KERNEL); + if (dev->dev->dma_mask) + *dev->dev->dma_mask = DMA_BIT_MASK(32); + dev->dev->coherent_dma_mask = DMA_BIT_MASK(32); + } ret = v4l2_device_register(dev->dev, &dev->v4l2_dev); if (ret) { diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index af7ff78c9259..2a5623c5a213 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -284,7 +284,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, mem->size = PAGE_ALIGN(buf->bsize); mem->vaddr = dma_alloc_coherent(q->dev, mem->size, - &mem->dma_handle, GFP_DMA); + &mem->dma_handle, + GFP_DMA | GFP_KERNEL | __GFP_NOFAIL); if (!mem->vaddr) { dev_err(q->dev, "dma_alloc_coherent size %ld failed\n", mem->size); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a2f4b13bce21..8c1d5b2502c2 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -496,7 +496,7 @@ setup_boot_partitions(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int err, busy = 0; - u32 part, new_part; + u32 part; u8 *ext_csd, boot_config; struct mmc_command cmd; struct mmc_card *card = container_of(dev, struct mmc_card, dev); @@ -514,12 +514,28 @@ setup_boot_partitions(struct device *dev, struct device_attribute *attr, /* it's a normal SD/MMC but user request to configure boot partition */ if (card->ext_csd.boot_size <= 0) { - printk(KERN_ERR "%s: this is a normal SD/MMC card" - " but you request to access boot partition!\n", + pr_err("%s: fail to send SWITCH command to card " \ + "to update boot_config of the EXT_CSD!\n", mmc_hostname(card->host)); return -EINVAL; } + /* + * partition must be - + * 0 - user area + * 1 - boot partition 1 + * 2 - boot partition 2 + * DO NOT switch the partitions that used to be accessed + * in OS layer HERE + */ + if (part & EXT_CSD_BOOT_PARTITION_ACCESS_MASK) { + pr_err("%s: DO NOT switch the partitions that used to be\n" \ + " accessed in OS layer HERE. please following the\n" \ + " guidance of Documentation/mmc/mmc-dev-parts.txt.\n", + mmc_hostname(card->host)); + return -EINVAL; + } + ext_csd = kmalloc(512, GFP_KERNEL); if (!ext_csd) { printk(KERN_ERR "%s: could not allocate a buffer to " @@ -574,29 +590,11 @@ setup_boot_partitions(struct device *dev, struct device_attribute *attr, goto err_rtn; } - /* switch the partitions that used to be accessed in OS layer */ - /* partition must be - - * 0 - user area - * 1 - boot partition 1 - * 2 - boot partition 2 - */ - if ((part & EXT_CSD_BOOT_PARTITION_ACCESS_MASK) > 2) { - printk(KERN_ERR "%s: wrong partition id" - " 0 (user area), 1 (boot1), 2 (boot2)\n", - mmc_hostname(card->host)); - err = -EINVAL; - goto err_rtn; - } - - - /* Send SWITCH command to change partition for access */ - boot_config &= ~EXT_CSD_BOOT_PARTITION_ACCESS_MASK; - boot_config |= (part & EXT_CSD_BOOT_PARTITION_ACCESS_MASK); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG, boot_config, card->ext_csd.part_time); if (err) { - printk(KERN_ERR "%s: fail to send SWITCH command" - " to card to swich partition for access!\n", + pr_err("%s: fail to send SWITCH command to card " \ + "to update boot_config of the EXT_CSD!\n", mmc_hostname(card->host)); goto err_rtn; } @@ -634,14 +632,6 @@ setup_boot_partitions(struct device *dev, struct device_attribute *attr, goto err_rtn; } - new_part = ext_csd[EXT_CSD_PART_CONFIG] & - EXT_CSD_BOOT_PARTITION_ACCESS_MASK; - if ((part & EXT_CSD_BOOT_PARTITION_ACCESS_MASK) != new_part) { - printk(KERN_ERR "%s: after SWITCH, current part id %d is not" - " same as requested partition %d!\n", - mmc_hostname(card->host), new_part, part); - goto err_rtn; - } card->ext_csd.boot_config = ext_csd[EXT_CSD_PART_CONFIG]; err_rtn: @@ -669,8 +659,8 @@ setup_boot_bus(struct device *dev, struct device_attribute *attr, sscanf(buf, "%d\n", &boot_bus); if (card->csd.mmca_vsn < CSD_SPEC_VER_4) { - printk(KERN_ERR "%s: invalid mmc version" - " mmc version is below version 4!)\n", + pr_err("%s: fail to send SWITCH command to card " \ + "to update boot_config of the EXT_CSD!\n", mmc_hostname(card->host)); return -EINVAL; } @@ -794,16 +784,6 @@ static ssize_t mmc_boot_info_show(struct device *dev, "Reserved", "User area enabled for boot"}; - char *boot_partition_access[8] = { - "No access to boot partition", - "R/W boot partition 1", - "R/W boot partition 2", - "R/W Replay Protected Memory Block (RPMB)", - "Access to General Purpose partition 1", - "Access to General Purpose partition 2", - "Access to General Purpose partition 3", - "Access to General Purpose partition 4"}; - char *bus_width[4] = { "x1 (sdr) or x4 (ddr) bus width in boot operation mode", "x4 (sdr/ddr) bus width in boot operation mode", @@ -817,7 +797,6 @@ static ssize_t mmc_boot_info_show(struct device *dev, "Reserved"}; int partition; - int access; int width; int mode; u8 *ext_csd = NULL; @@ -830,7 +809,6 @@ static ssize_t mmc_boot_info_show(struct device *dev, mmc_read_ext_csd(card, ext_csd); partition = (card->ext_csd.boot_config >> 3) & 0x7; - access = card->ext_csd.boot_config & 0x7; width = card->ext_csd.boot_bus_width & 0x3; mode = (card->ext_csd.boot_bus_width >> 3) & 0x3; @@ -843,7 +821,6 @@ static ssize_t mmc_boot_info_show(struct device *dev, "boot_partition:0x%02x;\n" " BOOT_ACK:%x - %s\n" " BOOT_PARTITION-ENABLE: %x - %s\n" - " PARTITION_ACCESS:%x - %s\n" "boot_bus:0x%02x\n" " BOOT_MODE:%x - %s\n" " RESET_BOOT_BUS_WIDTH:%x - %s\n" @@ -872,8 +849,6 @@ static ssize_t mmc_boot_info_show(struct device *dev, "No boot acknowledge sent", partition, boot_partition[partition], - access, - boot_partition_access[access], card->ext_csd.boot_bus_width, mode, diff --git a/drivers/mxc/asrc/mxc_asrc.c b/drivers/mxc/asrc/mxc_asrc.c index ecfd236b3e1b..ea6573867a5c 100644 --- a/drivers/mxc/asrc/mxc_asrc.c +++ b/drivers/mxc/asrc/mxc_asrc.c @@ -53,18 +53,18 @@ DEFINE_SPINLOCK(pair_lock); DEFINE_SPINLOCK(input_int_lock); DEFINE_SPINLOCK(output_int_lock); -#define AICPA 0 /* Input Clock Divider A Offset */ -#define AICDA 3 /* Input Clock Prescaler A Offset */ -#define AICPB 6 /* Input Clock Divider B Offset */ -#define AICDB 9 /* Input Clock Prescaler B Offset */ -#define AOCPA 12 /* Output Clock Divider A Offset */ -#define AOCDA 15 /* Output Clock Prescaler A Offset */ -#define AOCPB 18 /* Output Clock Divider B Offset */ -#define AOCDB 21 /* Output Clock Prescaler B Offset */ -#define AICPC 0 /* Input Clock Divider C Offset */ -#define AICDC 3 /* Input Clock Prescaler C Offset */ -#define AOCDC 6 /* Output Clock Prescaler C Offset */ -#define AOCPC 9 /* Output Clock Divider C Offset */ +#define AICPA 0 /* Input Clock Prescaler A Offset */ +#define AICDA 3 /* Input Clock Divider A Offset */ +#define AICPB 6 /* Input Clock Prescaler B Offset */ +#define AICDB 9 /* Input Clock Divider B Offset */ +#define AOCPA 12 /* Output Clock Prescaler A Offset */ +#define AOCDA 15 /* Output Clock Divider A Offset */ +#define AOCPB 18 /* Output Clock Prescaler B Offset */ +#define AOCDB 21 /* Output Clock Divider B Offset */ +#define AICPC 0 /* Input Clock Prescaler C Offset */ +#define AICDC 3 /* Input Clock Divider C Offset */ +#define AOCPC 6 /* Output Clock Prescaler C Offset */ +#define AOCDC 9 /* Output Clock Divider C Offset */ char *asrc_pair_id[] = { [0] = "ASRC RX PAIR A", @@ -144,12 +144,15 @@ static unsigned char output_clk_map_v1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, }; +/* V2 uses the same map for input and output */ static unsigned char input_clk_map_v2[] = { - 0, 1, 2, 3, 4, 5, 0xf, 0xf, 0xf, 8, 9, 0xa, 0xb, 0xc, 0xf, 0xd, +/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf*/ + 0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd, }; static unsigned char output_clk_map_v2[] = { - 8, 9, 0xa, 0, 0xc, 0x5, 0xf, 0xf, 0, 1, 2, 0xf, 0xf, 4, 0xf, 0xd, +/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf*/ + 0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd, }; static unsigned char *input_clk_map, *output_clk_map; @@ -318,34 +321,42 @@ int asrc_req_pair(int chn_num, enum asrc_pair_index *index) int err = 0; unsigned long lock_flags; struct asrc_pair *pair; + int imax = 0, busy = 0, i; + spin_lock_irqsave(&data_lock, lock_flags); - if (chn_num > 2) { - pair = &g_asrc->asrc_pair[ASRC_PAIR_B]; - if (pair->active || (chn_num > pair->chn_max)) - err = -EBUSY; - else { - *index = ASRC_PAIR_B; - pair->chn_num = chn_num; - pair->active = 1; + for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { + pair = &g_asrc->asrc_pair[i]; + if (chn_num > pair->chn_max) { + imax++; + continue; + } else if (pair->active) { + busy++; + continue; } + /* Save the current qualified pair */ + *index = i; + + /* Check if this pair is a perfect one */ + if (chn_num == pair->chn_max) + break; + } + + if (imax >= ASRC_PAIR_MAX_NUM) { + pr_err("No pair could afford requested channel number.\n"); + err = -EINVAL; + } else if (busy >= ASRC_PAIR_MAX_NUM) { + pr_err("All pairs are busy now.\n"); + err = -EBUSY; + } else if (busy + imax >= ASRC_PAIR_MAX_NUM) { + pr_err("All affordable pairs are busy now.\n"); + err = -EBUSY; } else { - pair = &g_asrc->asrc_pair[ASRC_PAIR_A]; - if (pair->active || (pair->chn_max == 0)) { - pair = &g_asrc->asrc_pair[ASRC_PAIR_C]; - if (pair->active || (pair->chn_max == 0)) - err = -EBUSY; - else { - *index = ASRC_PAIR_C; - pair->chn_num = 2; - pair->active = 1; - } - } else { - *index = ASRC_PAIR_A; - pair->chn_num = 2; - pair->active = 1; - } + pair = &g_asrc->asrc_pair[*index]; + pair->chn_num = chn_num; + pair->active = 1; } + spin_unlock_irqrestore(&data_lock, lock_flags); if (!err) { @@ -381,6 +392,9 @@ int asrc_config_pair(struct asrc_config *config) int err = 0; int reg, tmp, channel_num; unsigned long lock_flags; + unsigned long aicp_shift, aocp_shift; + unsigned long asrc_asrcdr_reg, dp_clear_mask; + /* Set the channel number */ reg = __raw_readl(g_asrc->vaddr + ASRC_ASRCNCR_REG); spin_lock_irqsave(&data_lock, lock_flags); @@ -426,142 +440,68 @@ int asrc_config_pair(struct asrc_config *config) __raw_writel(reg, g_asrc->vaddr + ASRC_ASRCTR_REG); /* Default Clock Divider Setting */ - reg = __raw_readl(g_asrc->vaddr + ASRC_ASRCDR1_REG); - if (config->pair == ASRC_PAIR_A) { - reg = __raw_readl(g_asrc->vaddr + ASRC_ASRCDR1_REG); - reg &= 0xfc0fc0; - /* Input Part */ - if ((config->inclk & 0x0f) == INCLK_SPDIF_RX) - reg |= 7 << AICPA; - else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX) - reg |= 6 << AICPA; - else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) { - tmp = - asrc_get_asrck_clock_divider(config-> - input_sample_rate); - reg |= tmp << AICPA; - } else { - if (config->input_word_width == ASRC_WIDTH_16_BIT) - reg |= 5 << AICPA; - else if (config->input_word_width == ASRC_WIDTH_24_BIT) - reg |= 6 << AICPA; - else - err = -EFAULT; - } - /* Output Part */ - if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX) - reg |= 7 << AOCPA; - else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX) - reg |= 6 << AOCPA; - else if (((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) && - ((config->inclk & 0x0f) == INCLK_NONE)) - reg |= 5 << AOCPA; - else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) { - tmp = - asrc_get_asrck_clock_divider(config-> - output_sample_rate); - reg |= tmp << AOCPA; - } else { - if (config->output_word_width == ASRC_WIDTH_16_BIT) - reg |= 5 << AOCPA; - else if (config->output_word_width == ASRC_WIDTH_24_BIT) - reg |= 6 << AOCPA; - else - err = -EFAULT; - } - - __raw_writel(reg, g_asrc->vaddr + ASRC_ASRCDR1_REG); - - } else if (config->pair == ASRC_PAIR_B) { - reg = __raw_readl(g_asrc->vaddr + ASRC_ASRCDR1_REG); - reg &= 0x03f03f; - /* Input Part */ - if ((config->inclk & 0x0f) == INCLK_SPDIF_RX) - reg |= 7 << AICPB; - else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX) - reg |= 6 << AICPB; - else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) { - tmp = - asrc_get_asrck_clock_divider(config-> - input_sample_rate); - reg |= tmp << AICPB; - } else { - if (config->input_word_width == ASRC_WIDTH_16_BIT) - reg |= 5 << AICPB; - else if (config->input_word_width == ASRC_WIDTH_24_BIT) - reg |= 6 << AICPB; - else - err = -EFAULT; - } - /* Output Part */ - if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX) - reg |= 7 << AOCPB; - else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX) - reg |= 6 << AOCPB; - else if (((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) && - ((config->inclk & 0x0f) == INCLK_NONE)) - reg |= 5 << AOCPB; - else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) { - tmp = - asrc_get_asrck_clock_divider(config-> - output_sample_rate); - reg |= tmp << AOCPB; - } else { - if (config->output_word_width == ASRC_WIDTH_16_BIT) - reg |= 5 << AOCPB; - else if (config->output_word_width == ASRC_WIDTH_24_BIT) - reg |= 6 << AOCPB; - else - err = -EFAULT; - } - - __raw_writel(reg, g_asrc->vaddr + ASRC_ASRCDR1_REG); + switch (config->pair) { + case ASRC_PAIR_A: + asrc_asrcdr_reg = ASRC_ASRCDR1_REG; + dp_clear_mask = 0xfc0fc0; + aicp_shift = AICPA; + aocp_shift = AOCPA; + break; + case ASRC_PAIR_B: + asrc_asrcdr_reg = ASRC_ASRCDR1_REG; + dp_clear_mask = 0x03f03f; + aicp_shift = AICPB; + aocp_shift = AOCPB; + break; + case ASRC_PAIR_C: + asrc_asrcdr_reg = ASRC_ASRCDR2_REG; + dp_clear_mask = 0x00; + aicp_shift = AICPC; + aocp_shift = AOCPC; + break; + default: + pr_err("Invalid Pair number %d\n", config->pair); + return -EFAULT; + } + reg = __raw_readl(g_asrc->vaddr + asrc_asrcdr_reg); + reg &= dp_clear_mask; + /* Input Part */ + if ((config->inclk & 0x0f) == INCLK_SPDIF_RX) + reg |= 7 << aicp_shift; + else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX) + reg |= 6 << aicp_shift; + else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) { + tmp = asrc_get_asrck_clock_divider(config->input_sample_rate); + reg |= tmp << aicp_shift; } else { - reg = __raw_readl(g_asrc->vaddr + ASRC_ASRCDR2_REG); - reg &= 0; - /* Input Part */ - if ((config->inclk & 0x0f) == INCLK_SPDIF_RX) - reg |= 7 << AICPC; - else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX) - reg |= 6 << AICPC; - else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) { - tmp = - asrc_get_asrck_clock_divider(config-> - input_sample_rate); - reg |= tmp << AICPC; - } else { - if (config->input_word_width == ASRC_WIDTH_16_BIT) - reg |= 5 << AICPC; - else if (config->input_word_width == ASRC_WIDTH_24_BIT) - reg |= 6 << AICPC; - else - err = -EFAULT; - } - /* Output Part */ - if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX) - reg |= 7 << AOCPC; - else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX) - reg |= 6 << AOCPC; - else if (((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) && - ((config->inclk & 0x0f) == INCLK_NONE)) - reg |= 5 << AOCPC; - else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) { - tmp = - asrc_get_asrck_clock_divider(config-> - output_sample_rate); - reg |= tmp << AOCPC; - } else { - if (config->output_word_width == ASRC_WIDTH_16_BIT) - reg |= 5 << AOCPC; - else if (config->output_word_width == ASRC_WIDTH_24_BIT) - reg |= 6 << AOCPC; - else - err = -EFAULT; - } - __raw_writel(reg, g_asrc->vaddr + ASRC_ASRCDR2_REG); - + if (config->input_word_width == ASRC_WIDTH_16_BIT) + reg |= 5 << aicp_shift; + else if (config->input_word_width == ASRC_WIDTH_24_BIT) + reg |= 6 << aicp_shift; + else + err = -EFAULT; } + /* Output Part */ + if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX) + reg |= 7 << aocp_shift; + else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX) + reg |= 6 << aocp_shift; + else if (((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) + && ((config->inclk & 0x0f) == INCLK_NONE)) + reg |= 5 << aocp_shift; + else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) { + tmp = asrc_get_asrck_clock_divider(config->output_sample_rate); + reg |= tmp << aocp_shift; + } else { + if (config->output_word_width == ASRC_WIDTH_16_BIT) + reg |= 5 << aocp_shift; + else if (config->output_word_width == ASRC_WIDTH_24_BIT) + reg |= 6 << aocp_shift; + else + err = -EFAULT; + } + __raw_writel(reg, g_asrc->vaddr + asrc_asrcdr_reg); /* check whether ideal ratio is a must */ if ((config->inclk & 0x0f) == INCLK_NONE) { @@ -597,25 +537,6 @@ int asrc_config_pair(struct asrc_config *config) } } - if ((config->inclk == INCLK_NONE) && - (config->outclk == OUTCLK_ESAI_TX)) { - reg = __raw_readl(g_asrc->vaddr + ASRC_ASRCTR_REG); - reg &= ~(1 << (20 + config->pair)); - reg |= (0x03 << (13 + (config->pair << 1))); - __raw_writel(reg, g_asrc->vaddr + ASRC_ASRCTR_REG); - err = asrc_set_clock_ratio(config->pair, - config->input_sample_rate, - config->output_sample_rate); - if (err < 0) - return err; - err = asrc_set_process_configuration(config->pair, - config->input_sample_rate, - config-> - output_sample_rate); - if (err < 0) - return err; - } - /* Config input and output wordwidth */ reg = __raw_readl( g_asrc->vaddr + ASRC_ASRMCR1A_REG + (config->pair << 2)); @@ -877,10 +798,10 @@ static int mxc_init_asrc(void) __raw_writel(0x001f00, g_asrc->vaddr + ASRC_ASRTFR1); /* Set the processing clock for 76KHz, 133M */ - __raw_writel(0x30E, g_asrc->vaddr + ASRC_ASR76K_REG); + __raw_writel(0x06D6, g_asrc->vaddr + ASRC_ASR76K_REG); /* Set the processing clock for 56KHz, 133M */ - __raw_writel(0x0426, g_asrc->vaddr + ASRC_ASR56K_REG); + __raw_writel(0x0947, g_asrc->vaddr + ASRC_ASR56K_REG); return 0; } @@ -1035,7 +956,7 @@ static void mxc_free_dma_buf(struct asrc_pair_params *params) } if (params->output_dma_total.dma_vaddr != NULL) { - kfree(params->input_dma_total.dma_vaddr); + kfree(params->output_dma_total.dma_vaddr); params->output_dma_total.dma_vaddr = NULL; } @@ -1847,13 +1768,26 @@ static int asrc_write_proc_attr(struct file *file, const char *buffer, total = 10; else total = 5; - if ((na + nb + nc) != total) { - pr_info("Wrong ASRCNR settings\n"); - return -EFAULT; + + if ((na + nb + nc) > total) { + pr_err("Don't surpass %d for total.\n", total); + return -EINVAL; + } else if (na % 2 != 0 || nb % 2 != 0 || nc % 2 != 0) { + pr_err("Please set an even number for each pair.\n"); + return -EINVAL; + } else if (na < 0 || nb < 0 || nc < 0) { + pr_err("Please set an positive number for each pair.\n"); + return -EINVAL; } + reg = na | (nb << g_asrc->mxc_asrc_data->channel_bits) | (nc << (g_asrc->mxc_asrc_data->channel_bits * 2)); + /* Update chn_max */ + g_asrc->asrc_pair[ASRC_PAIR_A].chn_max = na; + g_asrc->asrc_pair[ASRC_PAIR_B].chn_max = nb; + g_asrc->asrc_pair[ASRC_PAIR_C].chn_max = nc; + clk_enable(g_asrc->mxc_asrc_data->asrc_core_clk); __raw_writel(reg, g_asrc->vaddr + ASRC_ASRCNCR_REG); clk_disable(g_asrc->mxc_asrc_data->asrc_core_clk); diff --git a/drivers/mxc/gpu-viv/Kbuild b/drivers/mxc/gpu-viv/Kbuild index 0b18a7bdcc49..93b12591986d 100644 --- a/drivers/mxc/gpu-viv/Kbuild +++ b/drivers/mxc/gpu-viv/Kbuild @@ -1,6 +1,6 @@ ############################################################################## # -# Copyright (C) 2005 - 2012 by Vivante Corp. +# Copyright (C) 2005 - 2013 by Vivante Corp. # # 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 diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c index c768d8b2b420..3e2acf2f725c 100644 --- a/drivers/mxc/ipu3/ipu_capture.c +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -98,6 +98,7 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; break; case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_GENERIC_16: cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; break; case IPU_PIX_FMT_RGB565: diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 563d5320cc9d..e914a9b7baa3 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -110,6 +110,17 @@ static inline int _ipu_is_primary_disp_chan(uint32_t dma_chan) (dma_chan == 28) || (dma_chan == 41)); } +static inline int _ipu_is_sync_irq(uint32_t irq) +{ + /* sync interrupt register number */ + int reg_num = irq / 32 + 1; + + return ((reg_num == 1) || (reg_num == 2) || (reg_num == 3) || + (reg_num == 4) || (reg_num == 7) || (reg_num == 8) || + (reg_num == 11) || (reg_num == 12) || (reg_num == 13) || + (reg_num == 14) || (reg_num == 15)); +} + #define idma_is_valid(ch) (ch != NO_DMA) #define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0) #define idma_is_set(ipu, reg, dma) (ipu_idmac_read(ipu, reg(dma)) & idma_mask(dma)) @@ -1166,10 +1177,17 @@ int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, rot_mode); } else if (_ipu_is_smfc_chan(dma_chan)) { burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); - if ((pixel_fmt == IPU_PIX_FMT_GENERIC) && - ((_ipu_ch_param_get_bpp(ipu, dma_chan) == 5) || - (_ipu_ch_param_get_bpp(ipu, dma_chan) == 3))) + /* + * This is different from IPUv3 spec, but it is confirmed + * in IPUforum that SMFC burst size should be NPB[6:3] + * when IDMAC works in 16-bit generic data mode. + */ + if (pixel_fmt == IPU_PIX_FMT_GENERIC) + /* 8 bits per pixel */ burst_size = burst_size >> 4; + else if (pixel_fmt == IPU_PIX_FMT_GENERIC_16) + /* 16 bits per pixel */ + burst_size = burst_size >> 3; else burst_size = burst_size >> 2; _ipu_smfc_set_burst_size(ipu, channel, burst_size-1); @@ -2458,23 +2476,40 @@ static irqreturn_t ipu_err_irq_handler(int irq, void *desc) * @param ipu ipu handler * @param irq Interrupt line to enable interrupt for. * + * @return This function returns 0 on success or negative error code on + * fail. */ -void ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq) +int ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq) { uint32_t reg; unsigned long lock_flags; + int ret = 0; _ipu_get(ipu); spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); + /* + * Check sync interrupt handler only, since we do nothing for + * error interrupts but than print out register values in the + * error interrupt source handler. + */ + if (_ipu_is_sync_irq(irq) && (ipu->irq_list[irq].handler == NULL)) { + dev_err(ipu->dev, "handler hasn't been registered on sync " + "irq %d\n", irq); + ret = -EACCES; + goto out; + } + reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); reg |= IPUIRQ_2_MASK(irq); ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); - +out: spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); + + return ret; } EXPORT_SYMBOL(ipu_enable_irq); @@ -2586,6 +2621,7 @@ int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq, { uint32_t reg; unsigned long lock_flags; + int ret = 0; BUG_ON(irq >= IPU_IRQ_COUNT); @@ -2596,8 +2632,19 @@ int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq, if (ipu->irq_list[irq].handler != NULL) { dev_err(ipu->dev, "handler already installed on irq %d\n", irq); - spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); - return -EINVAL; + ret = -EINVAL; + goto out; + } + + /* + * Check sync interrupt handler only, since we do nothing for + * error interrupts but than print out register values in the + * error interrupt source handler. + */ + if (_ipu_is_sync_irq(irq) && (handler == NULL)) { + dev_err(ipu->dev, "handler is NULL for sync irq %d\n", irq); + ret = -EINVAL; + goto out; } ipu->irq_list[irq].handler = handler; @@ -2611,12 +2658,12 @@ int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq, reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); reg |= IPUIRQ_2_MASK(irq); ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); - +out: spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); - return 0; + return ret; } EXPORT_SYMBOL(ipu_request_irq); @@ -2791,6 +2838,7 @@ uint32_t bytes_per_pixel(uint32_t fmt) case IPU_PIX_FMT_YUV444P: return 1; break; + case IPU_PIX_FMT_GENERIC_16: /* generic data */ case IPU_PIX_FMT_RGB565: case IPU_PIX_FMT_YUYV: case IPU_PIX_FMT_UYVY: @@ -2798,6 +2846,7 @@ uint32_t bytes_per_pixel(uint32_t fmt) break; case IPU_PIX_FMT_BGR24: case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: return 3; break; case IPU_PIX_FMT_GENERIC_32: /*generic data */ diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c index cdf76b0ca68c..b10ce2a16f3a 100644 --- a/drivers/mxc/ipu3/ipu_device.c +++ b/drivers/mxc/ipu3/ipu_device.c @@ -680,6 +680,11 @@ static void dump_check_warn(struct device *dev, int warn) static int set_crop(struct ipu_crop *crop, int width, int height, int fmt) { + if ((width == 0) || (height == 0)) { + pr_err("Invalid param: width=%d, height=%d\n", width, height); + return -EINVAL; + } + if ((IPU_PIX_FMT_TILED_NV12 == fmt) || (IPU_PIX_FMT_TILED_NV12F == fmt)) { if (crop->w || crop->h) { @@ -719,6 +724,12 @@ static int set_crop(struct ipu_crop *crop, int width, int height, int fmt) crop->h -= crop->h%8; } + if ((crop->w == 0) || (crop->h == 0)) { + pr_err("Invalid crop param: crop.w=%d, crop.h=%d\n", + crop->w, crop->h); + return -EINVAL; + } + return 0; } @@ -733,26 +744,28 @@ static void update_offset(unsigned int fmt, case IPU_PIX_FMT_YUV420P: *off = pos_y * width + pos_x; *uoff = (width * (height - pos_y) - pos_x) - + ((width/2 * pos_y/2) + pos_x/2); - *voff = *uoff + (width/2 * height/2); + + (width/2) * (pos_y/2) + pos_x/2; + /* In case height is odd, round up to even */ + *voff = *uoff + (width/2) * ((height+1)/2); break; case IPU_PIX_FMT_YVU420P: *off = pos_y * width + pos_x; *voff = (width * (height - pos_y) - pos_x) - + ((width/2 * pos_y/2) + pos_x/2); - *uoff = *voff + (width/2 * height/2); + + (width/2) * (pos_y/2) + pos_x/2; + /* In case height is odd, round up to even */ + *uoff = *voff + (width/2) * ((height+1)/2); break; case IPU_PIX_FMT_YVU422P: *off = pos_y * width + pos_x; *voff = (width * (height - pos_y) - pos_x) - + ((width * pos_y)/2 + pos_x/2); - *uoff = *voff + (width * height)/2; + + (width/2) * pos_y + pos_x/2; + *uoff = *voff + (width/2) * height; break; case IPU_PIX_FMT_YUV422P: *off = pos_y * width + pos_x; *uoff = (width * (height - pos_y) - pos_x) - + (width * pos_y)/2 + pos_x/2; - *voff = *uoff + (width * height)/2; + + (width/2) * pos_y + pos_x/2; + *voff = *uoff + (width/2) * height; break; case IPU_PIX_FMT_YUV444P: *off = pos_y * width + pos_x; @@ -762,7 +775,7 @@ static void update_offset(unsigned int fmt, case IPU_PIX_FMT_NV12: *off = pos_y * width + pos_x; *uoff = (width * (height - pos_y) - pos_x) - + width * pos_y/2 + pos_x; + + width * (pos_y/2) + pos_x; break; case IPU_PIX_FMT_TILED_NV12: /* @@ -799,6 +812,7 @@ static int update_split_setting(struct ipu_task_entry *t, bool vdi_split) struct stripe_param down_stripe; u32 iw, ih, ow, oh; u32 max_width; + int ret; if (t->output.rotate >= IPU_ROTATE_90_RIGHT) return IPU_CHECK_ERR_SPLIT_WITH_ROT; @@ -809,12 +823,26 @@ static int update_split_setting(struct ipu_task_entry *t, bool vdi_split) ow = t->output.crop.w; oh = t->output.crop.h; + memset(&left_stripe, 0, sizeof(left_stripe)); + memset(&right_stripe, 0, sizeof(right_stripe)); + memset(&up_stripe, 0, sizeof(up_stripe)); + memset(&down_stripe, 0, sizeof(down_stripe)); + if (t->set.split_mode & RL_SPLIT) { + /* + * We do want equal strips: initialize stripes in case + * calc_stripes returns before actually doing the calculation + */ + left_stripe.input_width = iw / 2; + left_stripe.output_width = ow / 2; + right_stripe.input_column = iw / 2; + right_stripe.output_column = ow / 2; + if (vdi_split) max_width = soc_max_vdi_in_width(); else max_width = soc_max_out_width(); - ipu_calc_stripes_sizes(iw, + ret = ipu_calc_stripes_sizes(iw, ow, max_width, (((unsigned long long)1) << 32), /* 32bit for fractional*/ @@ -823,6 +851,9 @@ static int update_split_setting(struct ipu_task_entry *t, bool vdi_split) t->output.format, &left_stripe, &right_stripe); + if (ret) + dev_err(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n", + t->task_no, ret); t->set.sp_setting.iw = left_stripe.input_width; t->set.sp_setting.ow = left_stripe.output_width; t->set.sp_setting.outh_resize_ratio = left_stripe.irr; @@ -846,7 +877,15 @@ static int update_split_setting(struct ipu_task_entry *t, bool vdi_split) return IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER; if (t->set.split_mode & UD_SPLIT) { - ipu_calc_stripes_sizes(ih, + /* + * We do want equal strips: initialize stripes in case + * calc_stripes returns before actually doing the calculation + */ + up_stripe.input_width = ih / 2; + up_stripe.output_width = oh / 2; + down_stripe.input_column = ih / 2; + down_stripe.output_column = oh / 2; + ret = ipu_calc_stripes_sizes(ih, oh, soc_max_out_height(), (((unsigned long long)1) << 32), /* 32bit for fractional*/ @@ -855,6 +894,9 @@ static int update_split_setting(struct ipu_task_entry *t, bool vdi_split) t->output.format, &up_stripe, &down_stripe); + if (ret) + dev_err(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n", + t->task_no, ret); t->set.sp_setting.ih = up_stripe.input_width; t->set.sp_setting.oh = up_stripe.output_width; t->set.sp_setting.outv_resize_ratio = up_stripe.irr; diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c index 35b78199b0df..87fa001b8246 100644 --- a/drivers/mxc/ipu3/ipu_disp.c +++ b/drivers/mxc/ipu3/ipu_disp.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2013 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -61,7 +61,7 @@ static unsigned long _ipu_pixel_clk_get_rate(struct clk *clk) { struct ipu_soc *ipu = pixelclk2ipu(clk); u32 div; - u64 final_rate = clk_get_rate(clk->parent) * 16; + u64 final_rate = (unsigned long long)clk_get_rate(clk->parent) * 16; _ipu_get(ipu); div = ipu_di_read(ipu, clk->id, DI_BS_CLKGEN0); @@ -1297,6 +1297,10 @@ int32_t ipu_init_sync_panel(struct ipu_soc *ipu, int disp, uint32_t pixel_clk, msleep(5); /* Get integer portion of divider */ div = clk_get_rate(clk_get_parent(&ipu->pixel_clk[disp])) / rounded_pixel_clk; + if (!div) { + dev_err(ipu->dev, "invalid pixel clk div = 0\n"); + return -EINVAL; + } mutex_lock(&ipu->mutex_lock); diff --git a/drivers/mxc/ipu3/ipu_param_mem.h b/drivers/mxc/ipu3/ipu_param_mem.h index 1387c4948446..6d0a470dfef1 100644 --- a/drivers/mxc/ipu3/ipu_param_mem.h +++ b/drivers/mxc/ipu3/ipu_param_mem.h @@ -1,5 +1,5 @@ /* - * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2013 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -285,6 +285,13 @@ static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch, ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); /* burst size */ break; + case IPU_PIX_FMT_GENERIC_16: + /* Represents 16-bit generic data */ + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + break; case IPU_PIX_FMT_GENERIC_32: /*Represents 32-bit Generic data */ break; @@ -695,6 +702,7 @@ static inline void _ipu_ch_offset_update(struct ipu_soc *ipu, switch (pixel_fmt) { case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_GENERIC_16: case IPU_PIX_FMT_GENERIC_32: case IPU_PIX_FMT_RGB565: case IPU_PIX_FMT_BGR24: diff --git a/drivers/mxc/mlb/mxc_mlb150.c b/drivers/mxc/mlb/mxc_mlb150.c index 6b07d0f0a428..3cc0289c57b8 100755 --- a/drivers/mxc/mlb/mxc_mlb150.c +++ b/drivers/mxc/mlb/mxc_mlb150.c @@ -288,8 +288,8 @@ enum CLK_SPEED { }; struct mlb_ringbuf { - s8 *virt_bufs[TRANS_RING_NODES + 1]; - u32 phy_addrs[TRANS_RING_NODES + 1]; + s8 *virt_bufs[TRANS_RING_NODES]; + u32 phy_addrs[TRANS_RING_NODES]; s32 head; s32 tail; s32 unit_size; @@ -1538,6 +1538,11 @@ static s32 mlb150_trans_complete_check(struct mlb_dev_info *pdevinfo) return -ETIME; } + /* Interrupt from TX can only inform that the data is sent + * to AHB bus, not mean that it is sent to MITB. Thus we add + * a delay here for data to be completed sent. */ + udelay(1000); + return 0; } @@ -1902,24 +1907,27 @@ static int mxc_mlb150_open(struct inode *inode, struct file *filp) pr_err("can not alloc rx/tx buffers: %d\n", buf_size); return ret; } + pr_debug("IRAM Range: Virt 0x%x - 0x%x, Phys 0x%x - 0x%x, size: 0x%x\n", + buf_addr, (buf_addr + buf_size - 1), phy_addr, + (phy_addr + buf_size - 1), buf_size); pdevinfo->rbuf_base_virt = buf_addr; pdevinfo->rbuf_base_phy = phy_addr; memset(buf_addr, 0, buf_size); - for (j = 0; j < (TRANS_RING_NODES + 1); + for (j = 0; j < (TRANS_RING_NODES); ++j, buf_addr += ring_buf_size, phy_addr += ring_buf_size) { pdevinfo->rx_rbuf.virt_bufs[j] = buf_addr; pdevinfo->rx_rbuf.phy_addrs[j] = phy_addr; + pr_debug("RX Ringbuf[%d]: 0x%x 0x%x\n", j, buf_addr, phy_addr); } pdevinfo->rx_rbuf.unit_size = ring_buf_size; pdevinfo->rx_rbuf.total_size = buf_size; - buf_addr += ring_buf_size; - phy_addr += ring_buf_size; for (j = 0; j < (TRANS_RING_NODES); ++j, buf_addr += ring_buf_size, phy_addr += ring_buf_size) { pdevinfo->tx_rbuf.virt_bufs[j] = buf_addr; pdevinfo->tx_rbuf.phy_addrs[j] = phy_addr; + pr_debug("TX Ringbuf[%d]: 0x%x 0x%x\n", j, buf_addr, phy_addr); } pdevinfo->tx_rbuf.unit_size = ring_buf_size; @@ -2347,9 +2355,8 @@ static ssize_t mxc_mlb150_write(struct file *filp, const char __user *buf, /* Set ADT for TX */ mlb150_dev_pipo_next(ahb_ch, ctype, adt_sts, tx_buf_ptr); - } else { + } else read_unlock_irqrestore(&tx_rbuf->rb_lock, flags); - } ret = count; out: @@ -2670,10 +2677,10 @@ static int mxc_mlb150_resume(struct platform_device *pdev) { struct mlb_data *drvdata = platform_get_drvdata(pdev); - mlb150_dev_init(); - clk_enable(drvdata->clk_mlb6p); + mlb150_dev_init(); + return 0; } #else diff --git a/drivers/mxc/thermal/thermal.c b/drivers/mxc/thermal/thermal.c index 4931ce1fa252..0cc5e5973271 100644 --- a/drivers/mxc/thermal/thermal.c +++ b/drivers/mxc/thermal/thermal.c @@ -277,6 +277,7 @@ static int anatop_thermal_get_temp(struct thermal_zone_device *thermal, struct anatop_thermal *tz = thermal->devdata; unsigned int tmp; unsigned int reg; + unsigned int val; if (!tz) return -EINVAL; @@ -311,11 +312,16 @@ static int anatop_thermal_get_temp(struct thermal_zone_device *thermal, anatop_base + HW_ANADIG_TEMPSENSE0_SET); tmp = 0; + val = jiffies; /* read temperature values */ while ((__raw_readl(anatop_base + HW_ANADIG_TEMPSENSE0) - & BM_ANADIG_TEMPSENSE0_FINISHED) == 0) + & BM_ANADIG_TEMPSENSE0_FINISHED) == 0) { + if (time_after(jiffies, (unsigned long)(val + HZ / 2))) { + pr_info("Thermal sensor timeout, retry!\n"); + return 0; + } msleep(10); - + } reg = __raw_readl(anatop_base + HW_ANADIG_TEMPSENSE0); tmp = (reg & BM_ANADIG_TEMPSENSE0_TEMP_VALUE) >> BP_ANADIG_TEMPSENSE0_TEMP_VALUE; @@ -339,6 +345,11 @@ static int anatop_thermal_get_temp(struct thermal_zone_device *thermal, *temp = (cooling_device_disable && tz->temperature >= KELVIN_TO_CEL(TEMP_CRITICAL, KELVIN_OFFSET)) ? KELVIN_TO_CEL(TEMP_CRITICAL - 1, KELVIN_OFFSET) : tz->temperature; + /* Set alarm threshold if necessary */ + if ((__raw_readl(anatop_base + HW_ANADIG_TEMPSENSE0) & + BM_ANADIG_TEMPSENSE0_ALARM_VALUE) == 0) + anatop_update_alarm(raw_critical); + return 0; } @@ -883,7 +894,7 @@ static int anatop_thermal_counting_ratio(unsigned int fuse_data) raw_hot = (fuse_data & 0xfff00) >> 8; hot_temp = fuse_data & 0xff; - if (!calibration_valid && !cpu_is_mx6sl()) + if (!calibration_valid) /* * The universal equation for thermal sensor * is slope = 0.4297157 - (0.0015976 * 25C fuse), @@ -901,7 +912,6 @@ static int anatop_thermal_counting_ratio(unsigned int fuse_data) /* Init default critical temp to set alarm */ raw_critical = raw_25c - ratio * (KELVIN_TO_CEL(TEMP_CRITICAL, KELVIN_OFFSET) - 25) / 100; clk_enable(pll3_clk); - anatop_update_alarm(raw_critical); return ret; } diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c index 708e8e97112a..8c36d6abf852 100644 --- a/drivers/mxc/vpu/mxc_vpu.c +++ b/drivers/mxc/vpu/mxc_vpu.c @@ -126,7 +126,7 @@ static int vpu_alloc_dma_buffer(struct vpu_mem_desc *mem) mem->cpu_addr = (unsigned long) dma_alloc_coherent(NULL, PAGE_ALIGN(mem->size), (dma_addr_t *) (&mem->phy_addr), - GFP_DMA | GFP_KERNEL); + GFP_DMA | GFP_KERNEL | __GFP_NOFAIL); pr_debug("[ALLOC] mem alloc cpu_addr = 0x%x\n", mem->cpu_addr); if ((void *)(mem->cpu_addr) == NULL) { printk(KERN_ERR "Physical memory allocation error!\n"); @@ -258,7 +258,7 @@ static int vpu_open(struct inode *inode, struct file *filp) #ifdef CONFIG_SOC_IMX6Q clk_enable(vpu_clk); if (READ_REG(BIT_CUR_PC)) - printk(KERN_DEBUG "Not power off before vpu open!\n"); + pr_debug("Not power off before vpu open!\n"); clk_disable(vpu_clk); #endif } @@ -528,6 +528,20 @@ static long vpu_ioctl(struct file *filp, u_int cmd, } break; } + case VPU_IOC_LOCK_DEV: + { + u32 lock_en; + + if (get_user(lock_en, (u32 __user *) arg)) + return -EFAULT; + + if (lock_en) + mutex_lock(&vpu_data.lock); + else + mutex_unlock(&vpu_data.lock); + + break; + } default: { printk(KERN_ERR "No such IOCTL, cmd is %d\n", cmd); @@ -1055,6 +1069,16 @@ static void __exit vpu_exit(void) vpu_free_dma_buffer(&pic_para_mem); vpu_free_dma_buffer(&user_data_mem); + /* reset VPU state */ + if (!IS_ERR(vpu_regulator)) + regulator_enable(vpu_regulator); + clk_enable(vpu_clk); + if (vpu_plat->reset) + vpu_plat->reset(); + clk_disable(vpu_clk); + if (!IS_ERR(vpu_regulator)) + regulator_disable(vpu_regulator); + clk_put(vpu_clk); platform_driver_unregister(&mxcvpu_driver); diff --git a/drivers/net/fec.c b/drivers/net/fec.c index ebb09eb9a6f2..d2fea018f927 100755 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -636,7 +636,7 @@ static int fec_rx_poll(struct napi_struct *napi, int budget) data = (__u8 *)__va(bdp->cbd_bufaddr); if (bdp->cbd_bufaddr) - dma_unmap_single(&ndev->dev, bdp->cbd_bufaddr, + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) @@ -664,7 +664,7 @@ static int fec_rx_poll(struct napi_struct *napi, int budget) netif_receive_skb(skb); } - bdp->cbd_bufaddr = dma_map_single(&ndev->dev, data, + bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data, FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); rx_processing_done: /* Clear the status flags for this buffer */ @@ -1873,32 +1873,33 @@ fec_probe(struct platform_device *pdev) if (pdata) fep->phy_interface = pdata->phy; -#ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO - gpio_request(pdata->gpio_irq, "gpio_enet_irq"); - gpio_direction_input(pdata->gpio_irq); + if (pdata->gpio_irq < 0) { + gpio_request(pdata->gpio_irq, "gpio_enet_irq"); + gpio_direction_input(pdata->gpio_irq); - irq = gpio_to_irq(pdata->gpio_irq); - ret = request_irq(irq, fec_enet_interrupt, - IRQF_TRIGGER_RISING, - pdev->name, ndev); - if (ret) - goto failed_irq; -#else - /* This device has up to three irqs on some platforms */ - for (i = 0; i < 3; i++) { - irq = platform_get_irq(pdev, i); - if (i && irq < 0) - break; - ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev); - if (ret) { - while (--i >= 0) { - irq = platform_get_irq(pdev, i); - free_irq(irq, ndev); - } + irq = gpio_to_irq(pdata->gpio_irq); + ret = request_irq(irq, fec_enet_interrupt, + IRQF_TRIGGER_RISING, + pdev->name, ndev); + if (ret) goto failed_irq; + } else { + /* This device has up to three irqs on some platforms */ + for (i = 0; i < 3; i++) { + irq = platform_get_irq(pdev, i); + if (i && irq < 0) + break; + ret = request_irq(irq, fec_enet_interrupt, + IRQF_DISABLED, pdev->name, ndev); + if (ret) { + while (--i >= 0) { + irq = platform_get_irq(pdev, i); + free_irq(irq, ndev); + } + goto failed_irq; + } } } -#endif fep->clk = clk_get(&pdev->dev, "fec_clk"); if (IS_ERR(fep->clk)) { @@ -1949,15 +1950,15 @@ failed_init: clk_disable(fep->clk); clk_put(fep->clk); failed_clk: -#ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO - free_irq(irq, ndev); -#else - for (i = 0; i < 3; i++) { - irq = platform_get_irq(pdev, i); - if (irq > 0) - free_irq(irq, ndev); + if (pdata->gpio_irq < 0) + free_irq(irq, ndev); + else { + for (i = 0; i < 3; i++) { + irq = platform_get_irq(pdev, i); + if (irq > 0) + free_irq(irq, ndev); + } } -#endif failed_irq: iounmap(fep->hwp); failed_ioremap: diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 8ab80b70d276..bd356c40cec5 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -631,9 +631,9 @@ static int pfuze100_regulator_set_voltage_time_sel(struct regulator_dev *rdev, * 02: 8us, * 03: 16us, */ - step_delay >>= 5; + step_delay >>= 6; step_delay &= 0x3; - step_delay <<= 1; + step_delay = 2 << step_delay; if (pfuze100_regulators[id].voltages[old_sel] < pfuze100_regulators[id].voltages[new_sel]) diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c index f3d3d3b4ee5a..e40a8ad0e460 100755 --- a/drivers/usb/gadget/arcotg_udc.c +++ b/drivers/usb/gadget/arcotg_udc.c @@ -70,7 +70,6 @@ #endif #define DMA_ADDR_INVALID (~(dma_addr_t)0) DEFINE_MUTEX(udc_resume_mutex); -extern void usb_debounce_id_vbus(void); static const char driver_name[] = "fsl-usb2-udc"; static const char driver_desc[] = DRIVER_DESC; @@ -960,11 +959,8 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, (unsigned)EP_MAX_LENGTH_TRANSFER); if (NEED_IRAM(req->ep)) *length = min(*length, g_iram_size); -#ifdef CONFIG_FSL_UTP + dtd = dma_pool_alloc_nonbufferable(udc_controller->td_pool, GFP_ATOMIC, dma); -#else - dtd = dma_pool_alloc(udc_controller->td_pool, GFP_ATOMIC, dma); -#endif if (dtd == NULL) return dtd; @@ -3075,10 +3071,10 @@ static int __devinit fsl_udc_probe(struct platform_device *pdev) * do platform specific init: check the clock, grab/config pins, etc. */ if (pdata->init && pdata->init(pdev)) { - pdata->lowpower = false; ret = -ENODEV; goto err2a; } + pdata->lowpower = false; spin_lock_init(&pdata->lock); @@ -3234,6 +3230,7 @@ err4: err3: free_irq(udc_controller->irq, udc_controller); err2: + dr_phy_low_power_mode(udc_controller, true); if (pdata->exit) pdata->exit(pdata->pdev); err2a: @@ -3269,6 +3266,8 @@ static int fsl_udc_remove(struct platform_device *pdev) dr_wake_up_enable(udc_controller, false); dr_discharge_line(pdata, true); + + dr_clk_gate(false); /* DR has been stopped in usb_gadget_unregister_driver() */ remove_proc_file(); @@ -3303,9 +3302,9 @@ static int fsl_udc_remove(struct platform_device *pdev) device_unregister(&udc_controller->gadget.dev); /* free udc --wait for the release() finished */ wait_for_completion(&done); + /* - * do platform specific un-initialization: - * release iomux pins, etc. + * do platform specific un-initialization */ if (pdata->exit) pdata->exit(pdata->pdev); @@ -3449,7 +3448,6 @@ static int fsl_udc_resume(struct platform_device *pdev) u32 temp; if (udc_controller->stopped) dr_clk_gate(true); - usb_debounce_id_vbus(); if (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) { temp = fsl_readl(&dr_regs->otgsc); /* if b_session_irq_en is cleared by otg */ @@ -3496,7 +3494,6 @@ static int fsl_udc_resume(struct platform_device *pdev) dr_clk_gate(true); dr_wake_up_enable(udc_controller, false); dr_phy_low_power_mode(udc_controller, false); - usb_debounce_id_vbus(); /* if in host mode, we need to do nothing */ if ((fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) == 0) { dr_phy_low_power_mode(udc_controller, true); diff --git a/drivers/usb/host/ehci-arc.c b/drivers/usb/host/ehci-arc.c index e2000bd2e635..dde47dd76e5f 100755 --- a/drivers/usb/host/ehci-arc.c +++ b/drivers/usb/host/ehci-arc.c @@ -246,10 +246,10 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, * do platform specific init: check the clock, grab/config pins, etc. */ if (pdata->init && pdata->init(pdev)) { - pdata->lowpower = false; retval = -ENODEV; goto err4; } + pdata->lowpower = false; spin_lock_init(&pdata->lock); @@ -315,6 +315,9 @@ err2: usb_put_hcd(hcd); err1: dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); + fsl_usb_lowpower_mode(pdata, true); + if (pdata->usb_clock_for_pm) + pdata->usb_clock_for_pm(false); if (pdata->exit && pdata->pdev) pdata->exit(pdata->pdev); return retval; @@ -333,7 +336,6 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - u32 tmp; if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { /* Need open clock for register access */ @@ -343,40 +345,44 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, /*disable the wakeup to avoid an abnormal wakeup interrupt*/ usb_host_set_wakeup(hcd->self.controller, false); - tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); - if (tmp & PORT_PTS_PHCD) { - tmp &= ~PORT_PTS_PHCD; - ehci_writel(ehci, tmp, &ehci->regs->port_status[0]); - msleep(100); - } + /* Put the PHY out of low power mode */ + fsl_usb_lowpower_mode(pdata, false); } + /* disable the host wakeup */ + usb_host_set_wakeup(hcd->self.controller, false); + /*free the ehci_fsl_pre_irq */ + free_irq(hcd->irq, (void *)pdev); + + usb_remove_hcd(hcd); + + ehci_port_power(ehci, 0); + + iounmap(hcd->regs); + if (ehci->transceiver) { (void)otg_set_host(ehci->transceiver, 0); otg_put_transceiver(ehci->transceiver); } else { release_mem_region(hcd->rsrc_start, hcd->rsrc_len); } - /*disable the host wakeup and put phy to low power mode */ - usb_host_set_wakeup(hcd->self.controller, false); - /*free the ehci_fsl_pre_irq */ - free_irq(hcd->irq, (void *)pdev); - usb_remove_hcd(hcd); + usb_put_hcd(hcd); fsl_usb_lowpower_mode(pdata, true); - /* DDD shouldn't we turn off the power here? */ - fsl_platform_set_vbus_power(pdata, 0); + /* Close the VBUS */ + if (pdata->xcvr_ops && pdata->xcvr_ops->set_vbus_power) + pdata->xcvr_ops->set_vbus_power(pdata->xcvr_ops, pdata, 0); + + if (pdata->usb_clock_for_pm) + pdata->usb_clock_for_pm(false); /* - * do platform specific un-initialization: - * release iomux pins clocks, etc. + * do platform specific un-initialization */ if (pdata->exit && pdata->pdev) pdata->exit(pdata->pdev); - - iounmap(hcd->regs); } static void fsl_setup_phy(struct ehci_hcd *ehci, @@ -676,17 +682,32 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev, /* Only handles OTG mode switch event, system suspend event will be done in bus suspend */ if (pdata->pmflags == 0) { printk(KERN_DEBUG "%s, pm event \n", __func__); + disable_irq(hcd->irq); if (!host_can_wakeup_system(pdev)) { /* Need open clock for register access */ fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + /* + * Disable wakeup interrupt, since there is wakeup + * when phcd from 1->0 if wakeup interrupt is enabled + */ + usb_host_set_wakeup(hcd->self.controller, false); + + /* + * Open PHY's clock, then the wakeup settings + * can be wroten correctly + */ + fsl_usb_lowpower_mode(pdata, false); usb_host_set_wakeup(hcd->self.controller, false); + fsl_usb_lowpower_mode(pdata, true); + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); } else { if (pdata->platform_phy_power_on) pdata->platform_phy_power_on(); } + enable_irq(hcd->irq); printk(KERN_DEBUG "host suspend ends\n"); return 0; @@ -758,13 +779,18 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev, return 0; } +#define OTGSC_OFFSET 0x64 +#define OTGSC_ID_VALUE (1 << 8) +#define OTGSC_ID_INT_STS (1 << 16) static int ehci_fsl_drv_resume(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct usb_device *roothub = hcd->self.root_hub; unsigned long flags; - u32 tmp; + u32 tmp, otgsc; + bool id_changed; + int id_value; struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; struct fsl_usb2_wakeup_platform_data *wake_up_pdata = pdata->wakeup_pdata; /* Only handles OTG mode switch event */ @@ -772,29 +798,36 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev) if (pdata->pmflags == 0) { printk(KERN_DEBUG "%s,pm event, wait for wakeup irq if needed\n", __func__); wait_event_interruptible(wake_up_pdata->wq, !wake_up_pdata->usb_wakeup_is_pending); + disable_irq(hcd->irq); if (!host_can_wakeup_system(pdev)) { /* Need open clock for register access */ fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + fsl_usb_lowpower_mode(pdata, false); - usb_host_set_wakeup(hcd->self.controller, true); - -#ifndef NO_FIX_DISCONNECT_ISSUE - /*Unplug&plug device during suspend without remote wakeup enabled - For Low and full speed device, we should power on and power off - the USB port to make sure USB internal state machine work well. - */ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); - if ((tmp & PORT_CONNECT) && !(tmp & PORT_SUSPEND) && - ((tmp & (0x3<<26)) != (0x2<<26))) { - printk(KERN_DEBUG "%s will do power off and power on port.\n", pdata->name); - ehci_writel(ehci, tmp & ~(PORT_RWC_BITS | PORT_POWER), - &ehci->regs->port_status[0]); - ehci_writel(ehci, tmp | PORT_POWER, - &ehci->regs->port_status[0]); + if (pdata->operating_mode == FSL_USB2_DR_OTG) { + otgsc = ehci_readl(ehci, (u32 __iomem *)ehci->regs + OTGSC_OFFSET / 4); + id_changed = !!(otgsc & OTGSC_ID_INT_STS); + id_value = !!(otgsc & OTGSC_ID_VALUE); + if (((tmp & PORT_CONNECT) && !id_value) || id_changed) { + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } else if (!(tmp & PORT_CONNECT)) { + usb_host_set_wakeup(hcd->self.controller, true); + fsl_usb_lowpower_mode(pdata, true); + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + } + } else { + if (tmp & PORT_CONNECT) { + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } else { + usb_host_set_wakeup(hcd->self.controller, true); + fsl_usb_lowpower_mode(pdata, true); + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + } + } -#endif - fsl_usb_clk_gate(hcd->self.controller->platform_data, false); } + enable_irq(hcd->irq); return 0; } if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 9fd3c87babfb..00bd2e609525 100755 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -690,9 +690,16 @@ static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, return -ENODEV; if (!gadget) { - if (!otg_dev->otg.default_a) + /* + * At i.mx platform, we still not implement fully + * OTG. + */ + /* + if (!otg_dev->otg.default_a) { otg_p->gadget->ops->vbus_draw(otg_p->gadget, 0); - usb_gadget_vbus_disconnect(otg_dev->otg.gadget); + usb_gadget_vbus_disconnect(otg_dev->otg.gadget); + } + */ otg_dev->otg.gadget = 0; otg_dev->fsm.b_bus_req = 0; pdata->port_enables = 0; diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index a3e374bc5158..721ff88660df 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -207,7 +207,9 @@ int mxc_edid_fb_mode_is_equal(bool use_aspect, mode1->upper_margin == mode2->upper_margin && mode1->lower_margin == mode2->lower_margin && mode1->sync == mode2->sync && - mode1->refresh == mode2->refresh && + /* refresh check, 59.94Hz and 60Hz have the same parameter + * in struct of mxc_cea_mode */ + abs(mode1->refresh - mode2->refresh) <= 1 && (mode1->vmode & mask) == (mode2->vmode & mask)); } diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c index 0fd076537fb8..ebec7bcfcfa6 100644 --- a/drivers/video/mxc/mxc_elcdif_fb.c +++ b/drivers/video/mxc/mxc_elcdif_fb.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Freescale Semiconductor, Inc. + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. */ /* @@ -940,14 +940,14 @@ static int mxc_elcdif_fb_set_par(struct fb_info *fbi) setup_dotclk_panel((PICOS2KHZ(fbi->var.pixclock)) * 1000UL, fbi->var.vsync_len, - fbi->var.upper_margin + - fbi->var.yres + fbi->var.lower_margin, - fbi->var.upper_margin, + fbi->var.upper_margin + fbi->var.yres + + fbi->var.lower_margin + fbi->var.vsync_len, + fbi->var.upper_margin + fbi->var.vsync_len, fbi->var.yres, fbi->var.hsync_len, - fbi->var.left_margin + - fbi->var.xres + fbi->var.right_margin, - fbi->var.left_margin, + fbi->var.left_margin + fbi->var.xres + + fbi->var.right_margin + fbi->var.hsync_len, + fbi->var.left_margin + fbi->var.hsync_len, fbi->var.xres, bpp_to_pixfmt(fbi), data->output_pix_fmt, diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c index 1122e219642b..4103498dc1b0 100644 --- a/drivers/video/mxc/mxc_epdc_fb.c +++ b/drivers/video/mxc/mxc_epdc_fb.c @@ -2253,7 +2253,7 @@ static int epdc_submit_merge(struct update_desc_list *upd_desc_list, /* Merged update should take on the earliest order */ upd_desc_list->update_order = (upd_desc_list->update_order > update_to_merge->update_order) ? - update_to_merge->update_order : upd_desc_list->update_order; + upd_desc_list->update_order : update_to_merge->update_order; return MERGE_OK; } diff --git a/drivers/video/mxc/mxcfb_sii902x_elcdif.c b/drivers/video/mxc/mxcfb_sii902x_elcdif.c index ceab3e66e91c..ecba5b8b380d 100644 --- a/drivers/video/mxc/mxcfb_sii902x_elcdif.c +++ b/drivers/video/mxc/mxcfb_sii902x_elcdif.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -238,11 +238,6 @@ static void det_worker(struct work_struct *work) 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"); @@ -286,18 +281,15 @@ static void det_worker(struct work_struct *work) sii902x.fbi->flags &= ~FBINFO_MISC_USEREVENT; console_unlock(); } - - console_lock(); - fb_blank(sii902x.fbi, FB_BLANK_UNBLANK); - console_unlock(); + /* Power on sii902x */ + sii902x_poweron(); } } 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(); + /* Power off sii902x */ + sii902x_poweroff(); } kobject_uevent_env(&sii902x.pdev->dev.kobj, KOBJ_CHANGE, envp); } @@ -461,18 +453,6 @@ static int __devexit sii902x_remove(struct i2c_client *client) 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; @@ -522,8 +502,6 @@ static struct i2c_driver sii902x_i2c_driver = { }, .probe = sii902x_probe, .remove = sii902x_remove, - .suspend = sii902x_suspend, - .resume = sii902x_resume, .id_table = sii902x_id, }; diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 9c9620c67f1d..8902fb2b0b5e 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -54,6 +54,7 @@ #include <linux/types.h> #include <linux/switch.h> +#include "edid.h" #include <mach/mxc_edid.h> #include "mxc/mxc_dispdrv.h" @@ -167,6 +168,7 @@ struct mxc_hdmi { struct clk *hdmi_isfr_clk; struct clk *hdmi_iahb_clk; struct delayed_work hotplug_work; + struct delayed_work hdcp_hdp_work; struct notifier_block nb; struct hdmi_data_info hdmi_data; @@ -192,7 +194,10 @@ struct mxc_hdmi { struct switch_dev sdev_display; }; +static int hdmi_major; +static struct class *hdmi_class; struct i2c_client *hdmi_i2c; +struct mxc_hdmi *g_hdmi; static bool hdmi_inited; @@ -200,6 +205,8 @@ extern const struct fb_videomode mxc_cea_mode[64]; extern void mxc_hdmi_cec_handle(u16 cec_stat); static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event); +static void mxc_hdmi_enable_pins(struct mxc_hdmi *hdmi); +static void mxc_hdmi_disable_pins(struct mxc_hdmi *hdmi); #ifdef DEBUG static void dump_fb_videomode(struct fb_videomode *m) @@ -296,6 +303,51 @@ static DEVICE_ATTR(rgb_out_enable, S_IRUGO | S_IWUSR, mxc_hdmi_show_rgb_out_enable, mxc_hdmi_store_rgb_out_enable); +static ssize_t mxc_hdmi_show_hdcp_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + + if (hdmi->hdmi_data.hdcp_enable == false) + strcpy(buf, "hdcp disable\n"); + else + strcpy(buf, "hdcp enable\n"); + + return strlen(buf); +} + +static ssize_t mxc_hdmi_store_hdcp_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + char event_string[32]; + char *envp[] = { event_string, NULL }; + unsigned long value; + int ret; + + ret = strict_strtoul(buf, 10, &value); + if (ret) + return ret; + + hdmi->hdmi_data.hdcp_enable = value; + + /* Reconfig HDMI for HDCP */ + mxc_hdmi_setup(hdmi, 0); + + if (hdmi->hdmi_data.hdcp_enable == false) { + sprintf(event_string, "EVENT=hdcpdisable"); + kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); + } else { + sprintf(event_string, "EVENT=hdcpenable"); + kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); + } + + return count; + +} + +static DEVICE_ATTR(hdcp_enable, S_IRUGO | S_IWUSR, + mxc_hdmi_show_hdcp_enable, mxc_hdmi_store_hdcp_enable); /*! * this submodule is responsible for the video data synchronization. @@ -830,6 +882,45 @@ static int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data, } #endif +static bool hdmi_edid_wait_i2c_done(struct mxc_hdmi *hdmi, int msec) +{ + unsigned char val = 0; + val = hdmi_readb(HDMI_IH_I2CM_STAT0) & 0x2; + while (val == 0) { + udelay(1000); + if (msec-- == 0) { + dev_dbg(&hdmi->pdev->dev, + "HDMI EDID i2c operation time out!!\n"); + return false; + } + val = hdmi_readb(HDMI_IH_I2CM_STAT0) & 0x2; + } + return true; +} + +static u8 hdmi_edid_i2c_read(struct mxc_hdmi *hdmi, + u8 addr, u8 blockno) +{ + u8 spointer = blockno / 2; + u8 edidaddress = ((blockno % 2) * 0x80) + addr; + u8 data; + + hdmi_writeb(0xFF, HDMI_IH_I2CM_STAT0); + hdmi_writeb(edidaddress, HDMI_I2CM_ADDRESS); + hdmi_writeb(spointer, HDMI_I2CM_SEGADDR); + if (spointer == 0) + hdmi_writeb(HDMI_I2CM_OPERATION_READ, + HDMI_I2CM_OPERATION); + else + hdmi_writeb(HDMI_I2CM_OPERATION_READ_EXT, + HDMI_I2CM_OPERATION); + + hdmi_edid_wait_i2c_done(hdmi, 1000); + data = hdmi_readb(HDMI_I2CM_DATAI); + hdmi_writeb(0xFF, HDMI_IH_I2CM_STAT0); + return data; +} + /* "Power-down enable (active low)" * That mean that power up == 1! */ static void mxc_hdmi_phy_enable_power(u8 enable) @@ -1174,28 +1265,13 @@ static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) static void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) { - u8 de, val; - - if (hdmi->hdmi_data.video_mode.mDataEnablePolarity) - de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; - else - de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; - - /* disable rx detect */ - val = hdmi_readb(HDMI_A_HDCPCFG0); - val &= HDMI_A_HDCPCFG0_RXDETECT_MASK; - val |= HDMI_A_HDCPCFG0_RXDETECT_DISABLE; - hdmi_writeb(val, HDMI_A_HDCPCFG0); - - val = hdmi_readb(HDMI_A_VIDPOLCFG); - val &= HDMI_A_VIDPOLCFG_DATAENPOL_MASK; - val |= de; - hdmi_writeb(val, HDMI_A_VIDPOLCFG); - - val = hdmi_readb(HDMI_A_HDCPCFG1); - val &= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK; - val |= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE; - hdmi_writeb(val, HDMI_A_HDCPCFG1); + if (hdmi->hdmi_data.hdcp_enable) { + /* Enable HDMI DDC pin */ + mxc_hdmi_enable_pins(hdmi); + } else { + /* Disable HDMI DDC pin */ + mxc_hdmi_disable_pins(hdmi); + } } static void hdmi_config_AVI(struct mxc_hdmi *hdmi) @@ -1422,19 +1498,131 @@ static void hdmi_av_composer(struct mxc_hdmi *hdmi) dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); } +static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid, + struct mxc_edid_cfg *cfg, struct fb_info *fbi) +{ + int extblknum; + int i, j, ret; + unsigned char *ediddata = edid; + unsigned char tmpedid[EDID_LENGTH]; + + dev_info(&hdmi->pdev->dev, "%s\n", __func__); + + if (!edid || !cfg || !fbi) + return -EINVAL; + + /* init HDMI I2CM for read edid*/ + hdmi_writeb(0x0, HDMI_I2CM_DIV); + hdmi_writeb(0x00, HDMI_I2CM_SS_SCL_HCNT_1_ADDR); + hdmi_writeb(0x79, HDMI_I2CM_SS_SCL_HCNT_0_ADDR); + hdmi_writeb(0x00, HDMI_I2CM_SS_SCL_LCNT_1_ADDR); + hdmi_writeb(0x91, HDMI_I2CM_SS_SCL_LCNT_0_ADDR); + + hdmi_writeb(0x00, HDMI_I2CM_FS_SCL_HCNT_1_ADDR); + hdmi_writeb(0x0F, HDMI_I2CM_FS_SCL_HCNT_0_ADDR); + hdmi_writeb(0x00, HDMI_I2CM_FS_SCL_LCNT_1_ADDR); + hdmi_writeb(0x21, HDMI_I2CM_FS_SCL_LCNT_0_ADDR); + + hdmi_writeb(0x50, HDMI_I2CM_SLAVE); + hdmi_writeb(0x30, HDMI_I2CM_SEGADDR); + + /* Umask edid interrupt */ + hdmi_writeb(HDMI_I2CM_INT_DONE_POL, + HDMI_I2CM_INT); + + hdmi_writeb(HDMI_I2CM_CTLINT_NAC_POL | + HDMI_I2CM_CTLINT_ARBITRATION_POL, + HDMI_I2CM_CTLINT); + + /* reset edid data zero */ + memset(edid, 0, EDID_LENGTH*4); + memset(cfg, 0, sizeof(struct mxc_edid_cfg)); + + /* Check first three byte of EDID head */ + if (!(hdmi_edid_i2c_read(hdmi, 0, 0) == 0x00) || + !(hdmi_edid_i2c_read(hdmi, 1, 0) == 0xFF) || + !(hdmi_edid_i2c_read(hdmi, 2, 0) == 0xFF)) { + dev_info(&hdmi->pdev->dev, "EDID head check failed!"); + return -ENOENT; + } + + for (i = 0; i < 128; i++) { + *ediddata = hdmi_edid_i2c_read(hdmi, i, 0); + ediddata++; + } + + extblknum = edid[0x7E]; + if (extblknum < 0) + return extblknum; + + if (extblknum) { + ediddata = edid + EDID_LENGTH; + for (i = 0; i < 128; i++) { + *ediddata = hdmi_edid_i2c_read(hdmi, i, 1); + ediddata++; + } + } + + /* edid first block parsing */ + memset(&fbi->monspecs, 0, sizeof(fbi->monspecs)); + fb_edid_to_monspecs(edid, &fbi->monspecs); + + ret = mxc_edid_parse_ext_blk(edid + EDID_LENGTH, + cfg, &fbi->monspecs); + if (ret < 0) + return -ENOENT; + + /* need read segment block? */ + if (extblknum > 1) { + for (j = 1; j <= extblknum; j++) { + for (i = 0; i < 128; i++) + *(tmpedid + 1) = hdmi_edid_i2c_read(hdmi, i, j); + + /* edid ext block parsing */ + ret = mxc_edid_parse_ext_blk(tmpedid + EDID_LENGTH, + cfg, &fbi->monspecs); + if (ret < 0) + return -ENOENT; + } + } + + return 0; +} + static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) { int ret; u8 edid_old[HDMI_EDID_LEN]; + u8 clkdis; dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); /* save old edid */ memcpy(edid_old, hdmi->edid, HDMI_EDID_LEN); - ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, hdmi->edid, + if (!hdmi->hdmi_data.hdcp_enable) + ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, + hdmi->edid, &hdmi->edid_cfg, hdmi->fbi); + else { + + /* Disable HDCP clk */ + if (hdmi->hdmi_data.hdcp_enable) { + clkdis = hdmi_readb(HDMI_MC_CLKDIS); + clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); + } + + ret = mxc_edid_read_internal(hdmi, hdmi->edid, &hdmi->edid_cfg, hdmi->fbi); + /* Enable HDCP clk */ + if (hdmi->hdmi_data.hdcp_enable) { + clkdis = hdmi_readb(HDMI_MC_CLKDIS); + clkdis &= ~HDMI_MC_CLKDIS_HDCPCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); + } + + } if (ret < 0) return HDMI_EDID_FAIL; @@ -1454,6 +1642,28 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) return HDMI_EDID_SUCCESS; } +static void mxc_hdmi_enable_pins(struct mxc_hdmi *hdmi) +{ + struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* Enable pins to HDMI */ + if (plat->enable_pins) + plat->enable_pins(); +} + +static void mxc_hdmi_disable_pins(struct mxc_hdmi *hdmi) +{ + struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* Disable pins to HDMI */ + if (plat->disable_pins) + plat->disable_pins(); +} + static void mxc_hdmi_phy_disable(struct mxc_hdmi *hdmi) { dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); @@ -1696,7 +1906,7 @@ static void mxc_hdmi_set_mode(struct mxc_hdmi *hdmi) return; } - /* If video mode same as previous, init HDMI PHY and return */ + /* If video mode same as previous, init HDMI again */ if (fb_mode_is_equal(&hdmi->previous_non_vga_mode, mode)) { dev_dbg(&hdmi->pdev->dev, "%s: Video mode same as previous\n", __func__); @@ -1707,7 +1917,6 @@ static void mxc_hdmi_set_mode(struct mxc_hdmi *hdmi) } else { dev_dbg(&hdmi->pdev->dev, "%s: New video mode\n", __func__); mxc_hdmi_set_mode_to_vga_dvi(hdmi); - fb_videomode_to_var(&hdmi->fbi->var, mode); dump_fb_videomode((struct fb_videomode *)mode); mxc_hdmi_notify_fb(hdmi); @@ -1779,10 +1988,13 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) { dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - hdmi_disable_overflow_interrupts(); + /* Disable All HDMI clock */ + hdmi_writeb(0xff, HDMI_MC_CLKDIS); mxc_hdmi_phy_disable(hdmi); + hdmi_disable_overflow_interrupts(); + hdmi->cable_plugin = false; } @@ -1794,7 +2006,7 @@ static void hotplug_worker(struct work_struct *work) u32 phy_int_stat, phy_int_pol, phy_int_mask; u8 val; unsigned long flags; - char event_string[16]; + char event_string[32]; char *envp[] = { event_string, NULL }; phy_int_stat = hdmi->latest_intr_stat; @@ -1869,6 +2081,21 @@ static void hotplug_worker(struct work_struct *work) spin_unlock_irqrestore(&hdmi->irq_lock, flags); } +static void hdcp_hdp_worker(struct work_struct *work) +{ + struct delayed_work *delay_work = to_delayed_work(work); + struct mxc_hdmi *hdmi = + container_of(delay_work, struct mxc_hdmi, hdcp_hdp_work); + char event_string[32]; + char *envp[] = { event_string, NULL }; + + /* HDCP interrupt */ + sprintf(event_string, "EVENT=hdcpint"); + kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); + + /* Unmute interrupts in HDCP application*/ +} + static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) { struct mxc_hdmi *hdmi = data; @@ -1917,6 +2144,17 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20)); } + /* Check HDCP interrupt state */ + if (hdmi->hdmi_data.hdcp_enable) { + val = hdmi_readb(HDMI_A_APIINTSTAT); + if (val != 0) { + /* Mute interrupts until interrupt handled */ + val = 0xFF; + hdmi_writeb(val, HDMI_A_APIINTMSK); + schedule_delayed_work(&(hdmi->hdcp_hdp_work), msecs_to_jiffies(50)); + } + } + spin_unlock_irqrestore(&hdmi->irq_lock, flags); return IRQ_HANDLED; } @@ -1931,15 +2169,6 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) fb_var_to_videomode(&m, &hdmi->fbi->var); dump_fb_videomode(&m); - /* Exit the setup if we get mode change and are already set to - * this video mode */ - if ((event == FB_EVENT_MODE_CHANGE) && - fb_mode_is_equal(&hdmi->previous_mode, &m)) { - dev_dbg(&hdmi->pdev->dev, - "%s video mode did not change.\n", __func__); - mxc_hdmi_phy_init(hdmi); - return; - } dev_dbg(&hdmi->pdev->dev, "%s - video mode changed\n", __func__); /* Save mode as 'previous_mode' so that we can know if mode changed. */ @@ -1961,17 +2190,12 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) hdmi_disable_overflow_interrupts(); - if (hdmi->vic == 0) { - dev_dbg(&hdmi->pdev->dev, "Non-CEA mode used in HDMI\n"); + dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic); + if (hdmi->edid_cfg.hdmi_cap) + hdmi->hdmi_data.video_mode.mDVI = false; + else { + dev_dbg(&hdmi->pdev->dev, "CEA mode vic=%d work in DVI\n", hdmi->vic); hdmi->hdmi_data.video_mode.mDVI = true; - } else { - dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic); - if (hdmi->edid_cfg.hdmi_cap) - hdmi->hdmi_data.video_mode.mDVI = false; - else { - dev_dbg(&hdmi->pdev->dev, "CEA mode vic=%d work in DVI\n", hdmi->vic); - hdmi->hdmi_data.video_mode.mDVI = true; - } } if ((hdmi->vic == 6) || (hdmi->vic == 7) || @@ -2013,7 +2237,6 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) /* IPU not support depth color output */ hdmi->hdmi_data.enc_color_depth = 8; hdmi->hdmi_data.pix_repet_factor = 0; - hdmi->hdmi_data.hdcp_enable = 0; hdmi->hdmi_data.video_mode.mDataEnablePolarity = true; /* HDMI Initialization Step B.1 */ @@ -2308,6 +2531,7 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, hdmi->edid_cfg.hdmi_cap = true; INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_worker); + INIT_DELAYED_WORK(&hdmi->hdcp_hdp_work, hdcp_hdp_worker); /* Configure registers related to HDMI interrupt * generation before registering IRQ. */ @@ -2346,11 +2570,17 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, if (ret < 0) dev_warn(&hdmi->pdev->dev, "cound not create sys node for edid\n"); + ret = device_create_file(&hdmi->pdev->dev, &dev_attr_rgb_out_enable); if (ret < 0) dev_warn(&hdmi->pdev->dev, "cound not create sys node for rgb out enable\n"); + ret = device_create_file(&hdmi->pdev->dev, &dev_attr_hdcp_enable); + if (ret < 0) + dev_warn(&hdmi->pdev->dev, + "cound not create sys node for hdcp enable\n"); + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); hdmi_inited = true; @@ -2408,9 +2638,50 @@ static struct mxc_dispdrv_driver mxc_hdmi_drv = { .disable = mxc_hdmi_power_off, }; + +static int mxc_hdmi_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static long mxc_hdmi_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int __user *argp = (void __user *)arg; + int ret = 0; + + switch (cmd) { + case HDMI_IOC_GET_RESOURCE: + ret = copy_to_user(argp, &g_hdmi->hdmi_data, + sizeof(g_hdmi->hdmi_data)) ? -EFAULT : 0; + break; + case HDMI_IOC_GET_CPU_TYPE: + *argp = mxc_cpu_type; + break; + default: + pr_debug("Unsupport cmd %d\n", cmd); + break; + } + return ret; +} + +static int mxc_hdmi_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations mxc_hdmi_fops = { + .owner = THIS_MODULE, + .open = mxc_hdmi_open, + .release = mxc_hdmi_release, + .unlocked_ioctl = mxc_hdmi_ioctl, +}; + + static int __devinit mxc_hdmi_probe(struct platform_device *pdev) { struct mxc_hdmi *hdmi; + struct device *temp_class; int ret = 0; /* Check that I2C driver is loaded and available */ @@ -2423,6 +2694,28 @@ static int __devinit mxc_hdmi_probe(struct platform_device *pdev) ret = -ENOMEM; goto ealloc; } + g_hdmi = hdmi; + + hdmi_major = register_chrdev(hdmi_major, "mxc_hdmi", &mxc_hdmi_fops); + if (hdmi_major < 0) { + printk(KERN_ERR "HDMI: unable to get a major for HDMI\n"); + ret = -EBUSY; + goto ealloc; + } + + hdmi_class = class_create(THIS_MODULE, "mxc_hdmi"); + if (IS_ERR(hdmi_class)) { + ret = PTR_ERR(hdmi_class); + goto err_out_chrdev; + } + + temp_class = device_create(hdmi_class, NULL, MKDEV(hdmi_major, 0), + NULL, "mxc_hdmi"); + if (IS_ERR(temp_class)) { + ret = PTR_ERR(temp_class); + goto err_out_class; + } + hdmi->pdev = pdev; @@ -2460,6 +2753,11 @@ edispdrv: platform_device_put(hdmi->core_pdev); ecore: kfree(hdmi); +err_out_class: + device_destroy(hdmi_class, MKDEV(hdmi_major, 0)); + class_destroy(hdmi_class); +err_out_chrdev: + unregister_chrdev(hdmi_major, "mxc_hdmi"); ealloc: return ret; } @@ -2479,6 +2777,7 @@ static int mxc_hdmi_remove(struct platform_device *pdev) /* No new work will be scheduled, wait for running ISR */ free_irq(irq, hdmi); kfree(hdmi); + g_hdmi = NULL; return 0; } @@ -2500,6 +2799,13 @@ module_init(mxc_hdmi_init); static void __exit mxc_hdmi_exit(void) { + if (hdmi_major > 0) { + device_destroy(hdmi_class, MKDEV(hdmi_major, 0)); + class_destroy(hdmi_class); + unregister_chrdev(hdmi_major, "mxc_hdmi"); + hdmi_major = 0; + } + platform_driver_unregister(&mxc_hdmi_driver); } module_exit(mxc_hdmi_exit); |