diff options
author | Santosh Shilimkar <santosh.shilimkar@ti.com> | 2009-11-07 05:37:56 +0530 |
---|---|---|
committer | Santosh Shilimkar <santosh.shilimkar@ti.com> | 2009-11-07 05:37:56 +0530 |
commit | c1aa68e48f1b92da242edc657138ff91c1202536 (patch) | |
tree | 1bf4550615e8190bd9171a6b1494567c43037c95 /drivers | |
parent | fb580946c46c96e3a9d5e686c7a75a68a6e0c9bd (diff) | |
parent | 02b4b1c24a021c6e1fef40ce9c3ce9941ac45a61 (diff) |
Merge branch 'display_dev_omap4_sdc_1stweek' of git://dev.omapzoom.org/pub/scm/axelcx/kernel-display into L24.1-rc1_audio
Conflicts:
arch/arm/configs/omap_4430sdp_defconfig
arch/arm/configs/omap_4430simulator_defconfig
Diffstat (limited to 'drivers')
79 files changed, 34527 insertions, 38 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 48bbdbe43e69..820bdc190e9d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -113,4 +113,6 @@ source "drivers/xen/Kconfig" source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" + +source "drivers/media/video/tiler/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index bc4205d2fc3c..8a973d6e65a8 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -110,3 +110,4 @@ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/ obj-y += platform/ obj-y += ieee802154/ +obj-$(CONFIG_DMM_TILER) += media/ diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index ba69beeb0e21..9318e73fde87 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -5,6 +5,7 @@ menuconfig MEDIA_SUPPORT tristate "Multimedia support" depends on HAS_IOMEM + default y help If you want to use Video for Linux, DVB for Linux, or DAB adapters, enable this option and other options below. @@ -19,6 +20,7 @@ comment "Multimedia core support" config VIDEO_DEV tristate "Video For Linux" + default y ---help--- V4L core support for video capture and overlay devices, webcams and AM/FM radio cards. diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index dcf9fa9264bb..3ce58780d6df 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -718,6 +718,18 @@ config VIDEO_CAFE_CCIC CMOS camera controller. This is the controller found on first- generation OLPC systems. +config VIDEO_OMAP3 + bool "OMAP2/OMAP3/OMAP4 Camera and V4L2-DSS drivers" + select VIDEOBUF_GEN + select VIDEOBUF_DMA_SG + select OMAP2_DSS + depends on VIDEO_DEV && (ARCH_OMAP24XX || ARCH_OMAP34XX || ARCH_OMAP4) + default y + ---help--- + V4L2 DSS and Camera driver support for OMAP2/3/4 based boards. + +source "drivers/media/video/omap/Kconfig" + config SOC_CAMERA tristate "SoC camera support" depends on VIDEO_V4L2 && HAS_DMA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9f2e3214a482..ceff5fcc2e1b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -117,6 +117,8 @@ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o +obj-$(CONFIG_VIDEO_OMAP3) += omap/ + obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o @@ -160,6 +162,8 @@ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +obj-$(CONFIG_DMM_TILER) += tiler/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig new file mode 100644 index 000000000000..d7293a46839e --- /dev/null +++ b/drivers/media/video/omap/Kconfig @@ -0,0 +1,27 @@ +config VIDEO_OMAP_VIDEOLIB + tristate "OMAP Video out library" + depends on VIDEO_OMAP3 + default VIDEO_OMAP3 + +config VIDEO_OMAP_VIDEOOUT + tristate "OMAP Video out driver" + select VIDEOBUF_DMA_SG + select VIDEOBUF_GEN + depends on VIDEO_OMAP3 + default VIDEO_OMAP3 + +choice + prompt "TV Mode" + default NTSC_M + +config NTSC_M + bool "Use NTSC_M mode" + help + Select this option if you want NTSC_M mode on TV + +config PAL_BDGHI + bool "Use PAL_BDGHI mode" + help + Select this option if you want PAL_BDGHI mode on TV + +endchoice diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile new file mode 100644 index 000000000000..75e01d346c4d --- /dev/null +++ b/drivers/media/video/omap/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_OMAP_VIDEOLIB) += omap_voutlib.o +obj-$(CONFIG_VIDEO_OMAP_VIDEOOUT) += omap_vout.o + diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c new file mode 100644 index 000000000000..4597b7a66c4b --- /dev/null +++ b/drivers/media/video/omap/omap_vout.c @@ -0,0 +1,3078 @@ +/* + * drivers/media/video/omap/omap_vout.c + * + * Copyright (C) 2005-2009 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Leveraged code from the OMAP2 camera driver + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP24xx camera controller. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2009 Texas Instruments. + * + * History: + * 20-APR-2006 Khasim Modified VRFB based Rotation, + * The image data is always read from 0 degree + * view and written + * to the virtual space of desired rotation angle + * 4-DEC-2006 Jian Changed to support better memory management + * + * 17-Nov-2008 Hardik Changed to used the new DSS paches by Tomi + * Changed driver to use video_ioctl2 + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <linux/kdev_t.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/videodev2.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/irq.h> + +#include <media/videobuf-dma-sg.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> + +#include <asm/processor.h> +#include <mach/dma.h> +#include <mach/vram.h> + +#ifdef CONFIG_ARCH_OMAP4 /* TODO: correct this!*/ +#include "../tiler/dmm_def.h" +#endif +#include <mach/vrfb.h> + +#define TILER_ALLOCATE_V4L2 +/* #define TILER_TEST */ + +#ifdef TILER_TEST +#include <linux/io.h> +#endif + +#include <mach/display.h> +#include "omap_voutlib.h" +#include "omap_voutdef.h" +MODULE_AUTHOR("Texas Instruments."); +MODULE_DESCRIPTION("OMAP Video for Linux Video out driver"); +MODULE_LICENSE("GPL"); + +#define OMAP_VIDEO1 0 +#define OMAP_VIDEO2 1 +#ifdef CONFIG_ARCH_OMAP4 +#define OMAP_VIDEO3 2 +#endif + + +/* configuration macros */ +#define VOUT_NAME "omap_vout" + +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 + +#ifdef CONFIG_ARCH_OMAP4 +#define NUM_OF_VIDEO_CHANNELS 3 +#else +#define NUM_OF_VIDEO_CHANNELS 2 +#endif + +#define VID_MAX_WIDTH 1280 /* Largest width */ +#define VID_MAX_HEIGHT 720/* Largest height */ + +/* Mimimum requirement is 2x2 for DSS */ +#define VID_MIN_WIDTH 2 +#define VID_MIN_HEIGHT 2 + +/* 2048 x 2048 is max res supported by OMAP display controller */ +#define DMA_CHAN_ALLOTED 1 +#define DMA_CHAN_NOT_ALLOTED 0 +#define MAX_PIXELS_PER_LINE 2048 +#define VRFB_TX_TIMEOUT 1000 + +/* IRQ Bits mask of DSS */ +#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4) + +static struct videobuf_queue_ops video_vbq_ops; + +static u32 video1_numbuffers = 3; +static u32 video2_numbuffers = 3; +static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE; +static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE; +#ifdef CONFIG_ARCH_OMAP4 +static u32 video3_numbuffers = 3; +static u32 video3_bufsize = OMAP_VOUT_MAX_BUF_SIZE; +#endif +static u32 vid1_static_vrfb_alloc; +static u32 vid2_static_vrfb_alloc; +static int debug; + +/* Module parameters */ +module_param(video1_numbuffers, uint, S_IRUGO); +MODULE_PARM_DESC(video1_numbuffers, "Number of buffers to be allocated at \ + init time for Video1 device."); + +module_param(video2_numbuffers, uint, S_IRUGO); +MODULE_PARM_DESC(video2_numbuffers, "Number of buffers to be allocated at \ + init time for Video2 device."); + +module_param(video1_bufsize, uint, S_IRUGO); +MODULE_PARM_DESC(video1_bufsize, "Size of the buffer to be allocated for \ + video1 device"); + +module_param(video2_bufsize, uint, S_IRUGO); +MODULE_PARM_DESC(video2_bufsize, "Size of the buffer to be allocated for \ + video2 device"); + +#ifdef CONFIG_ARCH_OMAP4 +module_param(video3_numbuffers, uint, S_IRUGO); +MODULE_PARM_DESC(video3_numbuffers, "Number of buffers to be allocated at \ + init time for Video3 device."); + +module_param(video3_bufsize, uint, S_IRUGO); +MODULE_PARM_DESC(video1_bufsize, "Size of the buffer to be allocated for \ + video3 device"); +#endif + +module_param(vid1_static_vrfb_alloc, bool, S_IRUGO); +MODULE_PARM_DESC(vid1_static_vrfb_alloc, "Static allocation of the VRFB \ + buffer for video1 device"); + +module_param(vid2_static_vrfb_alloc, bool, S_IRUGO); +MODULE_PARM_DESC(vid2_static_vrfb_alloc, "Static allocation of the VRFB \ + buffer for video2 device"); + +module_param(debug, bool, S_IRUGO); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* Local Helper functions */ +static int omap_vout_create_video_devices(struct platform_device *pdev); +static int omapvid_apply_changes(struct omap_vout_device *vout); +static int omapvid_init(struct omap_vout_device *vout, u32 addr, u32 uv_addr); +static int omapvid_setup_overlay(struct omap_vout_device *vout, + struct omap_overlay *ovl, int posx, int posy, + int outw, int outh, u32 addr, u32 uv_addr); +static enum omap_color_mode video_mode_to_dss_mode(struct omap_vout_device + *vout); +static void omap_vout_isr(void *arg, unsigned int irqstatus); +static void omap_vout_cleanup_device(struct omap_vout_device *vout); + +/* + * Maximum amount of memory to use for rendering buffers. + * Default is enough to four (RGB24) DVI 720P buffers. + */ +#define MAX_ALLOWED_VIDBUFFERS 4 + +/* list of image formats supported by OMAP2 video pipelines */ +const static struct v4l2_fmtdesc omap_formats[] = { + { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * We interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + /* Note: V4L2 defines RGB32 as: RGB-8-8-8-8 we use + * this for RGB24 unpack mode, the last 8 bits are ignored + * */ + .description = "RGB32, le", + .pixelformat = V4L2_PIX_FMT_RGB32, + }, + { + /* Note: V4L2 defines RGB24 as: RGB-8-8-8 we use + * this for RGB24 packed mode + * + */ + .description = "RGB24, le", + .pixelformat = V4L2_PIX_FMT_RGB24, + }, + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "UYVY, packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + .description = "NV12 - YUV420 format", + .pixelformat = V4L2_PIX_FMT_NV12, + }, + +}; + +#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats)) + +/* Allocate buffers */ +static unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr) +{ + unsigned long virt_addr, addr; + u32 order, size; + + size = PAGE_ALIGN(buf_size); + order = get_order(size); + virt_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, order); + addr = virt_addr; + if (virt_addr) { + while (size > 0) { + SetPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + *phys_addr = (u32) virt_to_phys((void *) virt_addr); + return virt_addr; +} + +/* Free buffers */ +static void omap_vout_free_buffer(unsigned long virtaddr, u32 phys_addr, + u32 buf_size) +{ + unsigned long addr = virtaddr; + u32 order, size; + + size = PAGE_ALIGN(buf_size); + order = get_order(size); + while (size > 0) { + ClearPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + free_pages((unsigned long) virtaddr, order); +} + +/* Function for allocating video buffers */ +static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, + int count, int startindex) +{ + int i, j; + + for (i = 0; i < count; i++) { + if (!vout->smsshado_virt_addr[i]) { + vout->smsshado_virt_addr[i] = + omap_vout_alloc_buffer(vout->smsshado_size, + &vout->smsshado_phy_addr[i]); + } + if (!vout->smsshado_virt_addr[i] && startindex != -1) { + if (V4L2_MEMORY_MMAP == vout->memory + && i >= startindex) + break; + } + if (!vout->smsshado_virt_addr[i]) { + for (j = 0; j < i; j++) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[j], + vout->smsshado_phy_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } + count = 0; + return -ENOMEM; + } + memset((void *) vout->smsshado_virt_addr[i], 0, + vout->smsshado_size); + } + return 0; +} + +/* Try format */ +static int omap_vout_try_format(struct v4l2_pix_format *pix) +{ + int ifmt, bpp = 0; + + pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT, + (u32)VID_MAX_HEIGHT); + pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH); + + for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) { + if (pix->pixelformat == omap_formats[ifmt].pixelformat) + break; + } + + if (ifmt == NUM_OUTPUT_FORMATS) + ifmt = 0; + + pix->pixelformat = omap_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_ANY; + pix->priv = 0; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + bpp = YUYV_BPP; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB565_BPP; + break; + case V4L2_PIX_FMT_RGB24: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB24_BPP; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB32_BPP; + break; + case V4L2_PIX_FMT_NV12: + pix->colorspace = V4L2_COLORSPACE_JPEG; + bpp = 3/2; /* TODO: check this? */ + break; + } + if (V4L2_PIX_FMT_NV12 == pix->pixelformat) + pix->bytesperline = pix->width * 3/2; + else + pix->bytesperline = pix->width * bpp; + + pix->sizeimage = pix->bytesperline * pix->height; + + return bpp; +} + +/* + * omap_vout_uservirt_to_phys: This inline function is used to convert user + * space virtual address to physical address. + */ +static inline u32 omap_vout_uservirt_to_phys(u32 virtp) +{ + unsigned long physp = 0; + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + + vma = find_vma(mm, virtp); + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) { + physp = virt_to_phys((void *) virtp); + } else if ((vma) && (vma->vm_flags & VM_IO) + && (vma->vm_pgoff)) { + /* this will catch, kernel-allocated, + mmaped-to-usermode addresses */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + } else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, virtp, nr_pages, + 1, 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) { + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + } else { + printk(KERN_WARNING VOUT_NAME + "omap_vout_uservirt_to_phys:\ + get_user_pages failed\n"); + return 0; + } + } + + return physp; +} + +/* This functions wakes up the application once + * the DMA transfer to VRFB space is completed. */ +static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data) +{ + struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data; + + t->tx_status = 1; + wake_up_interruptible(&t->wait); +} + +/* Release the VRFB context once the module exits */ +static void omap_vout_release_vrfb(struct omap_vout_device *vout) +{ +#ifndef CONFIG_ARCH_OMAP4 +/* TODO: this is temporary disabling of vrfb to test V4L2: needs to be + corrected for future +*/ + int i; + for (i = 0; i < 4; i++) + omap_vrfb_release_ctx(&vout->vrfb_context[i]); +#endif + if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) { + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + omap_free_dma(vout->vrfb_dma_tx.dma_ch); + } + +} + +/* Return true if rotation is 90 or 270 */ +static inline int rotate_90_or_270(struct omap_vout_device *vout) +{ + return (vout->rotation == dss_rotation_90_degree || + vout->rotation == dss_rotation_270_degree); +} + +/* Return true if rotation is enabled */ +static inline int rotation_enabled(struct omap_vout_device *vout) +{ + return vout->rotation || vout->mirror; +} + +/* Reverse the rotation degree if mirroring is enabled */ +static inline int calc_rotation(struct omap_vout_device *vout) +{ +#ifndef CONFIG_ARCH_OMAP4 + if (!vout->mirror) + return vout->rotation; + switch (vout->rotation) { + case dss_rotation_90_degree: + return dss_rotation_270_degree; + case dss_rotation_270_degree: + return dss_rotation_90_degree; + case dss_rotation_180_degree: + return dss_rotation_0_degree; + default: + return dss_rotation_180_degree; + } +#else + return vout->rotation; +#endif +} + +/* Free the V4L2 buffers */ +static void omap_vout_free_buffers(struct omap_vout_device *vout) +{ + int i, numbuffers; + + /* Allocate memory for the buffes */ +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_VIDEO3 == vout->vid) { + numbuffers = video3_numbuffers; + vout->buffer_size = video3_bufsize; + } else +#endif + { + numbuffers = (vout->vid) ? video2_numbuffers + : video1_numbuffers; + vout->buffer_size = (vout->vid) ? video2_bufsize + : video1_bufsize; + } + for (i = 0; i < numbuffers; i++) { + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buf_phy_addr[i], vout->buffer_size); + vout->buf_phy_addr[i] = 0; + vout->buf_virt_addr[i] = 0; + } +} + +/* Free VRFB buffers */ +static void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) +{ + int j; + + for (j = 0; j < 4; j++) { + omap_vout_free_buffer(vout->smsshado_virt_addr[j], + vout->smsshado_phy_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } +} + +/* Allocate the buffers for the VRFB space. Data is copied from V4L2 + * buffers to the VRFB buffers using the DMA engine.*/ +static int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, + unsigned int *count, unsigned int startindex) +{ + int i; + + /* Allocate the VRFB buffers only if the buffers are not + * allocated during init time. + */ + if ((rotation_enabled(vout)) && + !vout->vrfb_static_allocation) + if (omap_vout_allocate_vrfb_buffers(vout, *count, startindex)) + return -ENOMEM; + +#ifndef CONFIG_ARCH_OMAP4 +/* TODO: this is temporary disabling of vrfb to test V4L2: needs to be + corrected for future +*/ + for (i = 0; i < *count; i++) { + omap_vrfb_setup(&vout->vrfb_context[i], + vout->smsshado_phy_addr[i], + vout->pix.width, vout->pix.height, + vout->dss_mode); + } +#endif + return 0; +} + +/* Allocate the buffers for TILER space. Ideally, the buffers will be ONLY + in tiler space, with different rotated views available by just a convert. + */ +static int omap_vout_tiler_buffer_setup(struct omap_vout_device *vout, + unsigned int *count, unsigned int startindex) +{ + int i, j; + enum tiler_fmt fmt; + + printk(KERN_INFO VOUT_NAME "tiler buffer setup:\n"); + printk(KERN_INFO VOUT_NAME "count - %d, start -%d :\n", + *count, startindex); + + if (OMAP_DSS_COLOR_NV12 == vout->dss_mode) + fmt = TILFMT_8BIT; /* Y-buffer*/ + else { + switch (vout->bpp) { + case 1: + fmt = TILFMT_8BIT; + break; + case 2: + fmt = TILFMT_16BIT; + break; + case 3: + case 4: + fmt = TILFMT_32BIT; + break; + default: + return -ENOMEM; + } + } + + for (i = 0; i < *count; i++) { + if (!vout->buf_phy_addr[i]) { + + if (DMM_NO_ERROR != tiler_alloc_buf(fmt, + vout->pix.width, vout->pix.height, + &vout->buf_phy_addr[i])) { + vout->buf_phy_addr[i] = 0; + } else + printk(KERN_INFO VOUT_NAME + " tiler buffer[%d] = 0x%lx\n", i, + vout->buf_phy_addr[i]); + + } + + if (!vout->buf_phy_addr[i] && startindex != -1) { + if (V4L2_MEMORY_MMAP == vout->memory + && i >= startindex) + break; /* TODO: check this logic */ + } + if (OMAP_DSS_COLOR_NV12 == vout->dss_mode) { + if (!vout->buf_phy_uv_addr[i]) { /* UV-buffer */ + if (DMM_NO_ERROR != + tiler_alloc_buf(TILFMT_16BIT, + vout->pix.width, (vout->pix.height / 2), + &vout->buf_phy_uv_addr[i])) { + vout->buf_phy_uv_addr[i] = 0; + } else + printk(KERN_INFO VOUT_NAME + " tiler uv buffer[%d] = 0x%lx\n", i, + vout->buf_phy_uv_addr[i]); + } + + /* if either Y- UV- buffer is not allocated, + free all allocated buffers */ + if (!vout->buf_phy_addr[i] || + !vout->buf_phy_uv_addr[i]) { + for (j = 0; j < i; j++) { + tiler_free_buf(vout->buf_phy_addr[j]); + tiler_free_buf( + vout->buf_phy_uv_addr[j]); + vout->buf_phy_addr[j] = 0; + vout->buf_phy_uv_addr[j] = 0; + } + *count = 0; + return -ENOMEM; + } + } + + if (!vout->buf_phy_addr[i]) { + for (j = 0; j < i; j++) { + tiler_free_buf(vout->buf_phy_addr[j]); + vout->buf_phy_addr[j] = 0; + } + *count = 0; + return -ENOMEM; + } + } + return 0; +} + +/* Free tiler buffers */ +static void omap_vout_free_tiler_buffers(struct omap_vout_device *vout) +{ + int j; + + for (j = 0; j < vout->buffer_allocated; j++) { + tiler_free_buf(vout->buf_phy_addr[j]); + tiler_free_buf(vout->buf_phy_uv_addr[j]); + vout->buf_phy_addr[j] = 0; + } + vout->buffer_allocated = 0; +} + + +/* Convert V4L2 rotation to DSS rotation + * V4L2 understand 0, 90, 180, 270. + * convert to 0, 1, 2 and 3 repsectively for DSS */ +static int v4l2_rot_to_dss_rot(int v4l2_rotation, enum dss_rotation *rotation, + bool mirror) +{ + switch (v4l2_rotation) { + case 90: + *rotation = dss_rotation_90_degree; + return 0; + case 180: + *rotation = dss_rotation_180_degree; + return 0; + case 270: + *rotation = dss_rotation_270_degree; + return 0; + case 0: + *rotation = dss_rotation_0_degree; + return 0; + default: + return -EINVAL; + } + +} + +/* Calculate the buffer offsets from which the streaming should + * start. This offset calculation is mainly required because of + * the VRFB 32 pixels alignment with rotation. + */ +static int omap_vout_calculate_offset(struct omap_vout_device *vout) +{ + struct v4l2_pix_format *pix = &(vout->pix); + struct v4l2_rect *crop = &(vout->crop); + enum dss_rotation rotation; + bool mirroring = vout->mirror; + int vr_ps = 1, ps = 2, temp_ps = 2; + int offset = 0, ctop = 0, cleft = 0, line_length = 0; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_dss_device *cur_display; + int *cropped_offset = &(vout->cropped_offset); + + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) + return -1; + cur_display = ovl->manager->device; + + rotation = calc_rotation(vout); + + if (V4L2_PIX_FMT_YUYV == pix->pixelformat || + V4L2_PIX_FMT_UYVY == pix->pixelformat) { + if (rotation_enabled(vout)) { + /* + * ps - Actual pixel size for YUYV/UYVY for + * VRFB/Mirroring is 4 bytes + * vr_ps - Virtually pixel size for YUYV/UYVY is + * 2 bytes + */ + ps = 4; + vr_ps = 2; + } else { + ps = 2; /* otherwise the pixel size is 2 byte */ + } + } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) { + ps = 4; + } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) { + ps = 3; + } + vout->ps = ps; + vout->vr_ps = vr_ps; + if (rotation_enabled(vout)) { + line_length = MAX_PIXELS_PER_LINE; + ctop = (pix->height - crop->height) - crop->top; + cleft = (pix->width - crop->width) - crop->left; + } else { + line_length = pix->width; + } + vout->line_length = line_length; +#ifndef CONFIG_ARCH_OMAP4 + switch (rotation) { + case dss_rotation_90_degree: + offset = vout->vrfb_context[0].yoffset * + vout->vrfb_context[0].bytespp; + temp_ps = ps / vr_ps; + if (mirroring == 0) { + *cropped_offset = offset + line_length * + temp_ps * cleft + crop->top * temp_ps; + } else { + *cropped_offset = offset + line_length * temp_ps * + cleft + crop->top * temp_ps + (line_length * + ((crop->width / (vr_ps)) - 1) * ps); + } + break; + case dss_rotation_180_degree: + offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset * + vout->vrfb_context[0].bytespp) + + (vout->vrfb_context[0].xoffset * + vout->vrfb_context[0].bytespp)); + if (mirroring == 0) { + *cropped_offset = offset + (line_length * ps * ctop) + + (cleft / vr_ps) * ps; + + } else { + *cropped_offset = offset + (line_length * ps * ctop) + + (cleft / vr_ps) * ps + (line_length * + (crop->height - 1) * ps); + } + break; + case dss_rotation_270_degree: + offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset * + vout->vrfb_context[0].bytespp; + temp_ps = ps / vr_ps; + if (mirroring == 0) { + *cropped_offset = offset + line_length * + temp_ps * crop->left + ctop * ps; + } else { + *cropped_offset = offset + line_length * + temp_ps * crop->left + ctop * ps + + (line_length * ((crop->width / vr_ps) - 1) * + ps); + } + break; + case dss_rotation_0_degree: + if (mirroring == 0) { + *cropped_offset = (line_length * ps) * + crop->top + (crop->left / vr_ps) * ps; + } else { + *cropped_offset = (line_length * ps) * + crop->top + (crop->left / vr_ps) * ps + + (line_length * (crop->height - 1) * ps); + } + break; + default: + *cropped_offset = (line_length * ps * crop->top) / + vr_ps + (crop->left * ps) / vr_ps + + ((crop->width / vr_ps) - 1) * ps; + break; + } +#endif + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "%s Offset:%x\n", __func__, *cropped_offset); + return 0; +} + +/* Initialize the overlay structure */ +int omapvid_init(struct omap_vout_device *vout, u32 addr, u32 uv_addr) +{ + int r = 0; + struct omapvideo_info *ovid = &(vout->vid_info); + struct omap_overlay *ovl; + int posx, posy; + int outw, outh, temp, rotation; + int i; + struct v4l2_window *win; + struct omap_video_timings *timing; + + win = &vout->win; + rotation = vout->rotation; + for (i = 0; i < ovid->num_overlays; i++) { + ovl = ovid->overlays[i]; + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + + timing = &ovl->manager->device->panel.timings; + + outw = win->w.width; + outh = win->w.height; + switch (rotation) { + + case dss_rotation_90_degree: + /* Invert the height and widht for 90 + * and 270 degree rotation + */ + temp = outw; + outw = outh; + outh = temp; + posy = (timing->y_res - win->w.width)- + win->w.left; + posx = win->w.top; + break; + + case dss_rotation_180_degree: + posx = (timing->x_res - win->w.width) - + win->w.left; + posy = (timing->y_res - win->w.height) - + win->w.top; + break; + + case dss_rotation_270_degree: + temp = outw; + outw = outh; + outh = temp; + posy = win->w.left; + posx = (timing->x_res - win->w.height) + - win->w.top; + break; + + default: + posx = win->w.left; + posy = win->w.top; + break; + } + + r = omapvid_setup_overlay(vout, ovl, posx, posy, outw, + outh, addr, uv_addr); + if (r) + goto err; + } + return 0; +err: + printk(KERN_WARNING VOUT_NAME "apply_changes failed\n"); + return r; +} + +/* Apply the changes set the go bit of DSS */ +int omapvid_apply_changes(struct omap_vout_device *vout) +{ + struct omapvideo_info *ovid = &(vout->vid_info); + struct omap_overlay *ovl; + int i; + + for (i = 0; i < ovid->num_overlays; i++) { + ovl = ovid->overlays[i]; + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + ovl->manager->apply(ovl->manager); + } + return 0; + +} + +/* helper function: for NV12, returns uv buffer address given single buffer + * for yuv - y buffer will still be in the input. + * used only for non-TILER case +*/ +u32 omapvid_get_uvbase_nv12(u32 paddr, int height, int width) +{ + u32 puv_addr = 0; + + puv_addr = (paddr + (height * width)); + return puv_addr; +} + +/* Setup the overlay */ +int omapvid_setup_overlay(struct omap_vout_device *vout, + struct omap_overlay *ovl, int posx, int posy, int outw, + int outh, u32 addr, u32 uv_addr) +{ + int r = 0; + enum dss_rotation rotation; + bool mirror; + int cropheight, cropwidth, pixheight, pixwidth; + struct omap_overlay_info info; + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 && + (outw != vout->pix.width || outh != vout->pix.height)) { + r = -EINVAL; + goto err; + } + + vout->dss_mode = video_mode_to_dss_mode(vout); + + if (vout->dss_mode == -EINVAL) { + r = -EINVAL; + goto err; + } + + rotation = vout->rotation; + mirror = vout->mirror; + + /* Setup the input plane parameters according to + * rotation value selected. + */ + if (rotate_90_or_270(vout)) { + cropheight = vout->crop.width; + cropwidth = vout->crop.height; + pixheight = vout->pix.width; + pixwidth = vout->pix.height; + } else { + cropheight = vout->crop.height; + cropwidth = vout->crop.width; + pixheight = vout->pix.height; + pixwidth = vout->pix.width; + } + + ovl->get_overlay_info(ovl, &info); + info.paddr = addr; +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_COLOR_NV12 == vout->dss_mode) + info.p_uv_addr = uv_addr; + else + info.p_uv_addr = NULL; +#endif + info.vaddr = NULL; + info.width = cropwidth; + info.height = cropheight; + info.color_mode = vout->dss_mode; + info.mirror = mirror; + info.pos_x = posx; + info.pos_y = posy; + info.out_width = outw; + info.out_height = outh; + info.global_alpha = vout->win.global_alpha; + if (!rotation_enabled(vout)) { + info.rotation = 0; + info.rotation_type = OMAP_DSS_ROT_DMA; + info.screen_width = pixwidth; + } else { + info.rotation = vout->rotation; +#ifdef CONFIG_ARCH_OMAP4 + info.rotation_type = OMAP_DSS_ROT_TILER; + info.screen_width = pixwidth; +#else + info.rotation_type = OMAP_DSS_ROT_VRFB; + info.screen_width = 2048; +#endif + } + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "%s info.enable=%d info.addr=%x info.width=%d\n info.height=%d \ + info.color_mode=%d info.rotation=%d info.mirror=%d\n \ + info.posx=%d info.posy=%d info.out_width = %d info.out_height=%d\n \ + info.rotation_type=%d info.screen_width=%d\n", __func__, info.enabled, + info.paddr, info.width, info.height, info.color_mode, info.rotation, + info.mirror, info.pos_x, info.pos_y, info.out_width, info.out_height, + info.rotation_type, info.screen_width); + +#ifdef CONFIG_ARCH_OMAP4 + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "info.puvaddr=%x\n", + info.p_uv_addr); +#endif + r = ovl->set_overlay_info(ovl, &info); + if (r) + goto err; + + return 0; +err: + printk(KERN_WARNING VOUT_NAME "setup_overlay failed\n"); + return r; +} + +/* convert V4L2 pixel format to DSS pixel format */ +static enum omap_color_mode video_mode_to_dss_mode(struct omap_vout_device + *vout) +{ + struct v4l2_pix_format *pix = &vout->pix; + + switch (pix->pixelformat) { +#ifdef CONFIG_ARCH_OMAP4 + case V4L2_PIX_FMT_NV12: + return OMAP_DSS_COLOR_NV12; +#endif + case 0: + break; + case V4L2_PIX_FMT_YUYV: + return OMAP_DSS_COLOR_YUV2; + + case V4L2_PIX_FMT_UYVY: + return OMAP_DSS_COLOR_UYVY; + + case V4L2_PIX_FMT_RGB565: + return OMAP_DSS_COLOR_RGB16; + + case V4L2_PIX_FMT_RGB24: + return OMAP_DSS_COLOR_RGB24P; + + case V4L2_PIX_FMT_RGB32: /* TODO: OMAP4: check this ??*/ + return (vout->vid == OMAP_VIDEO1) ? + OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32; + case V4L2_PIX_FMT_BGR32: + return OMAP_DSS_COLOR_RGBX32; + + default: + return -EINVAL; + } + return -EINVAL; +} + +/* Video buffer call backs */ + +/* Buffer setup function is called by videobuf layer when REQBUF ioctl is + * called. This is used to setup buffers and return size and count of + * buffers allocated. After the call to this buffer, videobuf layer will + * setup buffer queue depending on the size and count of buffers + */ +static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct omap_vout_device *vout = q->priv_data; + int startindex = 0, i, j; + u32 phy_addr = 0, virt_addr = 0; + + if (!vout) + return -EINVAL; + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) + return -EINVAL; + +#ifndef TILER_ALLOCATE_V4L2 +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_VIDEO3 == vout->vid) + startindex = video3_numbuffers; + else +#endif + startindex = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + + if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex) + *count = startindex; + +#ifndef CONFIG_ARCH_OMAP4 + if ((rotation_enabled(vout)) + && *count > 4) + *count = 4; + + /* If rotation is enabled, allocate memory for VRFB space also */ + if (rotation_enabled(vout)) { + if (omap_vout_vrfb_buffer_setup(vout, count, startindex)) + return -ENOMEM; + } + + /* Now allocated the V4L2 buffers */ + *size = vout->buffer_size; + + startindex = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + for (i = startindex; i < *count; i++) { + vout->buffer_size = *size; + + virt_addr = omap_vout_alloc_buffer(vout->buffer_size, + &phy_addr); + if (!virt_addr) { + printk(KERN_WARNING VOUT_NAME + "Memory not available: virt address " + "problem in buffer setup\n"); + if (!rotation_enabled(vout)) + break; + /* Free the VRFB buffers if no space for V4L2 buffers */ + for (j = i; j < *count; j++) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[j], + vout->smsshado_phy_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } + } + vout->buf_virt_addr[i] = virt_addr; + vout->buf_phy_addr[i] = phy_addr; + } + *count = vout->buffer_allocated = i; +#endif /* CONFIG_ARCH_OMAP4 */ + +#else /* TILER_ALLOCATE_V4L2 */ + + /* tiler_alloc_buf to be called here + pre-requisites: rotation, format? + based on that buffers will be allocated. + */ + /* Now allocated the V4L2 buffers */ + /* i is the block-width - either 4K or 8K, depending upon input width*/ + i = (vout->pix.width + + TILER_PAGESIZE - 1) & ~(TILER_PAGESIZE - 1); + + /* for NV12 format, buffer is height + height / 2*/ + if (OMAP_DSS_COLOR_NV12 == vout->dss_mode) + *size = vout->buffer_size = (vout->pix.height * 3/2 * i); + else + *size = vout->buffer_size = (vout->pix.height * i); + + printk(KERN_INFO "\nheight=%d, size = %d, vout->buffer_sz=%d\n", + vout->pix.height, *size, vout->buffer_size); + if (omap_vout_tiler_buffer_setup(vout, count, 0)) + return -ENOMEM; +#endif /* TILER_ALLOCATE_V4L2 */ + + if (V4L2_MEMORY_MMAP != vout->memory) + return 0; + + return 0; +} + +/* Free the V4L2 buffers additionally allocated than default + * number of buffers and free all the VRFB buffers */ +static void omap_vout_free_allbuffers(struct omap_vout_device *vout) +{ + int num_buffers = 0, i; + +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_VIDEO3 == vout->vid) + num_buffers = video3_numbuffers; + else +#endif + num_buffers = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + for (i = num_buffers; i < vout->buffer_allocated; i++) { + if (vout->buf_virt_addr[i]) { + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buf_phy_addr[i], vout->buffer_size); + } + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + /* Free the VRFB buffers only if they are allocated + * during reqbufs. Don't free if init time allocated + */ + if (!vout->vrfb_static_allocation) { + for (i = 0; i < 4; i++) { + if (vout->smsshado_virt_addr[i]) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[i], + vout->smsshado_phy_addr[i], + vout->smsshado_size); + vout->smsshado_virt_addr[i] = 0; + vout->smsshado_phy_addr[i] = 0; + } + } + } + vout->buffer_allocated = num_buffers; +} + +/* This function will be called when VIDIOC_QBUF ioctl is called. + * It prepare buffers before give out for the display. This function + * user space virtual address into physical address if userptr memory + * exchange mechanism is used. If rotation is enabled, it copies entire + * buffer into VRFB memory space before giving it to the DSS. + */ +static int omap_vout_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct omap_vout_device *vout = q->priv_data; + struct videobuf_dmabuf *dmabuf = NULL; + +#ifdef TILER_TEST + int i, j; + void __iomem *pMemoryLCD; + unsigned short uReadWord; + static unsigned uWriteWord = 0x001f; +#endif + +#ifndef CONFIG_ARCH_OMAP4 + u32 dest_frame_index = 0, src_element_index = 0; + u32 dest_element_index = 0, src_frame_index = 0; + u32 elem_count = 0, frame_count = 0, pixsize = 2; + enum dss_rotation rotation; + struct vid_vrfb_dma *tx; +#endif + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vout->pix.width; + vb->height = vout->pix.height; + vb->size = vb->width * vb->height * vout->bpp; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + +#ifndef TILER_ALLOCATE_V4L2 + /* if user pointer memory mechanism is used, get the physical + * address of the buffer + */ + if (V4L2_MEMORY_USERPTR == vb->memory) { + if (0 == vb->baddr) + return -EINVAL; + /* Virtual address */ + /* priv points to struct videobuf_pci_sg_memory. But we went + * pointer to videobuf_dmabuf, which is member of + * videobuf_pci_sg_memory */ + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + dmabuf->vmalloc = (void *) vb->baddr; + + /* Physical address */ + dmabuf->bus_addr = + (dma_addr_t) omap_vout_uservirt_to_phys(vb->baddr); + } + + if (!rotation_enabled(vout)) { + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + + vout->queued_buf_addr[vb->i] = (u8 *) dmabuf->bus_addr; + return 0; + } +#ifndef CONFIG_ARCH_OMAP4 + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + /* If rotation is enabled, copy input buffer into VRFB + * memory space using DMA. We are copying input buffer + * into VRFB memory space of desired angle and DSS will + * read image VRFB memory for 0 degree angle + */ + pixsize = vout->bpp * vout->vrfb_bpp; + /* + * DMA transfer in double index mode + */ + + /* Frame index */ + dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) - + (vout->pix.width * vout->bpp)) + 1; + + /* Source and destination parameters */ + src_element_index = 0; + src_frame_index = 0; + dest_element_index = 1; + /* Number of elements per frame */ + elem_count = vout->pix.width * vout->bpp; + frame_count = vout->pix.height; + tx = &vout->vrfb_dma_tx; + tx->tx_status = 0; + omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32, + (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT, + tx->dev_id, 0x0); + /* src_port required only for OMAP1 */ + omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + dmabuf->bus_addr, src_element_index, src_frame_index); + /*set dma source burst mode for VRFB */ + omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); + rotation = calc_rotation(vout); + + /* dest_port required only for OMAP1 */ + omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX, + vout->vrfb_context[vb->i].paddr[0], dest_element_index, + dest_frame_index); + /*set dma dest burst mode for VRFB */ + omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); + omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0); + + omap_start_dma(tx->dma_ch); + interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT); + + if (tx->tx_status == 0) { + omap_stop_dma(tx->dma_ch); + return -EINVAL; + } + /* Store buffers physical address into an array. Addresses + * from this array will be used to configure DSS */ + vout->queued_buf_addr[vb->i] = (u8 *) + vout->vrfb_context[vb->i].paddr[rotation]; +#endif +#else /* TILER to be used */ + + /* Here, we need to use the physical addresses given by Tiler: + */ + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + vout->queued_buf_addr[vb->i] = (u8 *) dmabuf->bus_addr; + vout->queued_buf_uv_addr[vb->i] = (u8 *) dmabuf->vmalloc; + +#ifdef TILER_TEST + { + uWriteWord = uWriteWord + 0x20; + printk(KERN_INFO "\nbefore ioremap: 0x%x\n", dmabuf->bus_addr); + pMemoryLCD = ioremap(dmabuf->bus_addr, + (32768 * vout->pix.height)); + for (i = 0; i < vout->pix.height; i++) { + for (j = 0; j < vout->pix.width; j++) { + writew(uWriteWord, + (unsigned short *)pMemoryLCD + + i*16384 + j); + uReadWord = readw((unsigned short *)pMemoryLCD + + i*16384 + j); + } + } + iounmap(pMemoryLCD); + printk(KERN_INFO "\nafter ioremap\n"); + } +#endif + + +#endif + return 0; +} + +/* Buffer queue funtion will be called from the videobuf layer when _QBUF + * ioctl is called. It is used to enqueue buffer, which is ready to be + * displayed. */ +static void omap_vout_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct omap_vout_device *vout = q->priv_data; + + /* Driver is also maintainig a queue. So enqueue buffer in the driver + * queue */ + list_add_tail(&vb->queue, &vout->dma_queue); + + vb->state = VIDEOBUF_PREPARED; +} + +/* Buffer release function is called from videobuf layer to release buffer + * which are already allocated */ +static void omap_vout_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct omap_vout_device *vout = q->priv_data; + + vb->state = VIDEOBUF_NEEDS_INIT; + + if (V4L2_MEMORY_MMAP != vout->memory) + return; +} + +/* + * File operations + */ +static void omap_vout_vm_open(struct vm_area_struct *vma) +{ + struct omap_vout_device *vout = vma->vm_private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + vout->mmap_count++; +} + +static void omap_vout_vm_close(struct vm_area_struct *vma) +{ + struct omap_vout_device *vout = vma->vm_private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + vout->mmap_count--; +} + +static struct vm_operations_struct omap_vout_vm_ops = { + .open = omap_vout_vm_open, + .close = omap_vout_vm_close, +}; + +static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct omap_vout_device *vout = file->private_data; + struct videobuf_queue *q = &vout->vbq; + unsigned long size = (vma->vm_end - vma->vm_start); + unsigned long start = vma->vm_start; + int i; + void *pos; + struct videobuf_dmabuf *dmabuf = NULL; + + int j = 0, k = 0, m = 0, p = 0, m_increment = 0; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__, + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* look for the buffer to map */ + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + if (V4L2_MEMORY_MMAP != q->bufs[i]->memory) + continue; + if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + break; + } + + if (VIDEO_MAX_FRAME == i) { + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); + return -EINVAL; + } + q->bufs[i]->baddr = vma->vm_start; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &omap_vout_vm_ops; + vma->vm_private_data = (void *) vout; + dmabuf = videobuf_to_dma(q->bufs[i]); + + pos = dmabuf->vmalloc; + +#ifndef TILER_ALLOCATE_V4L2 + vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT; + while (size > 0) { + unsigned long pfn; + pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT; + if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } +#else + pos = dmabuf->bus_addr; + /* get line width */ + /* for NV12, Y buffer is 1bpp*/ + if (OMAP_DSS_COLOR_NV12 == vout->dss_mode) { + p = (vout->pix.width + + TILER_PAGESIZE - 1) & ~(TILER_PAGESIZE - 1); + m_increment = 64 * TILER_WIDTH; + } else { + p = (vout->pix.width * vout->bpp + + TILER_PAGESIZE - 1) & ~(TILER_PAGESIZE - 1); + + if (vout->bpp > 1) + m_increment = 2*64*TILER_WIDTH; + else + m_increment = 64 * TILER_WIDTH; + } + + for (j = 0; j < vout->pix.height; j++) { + /* map each page of the line */ + if (0) + printk(KERN_NOTICE + "Y buffer %s::%s():%d: vm_start+%d = 0x%lx," + "dma->vmalloc+%d = 0x%lx, w=0x%x\n", + __FILE__, __func__, __LINE__, + k, vma->vm_start + k, m, + (pos + m), p); + vma->vm_pgoff = + ((unsigned long)pos + m) >> PAGE_SHIFT; + if (remap_pfn_range(vma, vma->vm_start + k, + ((unsigned long)pos + m) >> PAGE_SHIFT, + p, vma->vm_page_prot)) + return -EAGAIN; + k += p; + m += m_increment; + } + m = 0; + + /* UV Buffer in case of NV12 format */ + if (OMAP_DSS_COLOR_NV12 == vout->dss_mode) { + pos = dmabuf->vmalloc; + /* UV buffer is 2 bpp */ + p = (vout->pix.width * 2 + + TILER_PAGESIZE - 1) & ~(TILER_PAGESIZE - 1); + + m_increment = 2*64*TILER_WIDTH; + + /* UV buffer is height / 2*/ + for (j = 0; j < vout->pix.height / 2; j++) { + /* map each page of the line */ + if (0) + printk(KERN_NOTICE + "UV buffer %s::%s():%d: vm_start+%d = 0x%lx," + "dma->vmalloc+%d = 0x%lx, w=0x%x\n", + __FILE__, __func__, __LINE__, + k, vma->vm_start + k, m, + (pos + m), p); + vma->vm_pgoff = + ((unsigned long)pos + m) >> PAGE_SHIFT; + if (remap_pfn_range(vma, vma->vm_start + k, + ((unsigned long)pos + m) >> PAGE_SHIFT, + p, vma->vm_page_prot)) + return -EAGAIN; + k += p; + + m += m_increment; + } + + } + +#endif + vout->mmap_count++; + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + return 0; +} + +static int omap_vout_release(struct file *file) +{ + + struct omap_vout_device *vout = file->private_data; + struct videobuf_queue *q; + unsigned int t; + struct omapvideo_info *ovid; + unsigned int r; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__); + ovid = &(vout->vid_info); + + if (!vout) + return 0; + q = &vout->vbq; + + /* Disable all the overlay managers connected with this interface */ + for (t = 0; t < ovid->num_overlays; t++) { + struct omap_overlay *ovl = ovid->overlays[t]; + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + ovl->get_overlay_info(ovl, &info); + info.enabled = 0; + ovl->set_overlay_info(ovl, &info); + } + + } + /* Turn off the pipeline */ + r = omapvid_apply_changes(vout); + if (r) + printk(KERN_WARNING VOUT_NAME "Unable to apply changes\n"); + + /* Free all buffers */ + omap_vout_free_allbuffers(vout); + videobuf_mmap_free(q); + + /* Even if apply changes fails we should continue + freeing allocated memeory */ + if (vout->streaming) { + u32 mask = 0; + + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | + DISPC_IRQ_EVSYNC_ODD; + omap_dispc_unregister_isr(omap_vout_isr, vout, mask); + vout->streaming = 0; + + videobuf_streamoff(q); + videobuf_queue_cancel(q); + + } + + if (vout->mmap_count != 0) + vout->mmap_count = 0; + + vout->opened -= 1; + file->private_data = NULL; + + if (vout->buffer_allocated) + videobuf_mmap_free(q); + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + return r; +} + +static int omap_vout_open(struct file *file) +{ + struct omap_vout_device *vout = NULL; + struct videobuf_queue *q; + + vout = video_drvdata(file); + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__); + + if (vout == NULL) + return -ENODEV; + + /* for now, we only support single open */ + if (vout->opened) + return -EBUSY; + + vout->opened += 1; + + file->private_data = vout; + vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + q = &vout->vbq; + video_vbq_ops.buf_setup = omap_vout_buffer_setup; + video_vbq_ops.buf_prepare = omap_vout_buffer_prepare; + video_vbq_ops.buf_release = omap_vout_buffer_release; + video_vbq_ops.buf_queue = omap_vout_buffer_queue; + spin_lock_init(&vout->vbq_lock); + + videobuf_queue_sg_init(q, &video_vbq_ops, NULL, &vout->vbq_lock, + vout->type, V4L2_FIELD_NONE, sizeof + (struct videobuf_buffer), vout); + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + return 0; +} + +/* V4L2 ioctls */ +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct omap_vout_device *vout = fh; + + strlcpy(cap->driver, VOUT_NAME, + sizeof(cap->driver)); + strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); + cap->bus_info[0] = '\0'; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; + return 0; +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + fmt->index = index; + fmt->type = type; + if (index >= NUM_OUTPUT_FORMATS) + return -EINVAL; + + fmt->flags = omap_formats[index].flags; + strlcpy(fmt->description, omap_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = omap_formats[index].pixelformat; + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_vout_device *vout = fh; + + f->fmt.pix = vout->pix; + return 0; + +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_vout_device *vout = fh; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_video_timings *timing; + + if (vout->streaming) + return -EBUSY; + + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + /* get the display device attached to the overlay */ + timing = &ovl->manager->device->panel.timings; + + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + + omap_vout_try_format(&f->fmt.pix); + return 0; +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_vout_device *vout = fh; + int bpp; + int r; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_video_timings *timing; + + if (vout->streaming) + return -EBUSY; + + mutex_lock(&vout->lock); + + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + timing = &ovl->manager->device->panel.timings; + + /* TODO: check if TILER ADAPTATION is needed here. */ + /* We dont support RGB24-packed mode if vrfb rotation + * is enabled*/ + if ((rotation_enabled(vout)) && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + + /* get the framebuffer parameters */ + + if (rotate_90_or_270(vout)) { + vout->fbuf.fmt.height = timing->x_res; + vout->fbuf.fmt.width = timing->y_res; + } else { + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + } + + /* change to samller size is OK */ + + bpp = omap_vout_try_format(&f->fmt.pix); + if (V4L2_PIX_FMT_NV12 == f->fmt.pix.pixelformat) + f->fmt.pix.sizeimage = f->fmt.pix.width * + f->fmt.pix.height * 3/2; + else + f->fmt.pix.sizeimage = f->fmt.pix.width * + f->fmt.pix.height * bpp; + + /* try & set the new output format */ + vout->bpp = bpp; + vout->pix = f->fmt.pix; + vout->vrfb_bpp = 1; + + /* If YUYV then vrfb bpp is 2, for others its 1 */ + if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat || + V4L2_PIX_FMT_UYVY == vout->pix.pixelformat) + vout->vrfb_bpp = 2; + + /* set default crop and win */ + omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win); + + /* Save the changes in the overlay strcuture */ + r = omapvid_init(vout, 0, 0); + if (r) { + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + mutex_unlock(&vout->lock); + return -EINVAL; + } + mutex_unlock(&vout->lock); + return 0; +} + +static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + int err = -EINVAL; + struct omap_vout_device *vout = fh; + struct v4l2_window *win = &f->fmt.win; + + err = omap_vout_try_window(&vout->fbuf, win); + + if (err) + return err; + + if (vout->vid == OMAP_VIDEO1) + win->global_alpha = 255; + else + win->global_alpha = f->fmt.win.global_alpha; + return 0; +} + +static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_vout_device *vout = fh; + int err = -EINVAL; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct v4l2_window *win = &f->fmt.win; + + mutex_lock(&vout->lock); + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + err = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win); + if (err) { + mutex_unlock(&vout->lock); + return err; + } + if (ovl->id == OMAP_DSS_VIDEO1) + vout->win.global_alpha = 255; + else + vout->win.global_alpha = f->fmt.win.global_alpha; + + vout->win.chromakey = f->fmt.win.chromakey; + mutex_unlock(&vout->lock); + return 0; +} + +static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + fmt->index = index; + fmt->type = type; + if (index >= NUM_OUTPUT_FORMATS) + return -EINVAL; + + fmt->flags = omap_formats[index].flags; + strlcpy(fmt->description, omap_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = omap_formats[index].pixelformat; + return 0; +} + +static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_vout_device *vout = fh; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_overlay_manager_info info; + struct v4l2_window *win = &f->fmt.win; + u32 key_value = 0; + + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + + win->w = vout->win.w; + win->field = vout->win.field; + win->global_alpha = vout->win.global_alpha; + + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + key_value = info.trans_key; + } + win->chromakey = key_value; + return 0; +} + +static int vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + struct omap_vout_device *vout = fh; + enum v4l2_buf_type type = cropcap->type; + struct v4l2_pix_format *pix = &vout->pix; + + cropcap->type = type; + if (type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* Width and height are always even */ + cropcap->bounds.width = pix->width & ~1; + cropcap->bounds.height = pix->height & ~1; + + omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect); + cropcap->pixelaspect.numerator = 1; + cropcap->pixelaspect.denominator = 1; + return 0; +} + +static int vidioc_g_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + struct omap_vout_device *vout = fh; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + crop->c = vout->crop; + return 0; +} + +static int vidioc_s_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + struct omap_vout_device *vout = fh; + int err = -EINVAL; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_video_timings *timing; + + if (vout->streaming) + return -EBUSY; + + mutex_lock(&vout->lock); + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + + if (!ovl->manager || !ovl->manager->device) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + /* get the display device attached to the overlay */ + timing = &ovl->manager->device->panel.timings; + + if (rotate_90_or_270(vout)) { + vout->fbuf.fmt.height = timing->x_res; + vout->fbuf.fmt.width = timing->y_res; + } else { + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + } + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + err = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win, + &vout->fbuf, &crop->c); + mutex_unlock(&vout->lock); + return err; + } else { + mutex_unlock(&vout->lock); + return -EINVAL; + } +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_ROTATE: + v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); + break; + case V4L2_CID_BG_COLOR: + v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0); + break; + case V4L2_CID_VFLIP: + v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + default: + ctrl->name[0] = '\0'; + return -EINVAL; + + } + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) +{ + struct omap_vout_device *vout = fh; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ctrl->value = vout->control[0].value; + return 0; + case V4L2_CID_BG_COLOR: + { + struct omap_overlay_manager_info info; + struct omap_overlay *ovl; + ovl = vout->vid_info.overlays[0]; + + if (!ovl->manager || !ovl->manager->get_manager_info) + return -EINVAL; + + ovl->manager->get_manager_info(ovl->manager, &info); + ctrl->value = info.default_color; + return 0; + } + + case V4L2_CID_VFLIP: + ctrl->value = vout->control[2].value; + return 0; + default: + return -EINVAL; + } +} + +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) +{ + struct omap_vout_device *vout = fh; + + switch (a->id) { + case V4L2_CID_ROTATE: + { + int rotation = a->value; + + mutex_lock(&vout->lock); + + if (rotation && + vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + + if ((v4l2_rot_to_dss_rot(rotation, &vout->rotation, + vout->mirror))) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + vout->control[0].value = rotation; + mutex_unlock(&vout->lock); + return 0; + } + case V4L2_CID_BG_COLOR: + { + unsigned int color = a->value; + struct omap_overlay_manager_info info;; + struct omap_overlay *ovl; + ovl = vout->vid_info.overlays[0]; + + mutex_lock(&vout->lock); + if (!ovl->manager || !ovl->manager->get_manager_info) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + ovl->manager->get_manager_info(ovl->manager, &info); + info.default_color = color; + if (ovl->manager->set_manager_info(ovl->manager, &info)) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + + vout->control[1].value = color; + mutex_unlock(&vout->lock); + return 0; + } + case V4L2_CID_VFLIP: + { + unsigned int mirror = a->value; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + + mutex_lock(&vout->lock); + + if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + vout->mirror = mirror; + vout->control[2].value = mirror; + mutex_unlock(&vout->lock); + return 0; + } + + default: + return -EINVAL; + } + +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &(vout->vbq); + unsigned int i, num_buffers = 0; + int ret = 0; + struct videobuf_dmabuf *dmabuf = NULL; + + printk(KERN_INFO VOUT_NAME + "entered REQbuf: \n"); + + if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0)) + return -EINVAL; + /* if memory is not mmp or userptr + return error */ + if ((V4L2_MEMORY_MMAP != req->memory) && + (V4L2_MEMORY_USERPTR != req->memory)) + return -EINVAL; + + mutex_lock(&vout->lock); + /* Cannot be requested when streaming is on */ + if (vout->streaming) { + mutex_unlock(&vout->lock); + return -EBUSY; + } + + /* If buffers are already allocated free them */ + if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) { + if (vout->mmap_count) { + mutex_unlock(&vout->lock); + return -EBUSY; + } + +#ifndef TILER_ALLOCATE_V4L2 +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_VIDEO3 == vout->vid) + num_buffers = video3_numbuffers; + else +#endif + num_buffers = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + + for (i = num_buffers; i < vout->buffer_allocated; i++) { + dmabuf = videobuf_to_dma(q->bufs[i]); + omap_vout_free_buffer((u32)dmabuf->vmalloc, + dmabuf->bus_addr, vout->buffer_size); + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + vout->buffer_allocated = num_buffers; +#else /* TILER_ALLOCATE_V4L2*/ + for (i = 0; i < vout->buffer_allocated; i++) { + tiler_free_buf(vout->buf_phy_addr[i]); + tiler_free_buf(vout->buf_phy_uv_addr[i]); + vout->buf_phy_addr[i] = 0; + vout->buf_phy_uv_addr[i] = 0; + } + vout->buffer_allocated = 0; + +#endif /* TILER_ALLOCATE_V4L2*/ + videobuf_mmap_free(q); + + + } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) { + if (vout->buffer_allocated) { + videobuf_mmap_free(q); + for (i = 0; i < vout->buffer_allocated; i++) { + kfree(q->bufs[i]); + q->bufs[i] = NULL; + } + vout->buffer_allocated = 0; + } + } + /*store the memory type in data structure */ + vout->memory = req->memory; + + INIT_LIST_HEAD(&vout->dma_queue); + + /* call videobuf_reqbufs api */ + ret = videobuf_reqbufs(q, req); + if (ret < 0) { + mutex_unlock(&vout->lock); + return ret; + } + + vout->buffer_allocated = req->count; + for (i = 0; i < req->count; i++) { + dmabuf = videobuf_to_dma(q->bufs[i]); +#ifdef CONFIG_ARCH_OMAP4 + if (V4L2_PIX_FMT_NV12 == vout->pix.pixelformat) { +#ifndef TILER_ALLOCATE_V4L2 + dmabuf->vmalloc = (void *) omapvid_get_uvbase_nv12( + vout->buf_phy_addr[i], + vout->pix.height, + vout->pix.width); +#else + dmabuf->vmalloc = (void *) vout->buf_phy_uv_addr[i]; +#endif /* TILER_ALLOCATE_V4L2 */ + } else + dmabuf->vmalloc = NULL; +#else + dmabuf->vmalloc = (void *) vout->buf_virt_addr[i]; +#endif + dmabuf->bus_addr = (dma_addr_t) vout->buf_phy_addr[i]; + dmabuf->sglen = 1; + } + mutex_unlock(&vout->lock); + return 0; +} + +static int vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct omap_vout_device *vout = fh; + + return videobuf_querybuf(&(vout->vbq), b); +} + +static int vidioc_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buffer) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int ret = 0; + + printk(KERN_INFO VOUT_NAME + "entered qbuf: buffer address: %x \n", (unsigned int) buffer); + + if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) || + (buffer->index >= vout->buffer_allocated) || + + (q->bufs[buffer->index]->memory != buffer->memory)) { + return -EINVAL; + } + if (V4L2_MEMORY_USERPTR == buffer->memory) { + if ((buffer->length < vout->pix.sizeimage) || + (0 == buffer->m.userptr)) { + return -EINVAL; + } + } + +#ifndef CONFIG_ARCH_OMAP4 + if ((rotation_enabled(vout)) && + vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) { + printk(KERN_WARNING VOUT_NAME + "DMA Channel not allocated for Rotation\n"); + return -EINVAL; + } +#endif + ret = videobuf_qbuf(q, buffer); + return ret; +} + +static int vidioc_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int ret = 0; + + printk(KERN_INFO VOUT_NAME + "entered DQbuf: buffer address: %x \n", (unsigned int) b); + + if (!vout->streaming) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) + /* Call videobuf_dqbuf for non blocking mode */ + ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1); + else + /* Call videobuf_dqbuf for blocking mode */ + ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0); + return ret; +} + +static int vidioc_streamon(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + u32 addr = 0, uv_addr = 0; + int r = 0; + int t; + struct omapvideo_info *ovid = &(vout->vid_info); + u32 mask = 0; + + mutex_lock(&vout->lock); + + if (vout->streaming) { + mutex_unlock(&vout->lock); + return -EBUSY; + } + + r = videobuf_streamon(q); + if (r < 0) { + mutex_unlock(&vout->lock); + return r; + } + + if (list_empty(&vout->dma_queue)) { + mutex_unlock(&vout->lock); + return -EIO; + } + /* Get the next frame from the buffer queue */ + vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove buffer from the buffer queue */ + list_del(&vout->cur_frm->queue); + /* Mark state of the current frame to active */ + vout->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vout->field_id = 0; + + /* set flag here. Next QBUF will start DMA */ + vout->streaming = 1; + + vout->first_int = 1; + +#ifndef CONFIG_ARCH_OMAP4 + if (omap_vout_calculate_offset(vout)) { + mutex_unlock(&vout->lock); + return -EINVAL; + } +#endif + addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i] + + vout->cropped_offset; + uv_addr = (unsigned long) vout->queued_buf_uv_addr[vout->cur_frm->i]; + /* OMAP4: check if cropped_offset is needed? */ + + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | + DISPC_IRQ_EVSYNC_ODD; + + omap_dispc_register_isr(omap_vout_isr, vout, mask); + + for (t = 0; t < ovid->num_overlays; t++) { + struct omap_overlay *ovl = ovid->overlays[t]; + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + ovl->get_overlay_info(ovl, &info); + info.enabled = 1; + info.paddr = addr; + info.p_uv_addr = uv_addr; + if (ovl->set_overlay_info(ovl, &info)) + return -EINVAL; + } + } + + /* First save the configuration in ovelray structure */ + r = omapvid_init(vout, addr, uv_addr); + if (r) + printk(KERN_ERR VOUT_NAME "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + r = omapvid_apply_changes(vout); + if (r) + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + + mutex_unlock(&vout->lock); + return r; +} + +static int vidioc_streamoff(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct omap_vout_device *vout = fh; + int t, r = 0; + struct omapvideo_info *ovid = &(vout->vid_info); + u32 mask = 0; + + if (!vout->streaming) + return -EINVAL; + + vout->streaming = 0; + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | + DISPC_IRQ_EVSYNC_ODD; + + omap_dispc_unregister_isr(omap_vout_isr, vout, mask); + + for (t = 0; t < ovid->num_overlays; t++) { + struct omap_overlay *ovl = ovid->overlays[t]; + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + info.enabled = 0; + r = ovl->set_overlay_info(ovl, &info); + if (r) { + printk(KERN_ERR VOUT_NAME "failed to \ + update overlay info\n"); + return r; + } + } + + /* Turn of the pipeline */ + r = omapvid_apply_changes(vout); + if (r) { + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + return r; + } + videobuf_streamoff(&vout->vbq); + videobuf_queue_cancel(&vout->vbq); + } + return 0; +} + +static int vidioc_s_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + struct omap_vout_device *vout = fh; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_overlay_manager_info info; + enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + int enable = 0; + + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + + /* OMAP DSS doesn't support Source and Destination color + key together */ + if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) && + (a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) + return -EINVAL; + /* OMAP DSS Doesn't support the Destination color key + and alpha blending together */ + if (a->flags & V4L2_FBUF_FLAG_CHROMAKEY && + (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA)) + return -EINVAL; + + if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; + key_type = OMAP_DSS_COLOR_KEY_VID_SRC; + } else + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY; + + if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY; + key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + } else + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY; + + if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY | + V4L2_FBUF_FLAG_SRC_CHROMAKEY)) + enable = 1; + else + enable = 0; + + if (ovl->manager && ovl->manager->get_manager_info && + ovl->manager->set_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + info.trans_enabled = enable; + info.trans_key_type = key_type; + info.trans_key = vout->win.chromakey; + if (ovl->manager->set_manager_info(ovl->manager, &info)) + return -EINVAL; + } + + if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + enable = 1; + } else { + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA; + enable = 0; + } + if (ovl->manager && ovl->manager->get_manager_info && + ovl->manager->set_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + info.alpha_enabled = enable; + if (ovl->manager->set_manager_info(ovl->manager, &info)) + return -EINVAL; + } + + return 0; +} + +static int vidioc_g_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + struct omap_vout_device *vout = fh; + struct omap_overlay_manager_info info; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + enum omap_dss_trans_key_type key_type; + + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + + a->flags = 0x0; + + a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY + | V4L2_FBUF_CAP_SRC_CHROMAKEY; + + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC) + a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; + if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST) + a->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + + } + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + if (info.alpha_enabled) + a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + } + + return 0; +} + +static const struct v4l2_ioctl_ops vout_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static const struct v4l2_file_operations omap_vout_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .mmap = omap_vout_mmap, + .open = omap_vout_open, + .release = omap_vout_release, +}; + +/* Init functions used during driver intitalization */ +/* Initial setup of video_data */ +static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) +{ + struct v4l2_pix_format *pix; + struct video_device *vfd; + struct v4l2_control *control; + struct omap_dss_device *display = + vout->vid_info.overlays[0]->manager->device; + + /* set the default pix */ + pix = &vout->pix; + + /* Set the default picture of QVGA */ + pix->width = QQVGA_WIDTH; + pix->height = QQVGA_HEIGHT; + + /* Default pixel format is RGB 5-6-5 */ + pix->pixelformat = V4L2_PIX_FMT_RGB565; + pix->field = V4L2_FIELD_ANY; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + pix->colorspace = V4L2_COLORSPACE_JPEG; + + vout->bpp = RGB565_BPP; + vout->fbuf.fmt.width = display->panel.timings.x_res; + vout->fbuf.fmt.height = display->panel.timings.y_res; + + /* Set the data structures for the overlay parameters*/ + vout->win.global_alpha = 255; + vout->fbuf.flags = 0; + vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA | + V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY; + vout->win.chromakey = 0; + + omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win); + + /*Disable the rotation. */ + control = vout->control; + + control[0].id = V4L2_CID_ROTATE; + control[0].value = 0; + vout->rotation = -1; + vout->mirror = 0; + vout->vrfb_bpp = 2; + + control[1].id = V4L2_CID_BG_COLOR; + control[1].value = 0; + + /* initialize the video_device struct */ + vfd = vout->vfd = video_device_alloc(); + + if (!vfd) { + printk(KERN_ERR VOUT_NAME ": could not allocate\ + video device struct\n"); + return -ENOMEM; + } + vfd->release = video_device_release; + vfd->ioctl_ops = &vout_ioctl_ops; + + strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name)); + vfd->vfl_type = VFL_TYPE_GRABBER; + + /* need to register for a VID_HARDWARE_* ID in videodev.h */ + vfd->fops = &omap_vout_fops; + mutex_init(&vout->lock); + + vfd->minor = -1; + return 0; + +} + +/* Setup video buffers */ +static int __init omap_vout_setup_video_bufs(struct platform_device *pdev, + int vid_num) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = container_of(v4l2_dev, struct + omap2video_device, v4l2_dev); + struct omap_vout_device *vout; + int i, r = 0; + unsigned numbuffers; + struct video_device *vfd; +#ifndef CONFIG_ARCH_OMAP4 /* TODO: related to rotation */ + int j; + int image_width, image_height; + int static_vrfb_allocation = 0, vrfb_num_bufs = 4; +#endif + vout = vid_dev->vouts[vid_num]; + vfd = vout->vfd; + +#ifndef TILER_ALLOCATE_V4L2 +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_VIDEO3 == vid_num) { + numbuffers = video3_numbuffers; + vout->buffer_size = video3_bufsize; + } else +#endif + { + numbuffers = (vid_num == 0) ? video1_numbuffers + : video2_numbuffers; + vout->buffer_size = (vid_num == 0) ? video1_bufsize + : video2_bufsize; + } + printk(KERN_INFO VOUT_NAME "Buffer Size = %d\n", vout->buffer_size); + for (i = 0; i < numbuffers; i++) { + vout->buf_virt_addr[i] = + omap_vout_alloc_buffer(vout->buffer_size, + (u32 *) &vout->buf_phy_addr[i]); + if (!vout->buf_virt_addr[i]) { + numbuffers = i; + r = -ENOMEM; + goto free_buffers; + } + } + for (i = 0; i < 4; i++) { + + if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) { + printk(KERN_INFO VOUT_NAME ": VRFB Region allocation \ + for rotation failed\n"); + r = -ENOMEM; + break; + } + } + if (r == -ENOMEM) { + for (j = 0; j < i; j++) + omap_vrfb_release_ctx(&vout->vrfb_context[j]); + + goto free_buffers; + } + + vout->cropped_offset = 0; + + /* Calculate VRFB memory size */ + /* allocate for worst case size */ + image_width = VID_MAX_WIDTH / TILE_SIZE; + if (VID_MAX_WIDTH % TILE_SIZE) + image_width++; + + image_width = image_width * TILE_SIZE; + image_height = VID_MAX_HEIGHT / TILE_SIZE; + + if (VID_MAX_HEIGHT % TILE_SIZE) + image_height++; + + image_height = image_height * TILE_SIZE; + vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2); + + /* + * Request and Initialize DMA, for DMA based VRFB transfer + */ + vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE; + vout->vrfb_dma_tx.dma_ch = -1; + vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED; + r = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX", + omap_vout_vrfb_dma_tx_callback, + (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch); + if (r < 0) { + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + printk(KERN_INFO VOUT_NAME ": DMA Channel not alloted\ + for video%d [v4l2]\n", vfd->minor); + } + init_waitqueue_head(&vout->vrfb_dma_tx.wait); + + /* Allocate VRFB buffers if selected through bootargs */ + static_vrfb_allocation = (vid_num == 0) ? + vid1_static_vrfb_alloc : vid2_static_vrfb_alloc; + + /* statically allocated the VRFB buffer is done through + commands line aruments */ + if (static_vrfb_allocation) { + if (omap_vout_allocate_vrfb_buffers(vout, vrfb_num_bufs, -1)) { + r = -ENOMEM; + goto free_buffers; + } + vout->vrfb_static_allocation = 1; + } +#endif /* TILER_ALLOCATE_V4L2 */ + +/* NOTE: OMAP4, if TILER allocation, then nothing to pre-allocate */ + + return 0; + +free_buffers: + for (i = 0; i < numbuffers; i++) { + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buf_phy_addr[i], vout->buffer_size); + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + return r; + +} + +/* Create video out devices */ +static int __init omap_vout_create_video_devices(struct platform_device *pdev) +{ + int r = 0, k; + struct omap_vout_device *vout; + struct video_device *vfd = NULL; + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + + struct omap2video_device *vid_dev = container_of(v4l2_dev, struct + omap2video_device, v4l2_dev); + for (k = 0; k < pdev->num_resources; k++) { + + vout = kmalloc(sizeof(struct omap_vout_device), GFP_KERNEL); + if (!vout) { + printk(KERN_ERR VOUT_NAME ": could not allocate \ + memory\n"); + return -ENOMEM; + } + + memset(vout, 0, sizeof(struct omap_vout_device)); + + vout->vid = k; + vid_dev->vouts[k] = vout; + vout->vid_dev = vid_dev; +/* TODO: OMAP4: check?? */ + if (pdev->num_resources == 1) + vout->vid_info.overlays[0] = vid_dev->overlays[k + 2]; + else + /* Else select video1 and video2 one by one. */ + vout->vid_info.overlays[0] = vid_dev->overlays[k + 1]; + vout->vid_info.num_overlays = 1; + vout->vid_info.id = k + 1; + vid_dev->num_videos++; + + /* Setup the default configuration for the video devices + */ + if (omap_vout_setup_video_data(vout) != 0) { + r = -ENOMEM; + goto error; + } + + /* Allocate default number of buffers for the video streaming + * and reserve the VRFB space for rotation + */ + if (omap_vout_setup_video_bufs(pdev, k) != 0) { + r = -ENOMEM; + goto error1; + } + + /* Register the Video device with V4L2 + */ + vfd = vout->vfd; + if (video_register_device(vfd, VFL_TYPE_GRABBER, k + 1) < 0) { + printk(KERN_ERR VOUT_NAME ": could not register \ + Video for Linux device\n"); + vfd->minor = -1; + r = -ENODEV; + goto error2; + } + video_set_drvdata(vfd, vout); + + /* Configure the overlay structure */ + r = omapvid_init(vid_dev->vouts[k], 0, 0); + + if (r) + goto error2; + else + goto success; +error2: +#ifndef CONFIG_ARCH_OMAP4 + omap_vout_release_vrfb(vout); +#endif + omap_vout_free_buffers(vout); +error1: + video_device_release(vfd); +error: + kfree(vout); + return r; + +success: + printk(KERN_INFO VOUT_NAME ": registered and initialized\ + video device %d [v4l2]\n", vfd->minor); + if (k == (pdev->num_resources - 1)) + return 0; + } + return -ENODEV; + +} +/* Driver functions */ +static int omap_vout_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = container_of(v4l2_dev, struct + omap2video_device, v4l2_dev); + int k; + + v4l2_device_unregister(v4l2_dev); + for (k = 0; k < pdev->num_resources; k++) + omap_vout_cleanup_device(vid_dev->vouts[k]); + + for (k = 0; k < vid_dev->num_displays; k++) { + if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED) + vid_dev->displays[k]->disable(vid_dev->displays[k]); + + omap_dss_put_device(vid_dev->displays[k]); + } + kfree(vid_dev); + return 0; +} + +static int __init omap_vout_probe(struct platform_device *pdev) +{ + int r = 0, i; + struct omap2video_device *vid_dev = NULL; + struct omap_overlay *ovl; + struct omap_dss_device *def_display; + struct omap_dss_device *dssdev; + + if (pdev->num_resources == 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + r = -ENODEV; + return r; + } + + vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL); + if (vid_dev == NULL) { + r = -ENOMEM; + return r; + } + + vid_dev->num_displays = 0; + dssdev = NULL; + for_each_dss_dev(dssdev) { + omap_dss_get_device(dssdev); + vid_dev->displays[vid_dev->num_displays++] = dssdev; + } + + if (vid_dev->num_displays == 0) { + dev_err(&pdev->dev, "no displays\n"); + r = -EINVAL; + goto error0; + } + + vid_dev->num_overlays = omap_dss_get_num_overlays(); + for (i = 0; i < vid_dev->num_overlays; i++) + vid_dev->overlays[i] = omap_dss_get_overlay(i); + + vid_dev->num_managers = omap_dss_get_num_overlay_managers(); + for (i = 0; i < vid_dev->num_managers; i++) + vid_dev->managers[i] = omap_dss_get_overlay_manager(i); + + /* Get the Video1, video2 (and Video3 for OMAP4) overlay. + * Setup the Display attached to that overlays + */ + for (i = 1; i < (NUM_OF_VIDEO_CHANNELS + 1); i++) { + ovl = omap_dss_get_overlay(i); + if (ovl->manager && ovl->manager->device) { + def_display = ovl->manager->device; + } else { + dev_warn(&pdev->dev, "cannot find display\n"); + def_display = NULL; + } + if (def_display) { + r = def_display->enable(def_display); + if (r) { + /* Here we are not considering a error as display may be + enabled by frame buffer driver */ + dev_warn(&pdev->dev, + "'%s' Display already enabled\n", + def_display->name); + } + /* set the update mode */ + if (def_display->caps & + OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + if (def_display->set_update_mode) + def_display->set_update_mode( + def_display, + OMAP_DSS_UPDATE_AUTO); + if (def_display->enable_te) + def_display->enable_te(def_display, 1); +#else + if (def_display->set_update_mode) + def_display->set_update_mode( + def_display, + OMAP_DSS_UPDATE_MANUAL); + if (def_display->enable_te) + def_display->enable_te(def_display, 0); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode( + def_display, + OMAP_DSS_UPDATE_AUTO); + } + } + } + + if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) { + printk(KERN_ERR VOUT_NAME + "v4l2_device_register unsuccessful\n"); + return -ENODEV; + } + + r = omap_vout_create_video_devices(pdev); + if (r) + goto error0; + + for (i = 0; i < vid_dev->num_displays; i++) { + struct omap_dss_device *display = vid_dev->displays[i]; + + if (display->update) + display->update(display, 0, 0, + display->panel.timings.x_res, + display->panel.timings.y_res); + } + printk(KERN_INFO VOUT_NAME "display->updated\n"); + return 0; + +error0: + kfree(vid_dev); + return r; +} + +static struct platform_driver omap_vout_driver = { + .driver = { + .name = VOUT_NAME, + }, + .probe = omap_vout_probe, + .remove = omap_vout_remove, +}; + +void omap_vout_isr(void *arg, unsigned int irqstatus) +{ + int r; + struct timeval timevalue; + struct omap_vout_device *vout = + (struct omap_vout_device *) arg; + u32 addr, fid, uv_addr; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_dss_device *cur_display; + + if (!vout->streaming) + return; + + ovid = &(vout->vid_info); + ovl = ovid->overlays[0]; + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) + return; + cur_display = ovl->manager->device; + + spin_lock(&vout->vbq_lock); + do_gettimeofday(&timevalue); + + if (cur_display->type == OMAP_DISPLAY_TYPE_DPI) { + if (!(irqstatus & DISPC_IRQ_VSYNC)) + return; + if (!vout->first_int && (vout->cur_frm != vout->next_frm)) { + vout->cur_frm->ts = timevalue; + vout->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->cur_frm->done); + vout->cur_frm = vout->next_frm; + } + vout->first_int = 0; + if (list_empty(&vout->dma_queue)) { + spin_unlock(&vout->vbq_lock); + return; + } + + vout->next_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vout->next_frm->queue); + + vout->next_frm->state = VIDEOBUF_ACTIVE; + + addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i] + + vout->cropped_offset; + + uv_addr = (unsigned long) vout->queued_buf_uv_addr[ + vout->next_frm->i]; + /* TODO: check the cropped offset part*/ + + /* First save the configuration in ovelray structure */ + r = omapvid_init(vout, addr, uv_addr); + if (r) + printk(KERN_ERR VOUT_NAME + "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + r = omapvid_apply_changes(vout); + if (r) + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + } else { + + if (vout->first_int) { + vout->first_int = 0; + spin_unlock(&vout->vbq_lock); + return; + } + if (irqstatus & DISPC_IRQ_EVSYNC_ODD) { + fid = 1; + } else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN) { + fid = 0; + } else { + spin_unlock(&vout->vbq_lock); + return; + } + vout->field_id ^= 1; + if (fid != vout->field_id) { + if (0 == fid) + vout->field_id = fid; + + spin_unlock(&vout->vbq_lock); + return; + } + if (0 == fid) { + if (vout->cur_frm == vout->next_frm) { + spin_unlock(&vout->vbq_lock); + return; + } + vout->cur_frm->ts = timevalue; + vout->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->cur_frm->done); + vout->cur_frm = vout->next_frm; + } else if (1 == fid) { + if (list_empty(&vout->dma_queue) || + (vout->cur_frm != vout->next_frm)) { + spin_unlock(&vout->vbq_lock); + return; + } + vout->next_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vout->next_frm->queue); + + vout->next_frm->state = VIDEOBUF_ACTIVE; + addr = (unsigned long) + vout->queued_buf_addr[vout->next_frm->i] + + vout->cropped_offset; + uv_addr = (unsigned long) + vout->queued_buf_uv_addr[vout->next_frm->i]; + + /* TODO: check the cropped offset part*/ + + /* First save the configuration in ovelray structure */ + r = omapvid_init(vout, addr, uv_addr); + if (r) + printk(KERN_ERR VOUT_NAME "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + r = omapvid_apply_changes(vout); + if (r) + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + } + + } + spin_unlock(&vout->vbq_lock); +} + +static void omap_vout_cleanup_device(struct omap_vout_device *vout) +{ + + struct video_device *vfd; + + if (!vout) + return; + vfd = vout->vfd; + + if (vfd) { + if (vfd->minor == -1) { + /* + * The device was never registered, so release the + * video_device struct directly. + */ + video_device_release(vfd); + } else { + /* + * The unregister function will release the video_device + * struct as well as unregistering it. + */ + video_unregister_device(vfd); + } + } + +#ifndef CONFIG_ARCH_OMAP4 + omap_vout_release_vrfb(vout); +#endif + omap_vout_free_buffers(vout); +#ifdef CONFIG_ARCH_OMAP4 + omap_vout_free_tiler_buffers(vout); + /* TODO: check if this needs to be done? */ +#else + /* Free the VRFB buffer if allocated + * init time + */ + if (vout->vrfb_static_allocation) + omap_vout_free_vrfb_buffers(vout); +#endif + kfree(vout); +} + +static int __init omap_vout_init(void) +{ + + if (platform_driver_register(&omap_vout_driver) != 0) { + printk(KERN_ERR VOUT_NAME ": could not register \ + Video driver\n"); + return -EINVAL; + } + return 0; +} + +static void omap_vout_cleanup(void) +{ + platform_driver_unregister(&omap_vout_driver); +} + +late_initcall(omap_vout_init); +module_exit(omap_vout_cleanup); diff --git a/drivers/media/video/omap/omap_voutdef.h b/drivers/media/video/omap/omap_voutdef.h new file mode 100644 index 000000000000..700d6ccd347c --- /dev/null +++ b/drivers/media/video/omap/omap_voutdef.h @@ -0,0 +1,160 @@ +/* + * drivers/media/video/omap/omap_voutdef.h + * + * Copyright (C) 2009 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef OMAP_VOUTDEF_H +#define OMAP_VOUTDEF_H + +#include <mach/display.h> + +#define YUYV_BPP 2 +#define RGB565_BPP 2 +#define RGB24_BPP 3 +#define RGB32_BPP 4 +#define TILE_SIZE 32 +#define YUYV_VRFB_BPP 2 +#define RGB_VRFB_BPP 1 +#define MAX_CID 3 + +#define MAC_VRFB_CTXS 4 + +#ifdef CONFIG_ARCH_OMAP4 /* TODO: OMAP4 update displays and managers */ +#define MAX_VOUT_DEV 3 +#define MAX_OVLS 4 +#else +#define MAX_VOUT_DEV 2 +#define MAX_OVLS 3 +#endif +#define MAX_DISPLAYS 3 +#define MAX_MANAGERS 3 + +/* Enum for Rotation + * DSS understands rotation in 0, 1, 2, 3 context + * while V4L2 driver understands it as 0, 90, 180, 270 + */ +enum dss_rotation { + dss_rotation_0_degree = 0, + dss_rotation_90_degree = 1, + dss_rotation_180_degree = 2, + dss_rotation_270_degree = 3, +}; + +/* + * This structure is used to store the DMA transfer parameters + * for VRFB hidden buffer + */ +struct vid_vrfb_dma { + int dev_id; + int dma_ch; + int req_status; + int tx_status; + wait_queue_head_t wait; +}; + +struct omapvideo_info { + int id; + int num_overlays; + struct omap_overlay *overlays[MAX_OVLS]; +}; + +struct omap2video_device { + struct mutex mtx; + + int state; + + struct v4l2_device v4l2_dev; + int num_videos; + struct omap_vout_device *vouts[MAX_VOUT_DEV]; + + int num_displays; + struct omap_dss_device *displays[MAX_DISPLAYS]; + int num_overlays; + struct omap_overlay *overlays[MAX_OVLS]; + int num_managers; + struct omap_overlay_manager *managers[MAX_MANAGERS]; +}; + +/* per-device data structure */ +struct omap_vout_device { + + struct omapvideo_info vid_info; + struct video_device *vfd; + struct omap2video_device *vid_dev; + int vid; + int opened; + + /* we don't allow to change image fmt/size once buffer has + * been allocated + */ + int buffer_allocated; + /* allow to reuse previosuly allocated buffer which is big enough */ + int buffer_size; + /* keep buffer info accross opens */ + unsigned long buf_virt_addr[VIDEO_MAX_FRAME]; + unsigned long buf_phy_addr[VIDEO_MAX_FRAME]; + +/* NV12 support*/ + unsigned long buf_phy_uv_addr[VIDEO_MAX_FRAME]; + u8 *queued_buf_uv_addr[VIDEO_MAX_FRAME]; + + enum omap_color_mode dss_mode; + + /* we don't allow to request new buffer when old buffers are + * still mmaped + */ + int mmap_count; + + spinlock_t vbq_lock; /* spinlock for videobuf queues */ + unsigned long field_count; /* field counter for videobuf_buffer */ + + /* non-NULL means streaming is in progress. */ + bool streaming; + + struct v4l2_pix_format pix; + struct v4l2_rect crop; + struct v4l2_window win; + struct v4l2_framebuffer fbuf; + + /* Lock to protect the shared data structures in ioctl */ + struct mutex lock; + + /* V4L2 control structure for different control id */ + struct v4l2_control control[MAX_CID]; + enum dss_rotation rotation; + bool mirror; + int flicker_filter; + /* V4L2 control structure for different control id */ + + int bpp; /* bytes per pixel */ + int vrfb_bpp; /* bytes per pixel with respect to VRFB */ + struct vid_vrfb_dma vrfb_dma_tx; + unsigned int smsshado_phy_addr[MAC_VRFB_CTXS]; + unsigned int smsshado_virt_addr[MAC_VRFB_CTXS]; + struct vrfb vrfb_context[MAC_VRFB_CTXS]; + bool vrfb_static_allocation; + unsigned int smsshado_size; + unsigned char pos; + + int ps, vr_ps, line_length, first_int, field_id; + enum v4l2_memory memory; + struct videobuf_buffer *cur_frm, *next_frm; + struct list_head dma_queue; + u8 *queued_buf_addr[VIDEO_MAX_FRAME]; + u32 cropped_offset; + s32 tv_field1_offset; + void *isr_handle; + + /* Buffer queue variabled */ + struct omap_vout_device *vout; + enum v4l2_buf_type type; + struct videobuf_queue vbq; + int io_allowed; + +}; +#endif /* ifndef OMAP_VOUTDEF_H */ diff --git a/drivers/media/video/omap/omap_voutlib.c b/drivers/media/video/omap/omap_voutlib.c new file mode 100644 index 000000000000..37fed10107cc --- /dev/null +++ b/drivers/media/video/omap/omap_voutlib.c @@ -0,0 +1,257 @@ +/* + * drivers/media/video/omap/omap_voutlib.c + * + * Copyright (C) 2005-2009 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Based on the OMAP2 camera driver + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP24xx camera controller. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2009 Texas Instruments. + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +MODULE_AUTHOR("Texas Instruments."); +MODULE_DESCRIPTION("OMAP Video library"); +MODULE_LICENSE("GPL"); + +/* Return the default overlay cropping rectangle in crop given the image + * size in pix and the video display size in fbuf. The default + * cropping rectangle is the largest rectangle no larger than the capture size + * that will fit on the display. The default cropping rectangle is centered in + * the image. All dimensions and offsets are rounded down to even numbers. + */ +void omap_vout_default_crop(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop) +{ + crop->width = (pix->width < fbuf->fmt.width) ? + pix->width : fbuf->fmt.width; + crop->height = (pix->height < fbuf->fmt.height) ? + pix->height : fbuf->fmt.height; + crop->width &= ~1; + crop->height &= ~1; + crop->left = ((pix->width - crop->width) >> 1) & ~1; + crop->top = ((pix->height - crop->height) >> 1) & ~1; +} +EXPORT_SYMBOL_GPL(omap_vout_default_crop); + +/* Given a new render window in new_win, adjust the window to the + * nearest supported configuration. The adjusted window parameters are + * returned in new_win. + * Returns zero if succesful, or -EINVAL if the requested window is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_try_window(struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win) +{ + struct v4l2_rect try_win; + + /* make a working copy of the new_win rectangle */ + try_win = new_win->w; + + /* adjust the preview window so it fits on the display by clipping any + * offscreen areas + */ + if (try_win.left < 0) { + try_win.width += try_win.left; + try_win.left = 0; + } + if (try_win.top < 0) { + try_win.height += try_win.top; + try_win.top = 0; + } + try_win.width = (try_win.width < fbuf->fmt.width) ? + try_win.width : fbuf->fmt.width; + try_win.height = (try_win.height < fbuf->fmt.height) ? + try_win.height : fbuf->fmt.height; + if (try_win.left + try_win.width > fbuf->fmt.width) + try_win.width = fbuf->fmt.width - try_win.left; + if (try_win.top + try_win.height > fbuf->fmt.height) + try_win.height = fbuf->fmt.height - try_win.top; + try_win.width &= ~1; + try_win.height &= ~1; + + if (try_win.width <= 0 || try_win.height <= 0) + return -EINVAL; + + /* We now have a valid preview window, so go with it */ + new_win->w = try_win; + new_win->field = V4L2_FIELD_ANY; + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_try_window); + +/* Given a new render window in new_win, adjust the window to the + * nearest supported configuration. The image cropping window in crop + * will also be adjusted if necessary. Preference is given to keeping the + * the window as close to the requested configuration as possible. If + * successful, new_win, vout->win, and crop are updated. + * Returns zero if succesful, or -EINVAL if the requested preview window is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_new_window(struct v4l2_rect *crop, + struct v4l2_window *win, struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win) +{ + int err; + + err = omap_vout_try_window(fbuf, new_win); + if (err) + return err; + + /* update our preview window */ + win->w = new_win->w; + win->field = new_win->field; + win->chromakey = new_win->chromakey; + + /* adjust the cropping window to allow for resizing limitations */ + if ((crop->height/win->w.height) >= 4) { + /* The maximum vertical downsizing ratio is 4:1 */ + crop->height = win->w.height * 4; + } + if ((crop->width/win->w.width) >= 4) { + /* The maximum horizontal downsizing ratio is 4:1 */ + crop->width = win->w.width * 4; + } + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_new_window); + +/* Given a new cropping rectangle in new_crop, adjust the cropping rectangle to + * the nearest supported configuration. The image render window in win will + * also be adjusted if necessary. The preview window is adjusted such that the + * horizontal and vertical rescaling ratios stay constant. If the render + * window would fall outside the display boundaries, the cropping rectangle + * will also be adjusted to maintain the rescaling ratios. If successful, crop + * and win are updated. + * Returns zero if succesful, or -EINVAL if the requested cropping rectangle is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_new_crop(struct v4l2_pix_format *pix, + struct v4l2_rect *crop, struct v4l2_window *win, + struct v4l2_framebuffer *fbuf, const struct v4l2_rect *new_crop) +{ + struct v4l2_rect try_crop; + unsigned long vresize, hresize; + + /* make a working copy of the new_crop rectangle */ + try_crop = *new_crop; + + /* adjust the cropping rectangle so it fits in the image */ + if (try_crop.left < 0) { + try_crop.width += try_crop.left; + try_crop.left = 0; + } + if (try_crop.top < 0) { + try_crop.height += try_crop.top; + try_crop.top = 0; + } + try_crop.width = (try_crop.width < pix->width) ? + try_crop.width : pix->width; + try_crop.height = (try_crop.height < pix->height) ? + try_crop.height : pix->height; + if (try_crop.left + try_crop.width > pix->width) + try_crop.width = pix->width - try_crop.left; + if (try_crop.top + try_crop.height > pix->height) + try_crop.height = pix->height - try_crop.top; + try_crop.width &= ~1; + try_crop.height &= ~1; + if (try_crop.width <= 0 || try_crop.height <= 0) + return -EINVAL; + + if (crop->height != win->w.height) { + /* If we're resizing vertically, we can't support a crop width + * wider than 768 pixels. + */ + if (try_crop.width > 768) + try_crop.width = 768; + } + /* vertical resizing */ + vresize = (1024 * crop->height) / win->w.height; + if (vresize > 4096) + vresize = 4096; + else if (vresize == 0) + vresize = 1; + win->w.height = ((1024 * try_crop.height) / vresize) & ~1; + if (win->w.height == 0) + win->w.height = 2; + if (win->w.height + win->w.top > fbuf->fmt.height) { + /* We made the preview window extend below the bottom of the + * display, so clip it to the display boundary and resize the + * cropping height to maintain the vertical resizing ratio. + */ + win->w.height = (fbuf->fmt.height - win->w.top) & ~1; + if (try_crop.height == 0) + try_crop.height = 2; + } + /* horizontal resizing */ + hresize = (1024 * crop->width) / win->w.width; + if (hresize > 4096) + hresize = 4096; + else if (hresize == 0) + hresize = 1; + win->w.width = ((1024 * try_crop.width) / hresize) & ~1; + if (win->w.width == 0) + win->w.width = 2; + if (win->w.width + win->w.left > fbuf->fmt.width) { + /* We made the preview window extend past the right side of the + * display, so clip it to the display boundary and resize the + * cropping width to maintain the horizontal resizing ratio. + */ + win->w.width = (fbuf->fmt.width - win->w.left) & ~1; + if (try_crop.width == 0) + try_crop.width = 2; + } + + /* Check for resizing constraints */ + if ((try_crop.height/win->w.height) >= 4) { + /* The maximum vertical downsizing ratio is 4:1 */ + try_crop.height = win->w.height * 4; + } + if ((try_crop.width/win->w.width) >= 4) { + /* The maximum horizontal downsizing ratio is 4:1 */ + try_crop.width = win->w.width * 4; + } + + /* update our cropping rectangle and we're done */ + *crop = try_crop; + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_new_crop); + +/* Given a new format in pix and fbuf, crop and win + * structures are initialized to default values. crop + * is initialized to the largest window size that will fit on the display. The + * crop window is centered in the image. win is initialized to + * the same size as crop and is centered on the display. + * All sizes and offsets are constrained to be even numbers. + */ +void omap_vout_new_format(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop, + struct v4l2_window *win) +{ + /* crop defines the preview source window in the image capture + * buffer + */ + omap_vout_default_crop(pix, fbuf, crop); + + /* win defines the preview target window on the display */ + win->w.width = crop->width; + win->w.height = crop->height; + win->w.left = ((fbuf->fmt.width - win->w.width) >> 1) & ~1; + win->w.top = ((fbuf->fmt.height - win->w.height) >> 1) & ~1; +} +EXPORT_SYMBOL_GPL(omap_vout_new_format); diff --git a/drivers/media/video/omap/omap_voutlib.h b/drivers/media/video/omap/omap_voutlib.h new file mode 100644 index 000000000000..8ef6e25b9e62 --- /dev/null +++ b/drivers/media/video/omap/omap_voutlib.h @@ -0,0 +1,34 @@ +/* + * drivers/media/video/omap/omap_voutlib.h + * + * Copyright (C) 2009 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef OMAP_VOUTLIB_H +#define OMAP_VOUTLIB_H + +extern void omap_vout_default_crop(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop); + +extern int omap_vout_new_crop(struct v4l2_pix_format *pix, + struct v4l2_rect *crop, struct v4l2_window *win, + struct v4l2_framebuffer *fbuf, + const struct v4l2_rect *new_crop); + +extern int omap_vout_try_window(struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win); + +extern int omap_vout_new_window(struct v4l2_rect *crop, + struct v4l2_window *win, struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win); + +extern void omap_vout_new_format(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop, + struct v4l2_window *win); +#endif /* #ifndef OMAP_LIB_H */ + diff --git a/drivers/media/video/tiler/Kconfig b/drivers/media/video/tiler/Kconfig new file mode 100644 index 000000000000..926349dbe9a9 --- /dev/null +++ b/drivers/media/video/tiler/Kconfig @@ -0,0 +1,18 @@ + + +menuconfig dmm_tiler + bool "dmm_tiler" + default y + +if dmm_tiler + +config DMM_TILER + tristate "dmm_tiler" + default y + help + dmm tiler driver +endif + + + + diff --git a/drivers/media/video/tiler/Makefile b/drivers/media/video/tiler/Makefile new file mode 100644 index 000000000000..ada0d3125755 --- /dev/null +++ b/drivers/media/video/tiler/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_DMM_TILER) += tiler.o +tiler-objs = dmm.o dmm_hl_drv.o dmm_2d_alloc.o dmm_ll_drv.o dmm_page_rep.o + diff --git a/drivers/media/video/tiler/dmm.c b/drivers/media/video/tiler/dmm.c new file mode 100755 index 000000000000..0dbf6db289ae --- /dev/null +++ b/drivers/media/video/tiler/dmm.c @@ -0,0 +1,1508 @@ +/* + * dmm.c + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/cdev.h> /* struct cdev */ +#include <linux/kdev_t.h> /* MKDEV() */ +#include <linux/fs.h> /* register_chrdev_region() */ +#include <linux/device.h> /* struct class */ +#include <linux/platform_device.h> /* platform_device() */ +#include <linux/err.h> /* IS_ERR() */ +#include <linux/errno.h> +#include <linux/io.h> /* ioremap() */ +#include <linux/mm.h> /* mmaping */ +#include <linux/mm_types.h> +#include <linux/sched.h> /* current->mm */ +#include <linux/uaccess.h> /* copy_to_user() */ +#include "tiler.h" +#include "dmm_drv.h" +#include "dmm_prv.h" +#include "dmm_def.h" + +#define __NEWCODE__ +#ifdef __NEWCODE__ +struct pat_area { + int x0:8; + int y0:8; + int x1:8; + int y1:8; +}; + +struct pat_ctrl { + int start:4; + int direction:4; + int lut_id:8; + int sync:12; + int initiator:4; +}; + +struct pat_desc { + struct pat_desc *next; + struct pat_area area; + struct pat_ctrl ctrl; + unsigned long data; +}; + +void *dmm_base; + +static void pat_area_set(struct pat_area *area, char id); +static void pat_data_set(unsigned long data, char id); +static void pat_ctrl_set(struct pat_ctrl *ctrl, char id); +static void pat_desc_set(struct pat_desc *desc, char id); +static void hwinfo_get(); +static void pat_view_set(); +static void pat_view_map_set(); +static void pat_view_map_base_set(); +static void tiler_or_set(); + +static void tiler_or_set() /* (struct tiler_or *or, char id) */ +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set TILER_OR__0 register */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)TILER_OR__0); + reg_val = __raw_readl(reg); + regdump("TILER_OR__0", reg_val); + + bit_field = BITFIELD(31, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)0) << field_pos) & bit_field); + __raw_writel(0x88888888, reg); /* __raw_writel(new_val, reg); */ + + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)TILER_OR__0); + reg_val = __raw_readl(reg); + regdump("TILER_OR__0", reg_val); + + /* set TILER_OR__1 register */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)TILER_OR__1); + reg_val = __raw_readl(reg); + regdump("TILER_OR__1", reg_val); + + bit_field = BITFIELD(31, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)0) << field_pos) & bit_field); + __raw_writel(0x88888888, reg); /* __raw_writel(new_val, reg); */ + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)TILER_OR__1); + reg_val = __raw_readl(reg); + regdump("TILER_OR__1", reg_val); +} +static void pat_view_set() /* (struct pat_view *view, char id) */ +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_VIEW__0 register */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW__0); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW__0", reg_val); + + bit_field = BITFIELD(31, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)0) << field_pos) & bit_field); + __raw_writel(0x88888888, reg); /* __raw_writel(new_val, reg); */ + + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW__0); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW__0", reg_val); + + /* set PAT_VIEW__1 register */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW__1); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW__1", reg_val); + + bit_field = BITFIELD(31, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)0) << field_pos) & bit_field); + __raw_writel(0x88888888, reg); /* __raw_writel(new_val, reg); */ + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW__1); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW__1", reg_val); +} +static void pat_view_map_set() /* (struct pat_view_map *map, char id) */ +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_VIEW_MAP__0 register */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW_MAP__0); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW_MAP__0", reg_val); + + bit_field = BITFIELD(31, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)0) << field_pos) & bit_field); + __raw_writel(0x80808080, reg); /* __raw_writel(new_val, reg); */ + + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW_MAP__0); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW_MAP__0", reg_val); +} + +static void pat_view_map_base_set()/*(struct pat_view_map_base *base, char id)*/ +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_VIEW_MAP_BASE register */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW_MAP_BASE); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW_MAP_BASE", reg_val); + + bit_field = BITFIELD(31, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)0) << field_pos) & bit_field); + __raw_writel(0x80000000, reg); /* __raw_writel(new_val, reg); */ + + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_VIEW_MAP_BASE); + reg_val = __raw_readl(reg); + regdump("PAT_VIEW_MAP_BASE", reg_val); +} + +static void hwinfo_get() +{ + void __iomem *reg = NULL; + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)DMM_REVISION); + regdump("DMM_REVISION", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)DMM_HWINFO); + regdump("DMM_HWINFO", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)LISA_HWINFO); + regdump("LISA_HWINFO", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)TILER_HWINFO); + regdump("TILER_HWINFO", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_HWINFO); + regdump("PAT_HWINFO", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_GEOMETRY); + regdump("PAT_GEOMETRY", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_STATUS__0); + regdump("PAT_STATUS__0", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_STATUS__1); + regdump("PAT_STATUS__1", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_STATUS__2); + regdump("PAT_STATUS__2", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_STATUS__3); + regdump("PAT_STATUS__3", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PEG_HWINFO); + regdump("PEG_HWINFO", __raw_readl(reg)); +} +static void pat_desc_set(struct pat_desc *desc, char id) +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* write to pat registers */ + /* opt to individually set each reg, so set PAT_DESC register to NULL */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_DESCR__0); + reg_val = __raw_readl(reg); + regdump("PAT_DESCR__0", reg_val); + + bit_field = BITFIELD(31, 4); + field_pos = 4; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)desc) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_DESCR__0); + reg_val = __raw_readl(reg); + regdump("PAT_DESCR__0", reg_val); +} +static void pat_ctrl_set(struct pat_ctrl *ctrl, char id) +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_CTRL register */ + /* TODO: casting as unsigned long */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_CTRL__0); + reg_val = __raw_readl(reg); + regdump("PAT_CTRL__0", reg_val); + + bit_field = BITFIELD(31, 28); + field_pos = 28; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->initiator) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(16, 16); + field_pos = 16; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->sync) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(9, 8); + field_pos = 8; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->lut_id) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(6, 4); + field_pos = 4; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->direction) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(0, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->start) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_CTRL__0); + reg_val = __raw_readl(reg); + regdump("PAT_CTRL__0", reg_val); +} +static void pat_data_set(unsigned long data, char id) +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_DATA register */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_DATA__0); + reg_val = __raw_readl(reg); + regdump("PAT_DATA__0", reg_val); + + bit_field = BITFIELD(31, 4); + field_pos = 4; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)data >> 4) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_DATA__0); + reg_val = __raw_readl(reg); + regdump("PAT_DATA__0", reg_val); +} +static void pat_area_set(struct pat_area *area, char id) +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_AREA register */ + /* TODO: changed casting from char to unsigned long */ + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_AREA__0); + reg_val = __raw_readl(reg); + regdump("PAT_AREA__0", reg_val); + + bit_field = BITFIELD(30, 24); + field_pos = 24; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)area->y1) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(23, 16); + field_pos = 16; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)area->x1) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(14, 8); + field_pos = 8; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)area->y0) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(7, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)area->x0) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)PAT_AREA__0); + reg_val = __raw_readl(reg); + regdump("PAT_AREA__0", reg_val); +} +#endif /* end: #ifdef __NEWCODE__ */ + +#define DMM_MAJOR 0 +#define DMM_MINOR 0 +#define DMM_IO_BASE_ADDR 0x4e000000 + +unsigned long *dmm_virt_base_addr; +static struct dmmInstanceCtxT *ctxptr; + +struct node { + struct tiler_buf_info *ptr; + unsigned long reserved; + struct node *nextnode; +}; + +static struct node *lsthd; +static int id; + +static int dmm_open(struct inode *i, struct file *f); +static int dmm_release(struct inode *i, struct file *f); +static int dmm_ioctl(struct inode *i, struct file *f, + unsigned int c, unsigned long a); +static int dmm_mmap(struct file *f, struct vm_area_struct *v); +static void dmm_vma_open(struct vm_area_struct *vma); +static void dmm_vma_close(struct vm_area_struct *vma); +static void dmm_config(void); +static int removenode(struct node *listhead, int offset); +static int tiler_destroy_buf_info_list(struct node *listhead); +static int addnode(struct node *listhead, struct tiler_buf_info *p); +static int createlist(struct node **listhead); +static int tiler_find_buf(unsigned long sysptr, struct tiler_block_info *blk); + +static int +tiler_get_buf_info(struct node *listhead, struct tiler_buf_info **pp, int ofst); + +static int dmm_major; +static int dmm_minor; + +static struct vm_operations_struct dmm_remap_vm_ops = { + .open = dmm_vma_open, + .close = dmm_vma_close, +}; + +struct dmm_dev { + struct cdev cdev; +}; + +static struct dmm_dev *dmm_device; +static struct class *dmmdev_class; +static const struct file_operations dmm_fops = { + .open = dmm_open, + .ioctl = dmm_ioctl, + .release = dmm_release, + .mmap = dmm_mmap, +}; + +static struct platform_driver tiler_driver_ldm = { + .driver = { + .owner = THIS_MODULE, + .name = "tiler", + }, + .probe = NULL, + .shutdown = NULL, + .remove = NULL, + }; + +static int +__init dmm_init(void) +{ + dev_t dev = 0; + int retval = -1; + int error = -1; + struct device *device = NULL; + + if (dmm_major) { + dev = MKDEV(dmm_major, dmm_minor); + retval = register_chrdev_region(dev, 1, "tiler"); + } else { + retval = alloc_chrdev_region(&dev, dmm_minor, 1, "tiler"); + dmm_major = MAJOR(dev); + } + + dmm_device = kmalloc(sizeof(struct dmm_dev), GFP_KERNEL); + if (!dmm_device) { + retval = -ENOMEM; + unregister_chrdev_region(dev, 1); + printk(KERN_ERR "kmalloc():failed\n"); + goto EXIT; + } + memset(dmm_device, 0x0, sizeof(struct dmm_dev)); + cdev_init(&dmm_device->cdev, &dmm_fops); + dmm_device->cdev.owner = THIS_MODULE; + dmm_device->cdev.ops = &dmm_fops; + + retval = cdev_add(&dmm_device->cdev, dev, 1); + if (retval) + printk(KERN_ERR "cdev_add():failed\n"); + + dmmdev_class = class_create(THIS_MODULE, "tiler"); + + if (IS_ERR(dmmdev_class)) { + printk(KERN_ERR "class_create():failed\n"); + goto EXIT; + } + + device = device_create(dmmdev_class, NULL, dev, NULL, "tiler"); + if (device == NULL) + printk(KERN_ERR "device_create() fail\n"); + + retval = platform_driver_register(&tiler_driver_ldm); + + /* map the TILER i/o physical addr to krnl virt addr */ + dmm_virt_base_addr = (unsigned long *)ioremap(DMM_IO_BASE_ADDR, 2048); + + ctxptr = kmalloc(sizeof(struct dmmInstanceCtxT), GFP_KERNEL); + memset(ctxptr, 0x0, sizeof(struct dmmInstanceCtxT)); + error = dmm_instance_init((void *)ctxptr, TILER_WIDTH, + TILER_HEIGHT, NULL, NULL); + if (error == 1) { + retval = 0; + } else { + kfree(ctxptr); + return retval; + } + +#ifndef __NEWCODE__ + /* config LISA/PAT */ + dmm_config(); +#else + void __iomem *reg = NULL; + dmm_base = ioremap(DMM_BASE, 0x1000); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)LISA_MAP__0); + regdump("LISA_MAP__0", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_base | (unsigned long)LISA_MAP__1); + regdump("LISA_MAP__1", __raw_readl(reg)); + + pat_view_set(); + pat_view_map_set(); + pat_view_map_base_set(); + tiler_or_set(); +#endif + + /* create buffer info list */ + createlist(&lsthd); + id = 0xda7a000; + +EXIT: + return retval; +} + +static void +__exit dmm_exit(void) +{ + kfree(ctxptr); + tiler_destroy_buf_info_list(lsthd); + + platform_driver_unregister(&tiler_driver_ldm); + + cdev_del(&dmm_device->cdev); + kfree(dmm_device); + + device_destroy(dmmdev_class, MKDEV(dmm_major, dmm_minor)); + class_destroy(dmmdev_class); + + iounmap(dmm_virt_base_addr); +} + +static int +dmm_open(struct inode *ip, struct file *filp) +{ + int retval = -1; + retval = 0; + return retval; +} + +static int +dmm_release(struct inode *ip, struct file *filp) +{ + int retval = -1; + retval = 0; + return retval; +} + +static int +dmm_ioctl(struct inode *ip, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct tiler_buf_info *bufinfo = NULL; + void *ptr = NULL; + int retval = -1; + int error = -1; + pgd_t *pgd = NULL; + pmd_t *pmd = NULL; + pte_t *ptep = NULL, pte = 0x0; + unsigned long bytes = 0x1; + int offset = 0x0; + unsigned long ssptr = 0x0; + + struct tiler_buf_info buf_info = {0}; + struct tiler_block_info block_info = {0}; + + switch (cmd) { + case TILIOC_OPEN: + debug(__LINE__); + retval = 0; + break; + case TILIOC_CLOSE: + debug(__LINE__); + retval = 0; + break; + case TILIOC_GBUF: + debug(__LINE__); + + bytes = copy_from_user((void *)(&block_info), + (const void *)arg, sizeof(struct tiler_block_info)); + if (bytes != 0) + return retval; + + if (block_info.fmt == TILFMT_PAGE) { + error = tiler_alloc_buf(block_info.fmt, + block_info.dim.len, + 1, + &ptr); + } else { + error = tiler_alloc_buf(block_info.fmt, + block_info.dim.area.width, + block_info.dim.area.height, + &ptr); + } + + if (error != 0) + return retval; + + block_info.ssptr = (unsigned long)ptr; + bytes = copy_to_user((void *)arg, (const void *)(&block_info), + sizeof(struct tiler_block_info)); + if (bytes != 0) + return retval; + + retval = 0; + break; + case TILIOC_FBUF: + debug(__LINE__); + + bytes = copy_from_user((void *)(&block_info), + (const void *)arg, sizeof(struct tiler_block_info)); + if (bytes != 0) + return retval; + + error = tiler_free_buf(block_info.ssptr); + if (error == 0) + retval = 0; + break; + case TILIOC_GSSP: + debug(__LINE__); + + pgd = pgd_offset(current->mm, arg); + if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { + pmd = pmd_offset(pgd, arg); + if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { + ptep = pte_offset_map(pmd, arg); + if (ptep) { + pte = *ptep; + if (pte_present(pte)) { + ssptr = (pte & PAGE_MASK) + | (~PAGE_MASK & arg); + retval = ssptr; + break; + } + } + } + } + retval = 0x0; /* va not in page table */ + break; + case TILIOC_MBUF: + debug(__LINE__); + retval = 0; + break; + case TILIOC_QBUF: + debug(__LINE__); + + bytes = copy_from_user((void *)(&buf_info), + (const void *)arg, sizeof(struct tiler_buf_info)); + if (bytes != 0) + return retval; + + offset = buf_info.offset; + error = tiler_get_buf_info(lsthd, &bufinfo, offset); + if (error != 0) + return retval; + + bytes = copy_to_user((void *)arg, + (const void *)bufinfo, sizeof(struct tiler_buf_info)); + if (bytes != 0) + return retval; + + retval = 0; + break; + case TILIOC_RBUF: + debug(__LINE__); + + bufinfo = kmalloc(sizeof(struct tiler_buf_info), GFP_KERNEL); + memset(bufinfo, 0x0, sizeof(struct tiler_buf_info)); + bytes = copy_from_user((void *)bufinfo, + (const void *)arg, sizeof(struct tiler_buf_info)); + if (bytes != 0) + return retval; + + bufinfo->offset = id; + id += 0x1000; + + bytes = copy_to_user((void *)arg, + (const void *)bufinfo, sizeof(struct tiler_buf_info)); + if (bytes != 0) + return retval; + + error = addnode(lsthd, bufinfo); + if (error != 0) + return retval; + retval = 0; + break; + case TILIOC_URBUF: + debug(__LINE__); + + bytes = copy_from_user((void *)(&buf_info), + (const void *)arg, sizeof(struct tiler_buf_info)); + if (bytes != 0) + return retval; + + offset = buf_info.offset; + error = removenode(lsthd, offset); + if (error != 0) + return retval; + + retval = 0; + break; + case TILIOC_QUERY_BLK: + debug(__LINE__); + + bytes = copy_from_user((void *)(&block_info), + (const void *)arg, sizeof(struct tiler_block_info)); + if (bytes != 0) + return retval; + + error = tiler_find_buf(block_info.ssptr, &block_info); + if (error != 0) + return retval; + + bytes = copy_to_user((void *)arg, + (const void *)(&block_info), + sizeof(struct tiler_block_info)); + if (bytes != 0) + return retval; + + retval = 0; + break; + } + return retval; +} + +static int +dmm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret = -1; + struct tiler_buf_info *b = NULL; + int i = 0, j = 0, k = 0, m = 0, p = 0; + int bpp = 1; + + tilerdump(__LINE__); + ret = tiler_get_buf_info(lsthd, &b, vma->vm_pgoff << PAGE_SHIFT); + if (ret != 0) { + printk(KERN_ERR "%s::%s():%d: tiler_get_buf_info failed\n", + __FILE__, __func__, __LINE__); + return 0x0; + } + + for (i = 0; i < b->num_blocks; i++) { + if (b->blocks[i].fmt >= TILFMT_8BIT && + b->blocks[i].fmt <= TILFMT_32BIT) { + /* get line width */ + bpp = (b->blocks[i].fmt == TILFMT_8BIT ? 1 : + b->blocks[i].fmt == TILFMT_16BIT ? 2 : 4); + p = (b->blocks[i].dim.area.width * bpp + + TILER_PAGESIZE - 1) & ~(TILER_PAGESIZE - 1); + + for (j = 0; j < b->blocks[i].dim.area.height; j++) { + /* map each page of the line */ + if (0) + printk(KERN_NOTICE + "%s::%s():%d: vm_start+%d = 0x%lx," + "blk[%d].ssptr+%d = 0x%lx, w=0x%x\n", + __FILE__, __func__, __LINE__, + k, vma->vm_start + k, i, m, + (b->blocks[i].ssptr + m), p); + vma->vm_pgoff = + (b->blocks[i].ssptr + m) >> PAGE_SHIFT; + if (remap_pfn_range(vma, vma->vm_start + k, + (b->blocks[i].ssptr + m) >> PAGE_SHIFT, + p, vma->vm_page_prot)) + return -EAGAIN; + k += p; + if (b->blocks[i].fmt == TILFMT_8BIT) + m += 64*TILER_WIDTH; + else + m += 2*64*TILER_WIDTH; + } + m = 0; + } else if (b->blocks[i].fmt == TILFMT_PAGE) { + vma->vm_pgoff = (b->blocks[i].ssptr) >> PAGE_SHIFT; + p = (b->blocks[i].dim.len + TILER_PAGESIZE - 1) & + ~(TILER_PAGESIZE - 1); + if (0) + printk(KERN_NOTICE "%s::%s():%d:" + "vm_start = 0x%lx, blk[%d].ssptr = 0x%lx," + "w=0x%x\n", + __FILE__, __func__, __LINE__, + vma->vm_start + k, i, (b->blocks[i].ssptr), p); + if (remap_pfn_range(vma, vma->vm_start + k, + (b->blocks[i].ssptr) >> PAGE_SHIFT, p, + vma->vm_page_prot)) + return -EAGAIN;; + k += p; + } + } + vma->vm_ops = &dmm_remap_vm_ops; + dmm_vma_open(vma); + return 0; +} + +void +dmm_vma_open(struct vm_area_struct *vma) +{ + if (0) + printk(KERN_NOTICE "dmm VMA open, virt %lx, phys %lx\n", + vma->vm_start, vma->vm_pgoff << PAGE_SHIFT); +} + +void +dmm_vma_close(struct vm_area_struct *vma) +{ + /* printk(KERN_NOTICE "dmm VMA close.\n"); */ +} + +static void +dmm_config(void) +{ + struct dmmPATIrqConfigLstT patEvents; + struct dmmPATEngineConfigLstT patEngineConf[2]; + struct dmmPATViewConfigLstT patViewConf[16]; + struct dmmPATViewMapConfigLstT patViewMapConf[4]; + struct dmmLISAConfigLstT lisaMemMapConf[1]; + struct dmmTILERConfigLstT dmmTilerAliasView[16]; + unsigned int retCode = 0x0; + unsigned int i = 0x0; + + tilerdump(__LINE__); + /* clear irq event registers */ + patEvents.nextConf = NULL; + patEvents.irqConf.clrEvents = 1; + patEvents.irqConf.irqEvnts.irqAreaSelect = 0; + patEvents.irqConf.irqEvnts.lutMiss = 1; + patEvents.irqConf.irqEvnts.updData = 1; + patEvents.irqConf.irqEvnts.updCtrl = 1; + patEvents.irqConf.irqEvnts.updArea = 1; + patEvents.irqConf.irqEvnts.invData = 1; + patEvents.irqConf.irqEvnts.invDsc = 1; + patEvents.irqConf.irqEvnts.fillLst = 1; + patEvents.irqConf.irqEvnts.fillDsc = 1; + + if (dmm_pat_irq_config_set((struct dmmPATIrqConfigLstT *)&patEvents) != + DMM_NO_ERROR) { + printk(KERN_ERR "%s::%s():%d: ERROR!\n", + __FILE__, __func__, __LINE__); + retCode = 0x2; + } + + /* configure pat engines */ + patEngineConf[0].nextConf = &patEngineConf[1]; + patEngineConf[0].engineConf.dmmPatEngineSel = 0; + patEngineConf[0].engineConf.engineMode = NORMAL_MODE; + + patEngineConf[1].nextConf = NULL; + patEngineConf[1].engineConf.dmmPatEngineSel = 1; + patEngineConf[1].engineConf.engineMode = NORMAL_MODE; + + if (dmm_module_config(NULL, NULL, NULL, + (struct dmmPATEngineConfigLstT *)patEngineConf, + NULL, NULL, NULL) != + DMM_NO_ERROR) { + printk(KERN_ERR "%s::%s():%d: ERROR!\n", + __FILE__, __func__, __LINE__); + retCode = 0x2; + } + + /* configure LISA map 0 */ + lisaMemMapConf[0].nextConf = NULL; + lisaMemMapConf[0].mapConf.lisaMemMapIndx = 0; + lisaMemMapConf[0].mapConf.sysAddr = 0x80; + lisaMemMapConf[0].mapConf.sysSize = 0x5; + lisaMemMapConf[0].mapConf.sdrcIntl = 0x0 ; + lisaMemMapConf[0].mapConf.sdrcAddrspc = 0x1; + lisaMemMapConf[0].mapConf.sdrcMap = 0x1; + lisaMemMapConf[0].mapConf.sdrcAddr = 0x00; + + if (dmm_module_config(NULL, NULL, + (struct dmmLISAConfigLstT *)lisaMemMapConf, + NULL, NULL, NULL, NULL) != DMM_NO_ERROR) { + printk(KERN_ERR "%s::%s():%d: ERROR!\n", + __FILE__, __func__, __LINE__); + retCode = 0x2; + } + + /* */ + for (i = 0; i < 16; i++) { + patViewConf[i].nextConf = &patViewConf[i+1]; + patViewConf[i].aliasViewConf.initiatorId = i; + patViewConf[i].aliasViewConf.viewIndex = 0; + } + patViewConf[15].nextConf = NULL; + + if (dmm_module_config(NULL, NULL, NULL, NULL, + (struct dmmPATViewConfigLstT *)&patViewConf, + NULL, NULL) != + DMM_NO_ERROR) { + printk(KERN_ERR "%s::%s():%d: ERROR!\n", + __FILE__, __func__, __LINE__); + retCode = 0x2; + } + + /* configure pat view map 0 */ + patViewMapConf[0].nextConf = &patViewMapConf[1]; + patViewMapConf[0].viewConf.patViewMapIndx = 0; + patViewMapConf[0].viewConf.memoryAccessMode = MODE_8_BIT; + patViewMapConf[0].viewConf.contX = 0; + patViewMapConf[0].viewConf.transType = INDIRECT; + + /* Setting only the MSB seems to have no effect whatsoever */ + patViewMapConf[0].viewConf.dmmPATViewBase = 0xFFFFFFFF; + + patViewMapConf[1].nextConf = &patViewMapConf[2]; + patViewMapConf[1].viewConf.patViewMapIndx = 0; + patViewMapConf[1].viewConf.memoryAccessMode = MODE_16_BIT; + patViewMapConf[1].viewConf.contX = 0; + patViewMapConf[1].viewConf.transType = INDIRECT; + + /* Setting only the MSB seems to have no effect whatsoever */ + patViewMapConf[1].viewConf.dmmPATViewBase = 0xFFFFFFFF; + + patViewMapConf[2].nextConf = &patViewMapConf[3]; + patViewMapConf[2].viewConf.patViewMapIndx = 0; + patViewMapConf[2].viewConf.memoryAccessMode = MODE_32_BIT; + patViewMapConf[2].viewConf.contX = 0; + patViewMapConf[2].viewConf.transType = INDIRECT; + + /* Setting only the MSB seems to have no effect whatsoever */ + patViewMapConf[2].viewConf.dmmPATViewBase = 0xFFFFFFFF; + + patViewMapConf[3].nextConf = NULL; + patViewMapConf[3].viewConf.patViewMapIndx = 0; + patViewMapConf[3].viewConf.memoryAccessMode = MODE_PAGE; + patViewMapConf[3].viewConf.contX = 0; + patViewMapConf[3].viewConf.transType = INDIRECT; + + /* Setting only the MSB seems to have no effect whatsoever */ + patViewMapConf[3].viewConf.dmmPATViewBase = 0xFFFFFFFF; + + if (dmm_module_config(NULL, NULL, NULL, NULL, NULL, + (struct dmmPATViewMapConfigLstT *)&patViewMapConf, + NULL) != DMM_NO_ERROR) { + printk(KERN_ERR "%s::%s():%d: ERROR!\n", + __FILE__, __func__, __LINE__); + retCode = 0x2; + } + + /* set all initiators to zero degree natural */ + for (i = 0; i < 16; i++) { + dmmTilerAliasView[i].nextConf = &dmmTilerAliasView[i + 1]; + dmmTilerAliasView[i].aliasConf.initiatorId = i; + dmmTilerAliasView[i].aliasConf.orient.dmm90Rotate = 0; + dmmTilerAliasView[i].aliasConf.orient.dmmXInvert = 0; + dmmTilerAliasView[i].aliasConf.orient.dmmYInvert = 0; + } + dmmTilerAliasView[15].nextConf = NULL; + + if (dmm_module_config((struct dmmTILERConfigLstT *)dmmTilerAliasView, + NULL, NULL, NULL, NULL, NULL, NULL) != + DMM_NO_ERROR) { + printk(KERN_ERR "%s::%s():%d: ERROR!\n", + __FILE__, __func__, __LINE__); + retCode = 0x2; + } + tilerdump(__LINE__); +} + +int +tiler_alloc_buf(enum tiler_fmt fmt, + unsigned long width, + unsigned long height, + void **sysptr) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + struct dmmTILERContCtxT *dmmTilerCtx = + &((struct dmmInstanceCtxT *)ctxptr)->dmmTilerCtx; + struct dmmTILERContPageAreaT *bufferMappedZone; + void *custmPagesPtr = NULL; + enum dmmMemoryAccessT contMod; + + tilerdump(__LINE__); + + if (fmt == TILFMT_8BIT) + contMod = MODE_8_BIT; + else if (fmt == TILFMT_16BIT) + contMod = MODE_16_BIT; + else if (fmt == TILFMT_32BIT) + contMod = MODE_32_BIT; + else if (fmt == TILFMT_PAGE) + contMod = MODE_PAGE; + else + return DMM_SYS_ERROR; + + if (eCode == DMM_NO_ERROR) { + eCode = dmm_tiler_container_map_area(dmmTilerCtx, + width, + height, + contMod, + sysptr, + &bufferMappedZone); + } + + if (eCode == DMM_NO_ERROR) { + bufferMappedZone->xPageOfst = 0; + bufferMappedZone->yPageOfst = 0; + bufferMappedZone->xPageCount = + bufferMappedZone->x1 - bufferMappedZone->x0 + 1; + bufferMappedZone->yPageCount = + bufferMappedZone->y1 - bufferMappedZone->y0 + 1; + /* printk(KERN_ERR "x(%u-%u=%u>%u) y(%u-%u=%u>%u)\n", + bufferMappedZone->x0, bufferMappedZone->x1, + bufferMappedZone->xPageCount, bufferMappedZone->xPageOfst, + bufferMappedZone->y0, bufferMappedZone->y1, + bufferMappedZone->yPageCount, bufferMappedZone->yPageOfst); */ + + eCode = dmm_pat_phy2virt_mapping(bufferMappedZone, + custmPagesPtr); + } + + if (eCode != DMM_NO_ERROR) + *sysptr = NULL; + + return eCode; +} +EXPORT_SYMBOL(tiler_alloc_buf); + +/* :TODO: Currently we do not track enough information from alloc to get back + the actual width and height of the container, so we must make a guess. We + do not even have enough information to get the virtual stride of the buffer, + which is the real reason for this ioctl */ +int +tiler_find_buf(unsigned long sysptr, struct tiler_block_info *blk) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + struct dmmTILERContCtxT *dmmTilerCtx = + &((struct dmmInstanceCtxT *)ctxptr)->dmmTilerCtx; + + struct dmmTILERContPageAreaT *area; + + tilerdump(__LINE__); + + area = dmm_tiler_get_area_from_sysptr(dmmTilerCtx, + (void *)(DMM_ALIAS_VIEW_CLEAR & sysptr)); + blk->ptr = NULL; + if (area != NULL) { + int accMode = DMM_GET_ACC_MODE(sysptr); + blk->fmt = (accMode + 1); + if (blk->fmt == TILFMT_PAGE) { + blk->dim.len = area->xPageCount * + area->yPageCount * TILER_PAGESIZE; + blk->stride = 0; + blk->ssptr = + (unsigned long) + DMM_COMPOSE_TILER_ALIAS_PTR( + ((area->x0 | (area->y0 << 8)) << 12), accMode); + } else { + blk->stride = blk->dim.area.width = + area->xPageCount * TILER_BLOCK_WIDTH; + blk->dim.area.height = + area->yPageCount * TILER_BLOCK_HEIGHT; + if (blk->fmt == TILFMT_8BIT) { + blk->ssptr = + (unsigned long) + DMM_COMPOSE_TILER_ALIAS_PTR( + ((area->x0 << 6) | (area->y0 << 20)), + accMode); + } else { + blk->ssptr = + (unsigned long) + DMM_COMPOSE_TILER_ALIAS_PTR( + ((area->x0 << 7) | (area->y0 << 20)), + accMode); + blk->stride <<= 1; + blk->dim.area.height >>= 1; + if (blk->fmt == TILFMT_32BIT) + blk->dim.area.width >>= 1; + } + blk->stride = (blk->stride + TILER_PAGESIZE - 1) & + ~(TILER_PAGESIZE - 1); + } + } else { + blk->fmt = TILFMT_INVALID; + blk->dim.len = blk->stride = blk->ssptr = 0; + eCode = DMM_WRONG_PARAM; + } + + return eCode; +} + +int +tiler_free_buf(unsigned long sysptr) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + struct dmmTILERContCtxT *dmmTilerCtx = + &((struct dmmInstanceCtxT *)ctxptr)->dmmTilerCtx; + + struct dmmTILERContPageAreaT *areaToFree; + + tilerdump(__LINE__); + + /* if (aliasViewPtr) { + allocedPtr = (void *)((unsigned long)allocedPtr & + DMM_ALIAS_VIEW_CLEAR); + } */ + + areaToFree = dmm_tiler_get_area_from_sysptr(dmmTilerCtx, + (void *)(DMM_ALIAS_VIEW_CLEAR & sysptr)); + if (areaToFree != NULL) + eCode = dmm_tiler_container_unmap_area(dmmTilerCtx, areaToFree); + else + eCode = DMM_SYS_ERROR; + + return eCode; +} +EXPORT_SYMBOL(tiler_free_buf); + + + +#define DMM_SHIFT_PER_X_8 0 +#define DMM_SHIFT_PER_Y_8 0 +#define DMM_SHIFT_PER_X_16 0 +#define DMM_SHIFT_PER_Y_16 1 +#define DMM_SHIFT_PER_X_32 1 +#define DMM_SHIFT_PER_Y_32 1 +#define DMM_SHIFT_PER_X_PAGE 6 +#define DMM_SHIFT_PER_Y_PAGE 6 + +#define DMM_TILER_THE(NAME) (1 << DMM_TILER_##NAME##_BITS) +#define DMM_TILER_THE_(N, NAME) (1 << DMM_TILER_##NAME##_BITS_(N)) + +#define DMM_TILER_CONT_WIDTH_BITS 14 +#define DMM_TILER_CONT_HEIGHT_BITS 13 + +#define DMM_SHIFT_PER_P_(N) (DMM_SHIFT_PER_X_##N + DMM_SHIFT_PER_Y_##N) + +#define DMM_TILER_CONT_HEIGHT_BITS_(N) \ + (DMM_TILER_CONT_HEIGHT_BITS - DMM_SHIFT_PER_Y_##N) +#define DMM_TILER_CONT_WIDTH_BITS_(N) \ + (DMM_TILER_CONT_WIDTH_BITS - DMM_SHIFT_PER_X_##N) + +#define DMM_TILER_MASK(bits) ((1 << (bits)) - 1) + +#define DMM_TILER_GET_OFFSET_(N, var) \ + ((((unsigned long) var) & DMM_TILER_MASK(DMM_TILER_CONT_WIDTH_BITS + \ + DMM_TILER_CONT_HEIGHT_BITS)) >> DMM_SHIFT_PER_P_(N)) + +#define DMM_TILER_GET_0_X_(N, var) \ + (DMM_TILER_GET_OFFSET_(N, var) & \ + DMM_TILER_MASK(DMM_TILER_CONT_WIDTH_BITS_(N))) +#define DMM_TILER_GET_0_Y_(N, var) \ + (DMM_TILER_GET_OFFSET_(N, var) >> DMM_TILER_CONT_WIDTH_BITS_(N)) +#define DMM_TILER_GET_90_X_(N, var) \ + (DMM_TILER_GET_OFFSET_(N, var) & \ + DMM_TILER_MASK(DMM_TILER_CONT_HEIGHT_BITS_(N))) +#define DMM_TILER_GET_90_Y_(N, var) \ + (DMM_TILER_GET_OFFSET_(N, var) >> DMM_TILER_CONT_HEIGHT_BITS_(N)) + +void tiler_get_natural_xy(unsigned long tsptr, unsigned long *x, + unsigned long *y) +{ + unsigned long x_bits, y_bits, offset; + + switch (DMM_GET_ACC_MODE(tsptr)) { + case MODE_8_BIT: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(8); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(8); + offset = DMM_TILER_GET_OFFSET_(8, tsptr); + break; + case MODE_16_BIT: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(16); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(16); + offset = DMM_TILER_GET_OFFSET_(16, tsptr); + break; + case MODE_32_BIT: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(32); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(32); + offset = DMM_TILER_GET_OFFSET_(32, tsptr); + break; + case MODE_PAGE: + default: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(PAGE); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(PAGE); + offset = DMM_TILER_GET_OFFSET_(PAGE, tsptr); + break; + } + + if (DMM_GET_ROTATED(tsptr)) { + *x = offset >> y_bits; + *y = offset & DMM_TILER_MASK(y_bits); + } else { + *x = offset & DMM_TILER_MASK(x_bits); + *y = offset >> x_bits; + } + + if (DMM_GET_X_INVERTED(tsptr)) + *x ^= DMM_TILER_MASK(x_bits); + if (DMM_GET_Y_INVERTED(tsptr)) + *y ^= DMM_TILER_MASK(y_bits); +} + +unsigned long tiler_get_address(struct dmmViewOrientT orient, + enum dmmMemoryAccessT accessModeM, + unsigned long x, unsigned long y) +{ + unsigned long x_bits, y_bits, tmp, x_mask, y_mask, alignment; + + switch (accessModeM) { + case MODE_8_BIT: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(8); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(8); + alignment = DMM_SHIFT_PER_P_(8); + break; + case MODE_16_BIT: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(16); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(16); + alignment = DMM_SHIFT_PER_P_(16); + break; + case MODE_32_BIT: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(32); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(32); + alignment = DMM_SHIFT_PER_P_(32); + break; + case MODE_PAGE: + default: + x_bits = DMM_TILER_CONT_WIDTH_BITS_(PAGE); + y_bits = DMM_TILER_CONT_HEIGHT_BITS_(PAGE); + alignment = DMM_SHIFT_PER_P_(PAGE); + break; + } + + x_mask = DMM_TILER_MASK(x_bits); + y_mask = DMM_TILER_MASK(y_bits); + if (x < 0 || x > x_mask || y < 0 || y > y_mask) + return 0; + + if (orient.dmmXInvert) + x ^= x_mask; + if (orient.dmmYInvert) + y ^= y_mask; + + if (orient.dmm90Rotate) + tmp = ((x << y_bits) + y); + else + tmp = ((y << x_bits) + x); + + return (unsigned long) + DMM_COMPOSE_TILER_PTR((tmp << alignment), + (orient.dmm90Rotate ? 1 : 0), + (orient.dmmYInvert ? 1 : 0), + (orient.dmmXInvert ? 1 : 0), + accessModeM); +} + +unsigned long +tiler_reorient_addr(unsigned long tsptr, + struct dmmViewOrientT orient) +{ + unsigned long x, y; + + tilerdump(__LINE__); + + tiler_get_natural_xy(tsptr, &x, &y); + return tiler_get_address(orient, DMM_GET_ACC_MODE(tsptr), x, y); +} +EXPORT_SYMBOL(tiler_reorient_addr); + +unsigned long +tiler_get_natural_addr(void *sysPtr) +{ + return (unsigned long)sysPtr & DMM_ALIAS_VIEW_CLEAR; +} +EXPORT_SYMBOL(tiler_get_natural_addr); + +unsigned long +tiler_reorient_topleft(unsigned long tsptr, + struct dmmViewOrientT orient, + unsigned int validDataWidth, + unsigned int validDataHeight) +{ + struct dmmTILERContPageAreaT *bufferMappedZone; + struct dmmTILERContCtxT *dmmTilerCtx = + &((struct dmmInstanceCtxT *)ctxptr)->dmmTilerCtx; + enum dmmMemoryAccessT accessModeM; + unsigned long x_pagew, y_pagew, x, y; + + tilerdump(__LINE__); + + accessModeM = DMM_GET_ACC_MODE(tsptr); + + bufferMappedZone = \ + dmm_tiler_get_area_from_sysptr(dmmTilerCtx, (void *)tsptr); + + if (bufferMappedZone == NULL) + return 0x0; + + switch (accessModeM) { + case MODE_8_BIT: + x_pagew = DMM_PAGE_DIMM_X_MODE_8; + y_pagew = DMM_PAGE_DIMM_Y_MODE_8; + break; + case MODE_16_BIT: + x_pagew = DMM_PAGE_DIMM_X_MODE_16; + y_pagew = DMM_PAGE_DIMM_Y_MODE_16; + break; + case MODE_32_BIT: + x_pagew = DMM_PAGE_DIMM_X_MODE_32; + y_pagew = DMM_PAGE_DIMM_Y_MODE_32; + break; + case MODE_PAGE: + default: + x_pagew = y_pagew = 1; + break; + } + + if (!validDataWidth) + validDataWidth = (bufferMappedZone->x1 - + bufferMappedZone->x0 + 1) * x_pagew; + + if (!validDataHeight) + validDataHeight = (bufferMappedZone->y1 - + bufferMappedZone->y0 + 1) * y_pagew; + + x = bufferMappedZone->x0 * x_pagew; + y = bufferMappedZone->y0 * y_pagew; + + if (orient.dmmXInvert) + x += validDataWidth - 1; + if (orient.dmmYInvert) + y += validDataHeight - 1; + + return tiler_get_address(orient, accessModeM, x, y); +} +EXPORT_SYMBOL(tiler_reorient_topleft); + +void +tiler_rotate_view(struct dmmViewOrientT *orient, unsigned long rotation) +{ + rotation = (rotation / 90) & 3; + + if (rotation & 2) { + orient->dmmXInvert = !orient->dmmXInvert; + orient->dmmYInvert = !orient->dmmYInvert; + } + + if (rotation & 1) { + if (orient->dmm90Rotate) + orient->dmmYInvert = !orient->dmmYInvert; + else + orient->dmmXInvert = !orient->dmmXInvert; + orient->dmm90Rotate = !orient->dmm90Rotate; + } +} +EXPORT_SYMBOL(tiler_rotate_view); + +static int createlist(struct node **listhead) +{ + int error = -1; + void *ret = NULL; + + *listhead = kmalloc(sizeof(struct node), GFP_KERNEL); + if (*listhead == NULL) { + printk(KERN_ERR "%s():%d: ERROR!\n", __func__, __LINE__); + return error; + } + ret = memset(*listhead, 0x0, sizeof(struct node)); + if (ret != *listhead) { + printk(KERN_ERR "%s():%d: ERROR!\n", __func__, __LINE__); + return error; + } else { + /* printk(KERN_ERR "%s():%d: success!\n", __func__, __LINE__);*/ + } + return 0; +} + +static int addnode(struct node *listhead, struct tiler_buf_info *ptr) +{ + int error = -1; + struct node *tmpnode = NULL; + struct node *newnode = NULL; + void *ret = NULL; + + /* assert(listhead != NULL); */ + newnode = kmalloc(sizeof(struct node), GFP_KERNEL); + if (newnode == NULL) { + printk(KERN_ERR "%s():%d: ERROR!\n", __func__, __LINE__); + return error; + } + ret = memset(newnode, 0x0, sizeof(struct node)); + if (ret != newnode) { + printk(KERN_ERR "%s():%d: ERROR!\n", __func__, __LINE__); + return error; + } + newnode->ptr = ptr; + tmpnode = listhead; + + while (tmpnode->nextnode != NULL) + tmpnode = tmpnode->nextnode; + tmpnode->nextnode = newnode; + + return 0; +} + +static int +removenode(struct node *listhead, int offset) +{ + struct node *node = NULL; + struct node *tmpnode = NULL; + + node = listhead; + + while (node->nextnode != NULL) { + if (node->nextnode->ptr->offset == offset) { + tmpnode = node->nextnode; + node->nextnode = tmpnode->nextnode; + kfree(tmpnode->ptr); + kfree(tmpnode); + tmpnode = NULL; + return 0; + } + node = node->nextnode; + } + return -1; +} + +static int +tiler_destroy_buf_info_list(struct node *listhead) +{ + struct node *tmpnode = NULL; + struct node *node = NULL; + + node = listhead; + + while (node->nextnode != NULL) { + tmpnode = node->nextnode; + node->nextnode = tmpnode->nextnode; + kfree(tmpnode); + tmpnode = NULL; + } + kfree(listhead); + return 0; +} + +static int +tiler_get_buf_info(struct node *listhead, struct tiler_buf_info **pp, int offst) +{ + struct node *node = NULL; + + node = listhead; + + while (node->nextnode != NULL) { + if (node->nextnode->ptr->offset == offst) { + *pp = node->nextnode->ptr; + return 0; + } + node = node->nextnode; + } + return -1; +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("davidsin@ti.com"); +module_init(dmm_init); +module_exit(dmm_exit); + + diff --git a/drivers/media/video/tiler/dmm_2d_alloc.c b/drivers/media/video/tiler/dmm_2d_alloc.c new file mode 100755 index 000000000000..5debaa855927 --- /dev/null +++ b/drivers/media/video/tiler/dmm_2d_alloc.c @@ -0,0 +1,1814 @@ +/* ============================================================================= +* Texas Instruments OMAP(TM) Platform Software +* (c) Copyright 2009, Texas Instruments Incorporated. All Rights Reserved. +* +* Use of this software is controlled by the terms and conditions found +* in the license agreement under which this software has been supplied. +* =========================================================================== */ +/** +* @file dmm_2D_alloc.c +* +* Two dimensional allocation algorithm implementation file. +* +* @path WTSD_DucatiMMSW/drivers/drv_dmm/src +* +* @rev 00.02 +*/ +/* -------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- +*! +*! Revision History +*! =================================== +*! 19-02-2009 Andreev: created. +*! 27-03-2009 Andreev: bug fixes. +*! +* =========================================================================== */ + +/* User code goes here */ +/* ------compilation control switches --------------------------------------- */ +/**************************************************************** + * INCLUDE FILES + ***************************************************************/ +/* ----- system and platform files ----------------------------*/ +/*-------program files ----------------------------------------*/ +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> /* vmalloc_to_page */ +#include <linux/mmzone.h> /* __page_to_phys */ +#include "dmm_def.h" +#include "dmm_2d_alloc.h" +#include "dmm_prv.h" + +#define __NEWCODE__ + +#ifdef __NEWCODE__ +#include <linux/io.h> +#endif + +/**************************************************************** +* EXTERNAL REFERENCES NOTE: only use if not found in header file +****************************************************************/ +/*--------data declarations -----------------------------------*/ +/*--------function prototypes ---------------------------------*/ + +/**************************************************************** +* PUBLIC DECLARATIONS Defined here, used elsewhere +****************************************************************/ +/*--------data declarations -----------------------------------*/ +/*--------function prototypes ---------------------------------*/ + +/**************************************************************** +* PRIVATE DECLARATIONS Defined here, used only here +****************************************************************/ +/*--------macros ----------------------------------------------*/ +/*--------data declarations -----------------------------------*/ +/*--------function prototypes ---------------------------------*/ +#define DMM_ASSERT_BREAK tilerdump(__LINE__); while (1); +#define DMM_ENTER_CRITICAL_SECTION() +#define DMM_EXIT_CRITICAL_SETCTION + +/* ========================================================================== */ +/** + * overlapping_test() + * + * @brief Performs an area overlap test for errors. Debug only. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @return MSP_BOOL: MSP_TRUE if overlapping is detected. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void overlapping_test(struct dmmTILERContCtxT *dmmTilerCtx) +{ + struct dmmTILERContPageLstT *itr1 = dmmTilerCtx->usdArList; + + while (itr1 != NULL) { + struct dmmTILERContPageAreaT *chkPage = &(itr1->pgAr); + struct dmmTILERContPageLstT *itr2 = dmmTilerCtx->usdArList; + + while (itr2 != NULL) { + if (chkPage != &(itr2->pgAr)) { + if ((itr2->pgAr.x0 <= chkPage->x1 && + itr2->pgAr.x1 >= chkPage->x0) && + (itr2->pgAr.y0 <= chkPage->y1 && + itr2->pgAr.y1 >= chkPage->y0)) { + DMM_ASSERT_BREAK + } + } + + itr2 = itr2->pgArNext; + } + + itr1 = itr1->pgArNext; + } + return; +} + +/* ========================================================================== */ +/** + * point_free_test() + * + * @brief Tests if a page on a given coordinate is unoccupied. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param areaHit - struct dmmTILERContPageAreaT** - [out] Returns a pointer to + * the allocated area that was hit by the specified coordinate. + * + * @return MSP_BOOL: MSP_TRUE if the selected coordinate page is unoccupied. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline enum MSP_BOOL point_free_test(struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + struct dmmTILERContPageAreaT **areaHit) +{ + struct dmmTILERContPageLstT *arUsd = tlrCtx->usdArList; + *areaHit = NULL; + while (arUsd != NULL) { + if (arUsd->pgAr.x0 <= X && arUsd->pgAr.x1 >= X && + arUsd->pgAr.y0 <= Y && arUsd->pgAr.y1 >= Y) { + *areaHit = &(arUsd->pgAr); + return MSP_FALSE; + } + + arUsd = arUsd->pgArNext; + } + return MSP_TRUE; +} + +/* ========================================================================== */ +/** + * pointAreaHit() + * + * @brief Tests if a given coordinate is within a given area. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param areaHit - struct dmmTILERContPageAreaT* - [in] Given area to check if + * contains the given coordinate point. + * + * @return MSP_BOOL: MSP_TRUE if the given coordinate is contained in the + * given area. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +/* +inline enum MSP_BOOL pointAreaHit(signed long X, + signed long Y, + struct dmmTILERContPageAreaT* area) +{ + if ((area->x0 <= X && area->x1 >= X) && (area->y0 <= Y && area->y1 >= Y)) + { + return MSP_TRUE; + } + return MSP_FALSE; +} +*/ + +/* ========================================================================== */ +/** + * zone_area_overlap() + * + * @brief Tests if two areas overlap. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param SizeX - signed long - [in] Page width. + * + * @param SizeY - signed long - [in] Page height. + * + * @param areaHit - struct dmmTILERContPageAreaT* - [in] Given area to check. + * + * @return MSP_BOOL: MSP_TRUE if the given areas overlap. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline enum MSP_BOOL zone_area_overlap(signed long X, + signed long Y, + signed long SizeX, + signed long SizeY, + struct dmmTILERContPageAreaT *area) +{ + if ((area->x0 <= X + SizeX && area->x1 >= X) && + (area->y0 <= Y + SizeY && area->y1 >= Y)) { + return MSP_TRUE; + } + + return MSP_FALSE; +} + +/* ========================================================================== */ +/** + * lineAreaHit() + * + * @brief Tests if a page line overlaps with an area. Either width or height + * can be used to perform checks in both coordinates. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param SizeX - signed long - [in] Page width. + * + * @param SizeY - signed long - [in] Page height. + * + * @param areaHit - struct dmmTILERContPageAreaT* - [in] Given area to check. + * + * @return MSP_BOOL: MSP_TRUE if the given areas overlap. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +/* +inline enum MSP_BOOL lineAreaHit(signed long X, + signed long Y, + signed long SizeX, + signed long SizeY, + struct dmmTILERContPageAreaT* area) +{ + if (((area->x0 <= X && area->x1 >= X) || + (area->x0 <= X + SizeX && area->x1 >= X + SizeX)) && + ((area->y0 <= Y && area->y1 >= Y) || + (area->y0 <= Y + SizeY && area->y1 >= Y + SizeY))) + { + return MSP_TRUE; + } + + return MSP_FALSE; +} +*/ + +/* ========================================================================== */ +/** + * expand_right() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line (1D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param areaHit - struct dmmTILERContPageAreaT** - [out] Returns a pointer to + * the allocated area that was hit during the "expansion" along the specified + * coordinate line. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_right(struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + struct dmmTILERContPageAreaT **areaHit) +{ + *areaHit = NULL; + while (X < tlrCtx->contSizeX) { + if (MSP_FALSE == point_free_test(tlrCtx, X, Y, areaHit)) + return X - 1; + X++; + } + + return X - 1; +} + +/* ========================================================================== */ +/** + * expand_left() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line (1D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param areaHit - struct dmmTILERContPageAreaT** - [out] Returns a pointer to + * the allocated area that was hit during the "expansion" along the specified + * coordinate line. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_left(struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + struct dmmTILERContPageAreaT **areaHit) +{ + *areaHit = NULL; + while (X >= 0) { + if (MSP_FALSE == point_free_test(tlrCtx, X, Y, areaHit)) + return X + 1; + X--; + } + + return X + 1; +} + +/* ========================================================================== */ +/** + * expand_bottom() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line (1D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param areaHit - struct dmmTILERContPageAreaT** - [out] Returns a pointer to + * the allocated area that was hit during the "expansion" along the specified + * coordinate line. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_bottom(struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + struct dmmTILERContPageAreaT **areaHit) +{ + *areaHit = NULL; + while (Y < tlrCtx->contSizeY) { + if (MSP_FALSE == point_free_test(tlrCtx, X, Y, areaHit)) + return Y - 1; + Y++; + } + + return Y - 1; +} + +/* ========================================================================== */ +/** + * expand_top() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line (1D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param areaHit - struct dmmTILERContPageAreaT** - [out] Returns a pointer to + * the allocated area that was hit during the "expansion" along the specified + * coordinate line. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_top(struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + struct dmmTILERContPageAreaT **areaHit) +{ + *areaHit = NULL; + while (Y >= 0) { + if (MSP_FALSE == point_free_test(tlrCtx, X, Y, areaHit)) + return Y + 1; + Y--; + } + + return Y + 1; +} + +/* ========================================================================== */ +/** + * expand_line_on_bottom_to_right() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeY - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_line_on_bottom_to_right( + struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + signed long elSizeY) +{ + struct dmmTILERContPageLstT *iter; + int elSizeX = 0; + + while (elSizeX < tlrCtx->contSizeX) { + iter = tlrCtx->usdArList; + while (iter != NULL) { + if (1 == zone_area_overlap(X, Y, elSizeX, + elSizeY, &(iter->pgAr))) { + return X + elSizeX - 1; + } + iter = iter->pgArNext; + } + elSizeX++; + } + + return tlrCtx->contSizeX - 1; +} + +/* ========================================================================== */ +/** + * expand_line_on_bottom_to_left() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeY - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_line_on_bottom_to_left( + struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + signed long elSizeY) +{ + struct dmmTILERContPageLstT *iter; + int elSizeX = 0; + + while (X >= 0) { + iter = tlrCtx->usdArList; + while (iter != NULL) { + if (1 == zone_area_overlap(X - elSizeX, + Y, elSizeX, elSizeY, &(iter->pgAr))) { + return X + 1; + } + iter = iter->pgArNext; + } + X--; + elSizeX++; + } + + return 0; +} + +/* ========================================================================== */ +/** + * expandLineOnTopToRight() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeY - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +/* +inline signed long expandLineOnTopToRight(struct dmmTILERContCtxT* tlrCtx, + signed long X, signed long Y, + signed long elSizeY) +{ + struct dmmTILERContPageLstT* iter; + int elSizeX = 0; + + while (elSizeX < tlrCtx->contSizeX) + { + iter = tlrCtx->usdArList; + while (iter != NULL) + { + if (1 == zone_area_overlap(X, Y, elSizeX, elSizeY, + &(iter->pgAr))) + return X + elSizeX - 1; + iter = iter->pgArNext; + } + elSizeX++; + } + + return tlrCtx->contSizeX - 1; +} +*/ + +/* ========================================================================== */ +/** + * expandLineOnTopToLeft() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeY - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +/* +inline signed long expandLineOnTopToLeft(struct dmmTILERContCtxT* tlrCtx, + signed long X, signed long Y, + signed long elSizeY) +{ + struct dmmTILERContPageLstT* iter; + int elSizeX = 0; + + while (X >= 0) + { + iter = tlrCtx->usdArList; + while (iter != NULL) + { + if (1 == zone_area_overlap(X - elSizeX, + Y - elSizeY, elSizeX, elSizeY, + &(iter->pgAr))) + return X + 1; + iter = iter->pgArNext; + } + X--; + elSizeX++; + } + return 0; +} +*/ + +/* ========================================================================== */ +/** + * expand_line_on_right_to_bottom() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeX - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_line_on_right_to_bottom( + struct dmmTILERContCtxT *tlrCtx, + signed long X, + signed long Y, + signed long elSizeX) +{ + struct dmmTILERContPageLstT *iter; + int elSizeY = 0; + + while (elSizeY < tlrCtx->contSizeY) { + iter = tlrCtx->usdArList; + while (iter != NULL) { + if (1 == zone_area_overlap( + X, Y, elSizeX, elSizeY, &(iter->pgAr))) { + return Y + elSizeY - 1; + } + iter = iter->pgArNext; + } + elSizeY++; + } + + return tlrCtx->contSizeY - 1; +} + +/* ========================================================================== */ +/** + * expand_line_on_right_to_top() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeX - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +inline signed long expand_line_on_right_to_top(struct dmmTILERContCtxT *tlrCtx, + signed long X, signed long Y, + signed long elSizeX) +{ + struct dmmTILERContPageLstT *iter; + int elSizeY = 0; + + while (Y >= 0) { + iter = tlrCtx->usdArList; + while (iter != NULL) { + if (1 == zone_area_overlap(X, Y - elSizeY, + elSizeX, elSizeY, &(iter->pgAr))) { + return Y + 1; + } + iter = iter->pgArNext; + } + Y--; + elSizeY++; + } + + return 0; +} + +/* ========================================================================== */ +/** + * expandLineOnLeftToBottom() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeX - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +/* +inline signed long expandLineOnLeftToBottom(struct dmmTILERContCtxT* tlrCtx, + signed long X, signed long Y, + signed long elSizeX) +{ + struct dmmTILERContPageLstT* iter; + int elSizeY = 0; + + while (elSizeY < tlrCtx->contSizeY) + { + iter = tlrCtx->usdArList; + while (iter != NULL) + { + if (1 == zone_area_overlap(X, Y, elSizeX, elSizeY, + &(iter->pgAr))) + return Y + elSizeY - 1; + iter = iter->pgArNext; + } + elSizeY++; + } + return tlrCtx->contSizeY - 1; +} +*/ + +/* ========================================================================== */ +/** + * expandLineOnLeftToTop() + * + * @brief Finds all of the unocupied pages ("expansion") along the specified + * coordinate line area (2D check). + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X page coordinate. + * + * @param Y - signed long - [in] Y page coordinate. + * + * @param elSizeX - signed long - [in] Size of the area to check. + * + * @return signed long the calculated coordinate point. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +/* +inline signed long expandLineOnLeftToTop(struct dmmTILERContCtxT* tlrCtx, + signed long X, signed long Y, + signed long elSizeX) +{ + struct dmmTILERContPageLstT* iter; + int elSizeY = 0; + + while (Y >= 0) + { + iter = tlrCtx->usdArList; + while (iter != NULL) + { + if (1 == zone_area_overlap(X - elSizeX, Y - elSizeY, + elSizeX, elSizeY, &(iter->pgAr))) + return Y + 1; + iter = iter->pgArNext; + } + Y--; + elSizeY++; + } + return 0; +} +*/ + +/* ========================================================================== */ +/** + * area_required_to_allocated() + * + * @brief Given the required area to be allcoted, total area present and anchor + * points along a side of attachemnt, calculates the actual position of the new + * new 2D area that is allocated. + * + * @param areaReq - struct dmmTILERContPageAreaT* - [in] Required 2D area to be + * allocated. + * + * @param areaTotal - struct dmmTILERContPageAreaT* - [in] Total 2D area + * allocated for this area attachment. + * + * @param areaAlloc - struct dmmTILERContPageAreaT* - [out] The actual + * allocated area. + * + * @param anchorX - signed long - [in] X page coordinate anchor point. + * + * @param anchorY - signed long - [in] Y page coordinate anchor point. + * + * @param side - enum SideAffinity - [in] Side of attachment. + * + * @return none + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void area_required_to_allocated(struct dmmTILERContPageAreaT *areaReq, + struct dmmTILERContPageAreaT *areaTotal, + struct dmmTILERContPageAreaT *areaAlloc, + signed long anchorX, + signed long anchorY, + enum SideAffinity side) +{ + switch (side) { + case PSA_BOTTOM: + if (areaTotal->x1 - anchorX < areaReq->x1) { + if (areaTotal->x1 - areaTotal->x0 >= areaReq->x1) { + areaAlloc->x0 = + (unsigned short)(areaTotal->x1 - areaReq->x1); + areaAlloc->x1 = + (unsigned short)(areaAlloc->x0 + areaReq->x1); + } else { + areaAlloc->x0 = areaTotal->x0; + areaAlloc->x1 = areaTotal->x1; + } + } else { + areaAlloc->x0 = (unsigned short)(anchorX); + areaAlloc->x1 = (unsigned short)(anchorX + areaReq->x1); + } + + areaAlloc->y0 = (unsigned short)(anchorY); + if (areaAlloc->y0 + areaReq->y1 <= areaTotal->y1) { + areaAlloc->y1 = (unsigned short) + (areaAlloc->y0 + areaReq->y1); + } else { + areaAlloc->y1 = areaTotal->y1; + } + areaAlloc->fitToSide = PSA_BOTTOM; + break; + + case PSA_TOP: + if (areaTotal->x1 - anchorX < areaReq->x1) { + if (areaTotal->x1 - areaTotal->x0 >= areaReq->x1) { + areaAlloc->x0 = + (unsigned short) + (areaTotal->x1 - areaReq->x1); + areaAlloc->x1 = + (unsigned short) + (areaAlloc->x0 + areaReq->x1); + } else { + areaAlloc->x0 = areaTotal->x0; + areaAlloc->x1 = areaTotal->x1; + } + } else { + areaAlloc->x0 = (unsigned short)(anchorX); + areaAlloc->x1 = (unsigned short)(anchorX + areaReq->x1); + } + + areaAlloc->y1 = (unsigned short)(anchorY); + if (areaAlloc->y1 - areaReq->y1 >= areaTotal->y0) { + areaAlloc->y0 = (unsigned short) + (areaAlloc->y1 - areaReq->y1); + } else { + areaAlloc->y0 = areaTotal->y0; + } + areaAlloc->fitToSide = PSA_TOP; + break; + + case PSA_RIGHT: + if (areaTotal->y1 - anchorY < areaReq->y1) { + if (areaTotal->y1 - areaTotal->y0 >= areaReq->y1) { + areaAlloc->y0 = + (unsigned short) + (areaTotal->y1 - areaReq->y1); + areaAlloc->y1 = + (unsigned short) + (areaAlloc->y0 + areaReq->y1); + } else { + areaAlloc->y0 = areaTotal->y0; + areaAlloc->y1 = areaTotal->y1; + } + } else { + areaAlloc->y0 = (unsigned short)(anchorY); + areaAlloc->y1 = (unsigned short)(anchorY + areaReq->y1); + } + + areaAlloc->x0 = (unsigned short)(anchorX); + if (areaAlloc->x0 + areaReq->x1 <= areaTotal->x1) { + areaAlloc->x1 = (unsigned short) + (areaAlloc->x0 + areaReq->x1); + } else { + areaAlloc->x1 = areaTotal->x1; + } + areaAlloc->fitToSide = PSA_RIGHT; + break; + + case PSA_LEFT: + if (areaTotal->y1 - anchorY < areaReq->y1) { + if (areaTotal->y1 - areaTotal->y0 >= areaReq->y1) { + areaAlloc->y0 = + (unsigned short) + (areaTotal->y1 - areaReq->y1); + areaAlloc->y1 = + (unsigned short) + (areaAlloc->y0 + areaReq->y1); + } else { + areaAlloc->y0 = areaTotal->y0; + areaAlloc->y1 = areaTotal->y1; + } + } else { + areaAlloc->y0 = (unsigned short)(anchorY); + areaAlloc->y1 = (unsigned short)(anchorY + areaReq->y1); + } + + areaAlloc->x1 = (unsigned short)(anchorX); + if (areaAlloc->x1 - areaReq->x1 >= areaTotal->x0) { + areaAlloc->x0 = (unsigned short) + (areaAlloc->x1 - areaReq->x1); + } else { + areaAlloc->x0 = areaTotal->x0; + } + areaAlloc->fitToSide = PSA_LEFT; + break; + case PSA_NONE: + break; + } +} + +/* ========================================================================== */ +/** + * area_check_for_fit() + * + * @brief Checks if a specified free area is big enough for the required + * allocation, and if the specified free area is the best (smallest) choice + * of all discovered free areas. + * + * @param tmpArea - struct dmmTILERContPageAreaSpecT * - [in] A free area that + * has been discovered and needs to be checked if it is big and small enough + * for the required allocation area. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param areaReq - struct dmmTILERContPageAreaT* - [in] Required allcoation + * area. + * + * @return MSP_BOOL: True if the specified area is the new best fit. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +enum MSP_BOOL area_check_for_fit(struct dmmTILERContPageAreaSpecT *tmpArea, + struct dmmTILERContCtxT *tlrCtx, + struct dmmTILERContPageAreaT *areaReq) +{ + if ((tmpArea->ttlExpndAr.x1 - tmpArea->ttlExpndAr.x0) >= areaReq->x1 && + (tmpArea->ttlExpndAr.y1 - tmpArea->ttlExpndAr.y0) >= + areaReq->y1) { + if (/*The area of the just found total expanded space that may + house the new 2D area to be allocated */ + (tmpArea->ttlExpndAr.x1 - tmpArea->ttlExpndAr.x0) * + (tmpArea->ttlExpndAr.y1 - tmpArea->ttlExpndAr.y0) < + /* The current smallest area found among all expanded + spaces that can house the new 2D area to be allocated */ + (tlrCtx->tmpArSelect.ttlExpndAr.x1 - + tlrCtx->tmpArSelect.ttlExpndAr.x0) * + (tlrCtx->tmpArSelect.ttlExpndAr.y1 - + tlrCtx->tmpArSelect.ttlExpndAr.y0)) { + /* If the just found area is smaller than the current + "smallest"... */ + tlrCtx->tmpArSelect.ttlExpndAr.x0 = + tmpArea->ttlExpndAr.x0; + tlrCtx->tmpArSelect.ttlExpndAr.y0 = + tmpArea->ttlExpndAr.y0; + tlrCtx->tmpArSelect.ttlExpndAr.x1 = + tmpArea->ttlExpndAr.x1; + tlrCtx->tmpArSelect.ttlExpndAr.y1 = + tmpArea->ttlExpndAr.y1; + tlrCtx->tmpArSelect.ttlExpndAr.fitToSide = + tmpArea->ttlExpndAr.fitToSide; + + tlrCtx->tmpArSelect.plmntAr.x0 = tmpArea->plmntAr.x0; + tlrCtx->tmpArSelect.plmntAr.y0 = tmpArea->plmntAr.y0; + tlrCtx->tmpArSelect.plmntAr.x1 = tmpArea->plmntAr.x1; + tlrCtx->tmpArSelect.plmntAr.y1 = tmpArea->plmntAr.y1; + tlrCtx->tmpArSelect.plmntAr.fitToSide = + tmpArea->plmntAr.fitToSide; + + tlrCtx->tmpArSelect.anchrAr = tmpArea->anchrAr; + + return MSP_TRUE; + } + } + + return MSP_FALSE; +} + +/* ========================================================================== */ +/** + * area_fit_to_left() + * + * @brief Checks if a specified free area is big enough for the required + * allocation, and if the specified free area is the best (smallest) choice + * of all discovered free areas. + * + * @param areaReq - struct dmmTILERContPageAreaT* - [in] Required allcoation + * area. + * + * @param atchAr - struct dmmTILERContPageAreaT* - [in] Area along which + * specified side the search for a new free area is performed. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @return MSP_BOOL: True if a qualified free zones is discovered. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +enum MSP_BOOL area_fit_to_left(struct dmmTILERContPageAreaT *areaReq, + struct dmmTILERContPageAreaT *atchAr, + struct dmmTILERContCtxT *tlrCtx) +{ + struct dmmTILERContPageAreaT *areaHit; + struct dmmTILERContPageAreaSpecT fitArea; + signed long X; + signed long Y; + signed long anchorX; + signed long anchorY; + enum MSP_BOOL fit; + + areaHit = NULL; + fitArea.anchrAr = atchAr; + X = atchAr->x0 - 1; + Y = atchAr->y0; + anchorX = X; + anchorY = Y; + fit = MSP_FALSE; + + if (X >= 0 && X <= tlrCtx->contSizeX && Y >= 0 && Y <= + tlrCtx->contSizeY) { + tilerdump(__LINE__); + while (Y <= atchAr->y1) { + anchorY = Y; + if (1 == point_free_test(tlrCtx, X, Y, &areaHit)) { + if (Y == atchAr->y0) { + /* If this is the first attempt to start + "expanding" a 2D space, then it should + be checked to the respective left,top, + right or bottom directions if there is + more points that can be concatenated to + the area to discover. This "expansion" + in certain direction is causing some + interesting behaviour patterns. */ + Y = (unsigned short)expand_top(tlrCtx, + X, Y, &areaHit); + } + + /* Once the vertical "top" and "bottom" free + points along the side of the current area + are found a "line" of free points is defined. */ + fitArea.ttlExpndAr.y0 = (unsigned short)(Y); + fitArea.ttlExpndAr.y1 = + (unsigned short)expand_bottom + (tlrCtx, X, Y, &areaHit); + /* The just defined line + of free points is then "expanded" in the + opposite direction of the side of the current + area to witch the 2D space is being defined. */ + fitArea.ttlExpndAr.x0 = + (unsigned short)expand_line_on_bottom_to_left( + tlrCtx, X, Y, fitArea.ttlExpndAr.y1 - Y); + fitArea.ttlExpndAr.x1 = (unsigned short)(X); + + fitArea.ttlExpndAr.fitToSide = PSA_LEFT; + /* Once the new 2D space is defined along the + specified side of the current 2D area, the + required 2D area to be allocated has to be + placed in the total expanded space as close to + the current 2D area as possible (aligning to + one of the corners of the new expanded space + along the side of the current 2D area). + Manipulating this function can change the + allocation behaviour pattern drastically, and + after further investigation will probably be + target of changes. */ + area_required_to_allocated(areaReq, + &(fitArea.ttlExpndAr), &(fitArea.plmntAr), + anchorX, anchorY, PSA_LEFT); + + fit |= area_check_for_fit(&fitArea, + tlrCtx, areaReq); + + break; + } else if (areaHit != NULL) { + /* If an area is hit when the first free point + is being determined, it is safe to "jump" over + it and start the new checks for free points. */ + Y = areaHit->y1 + 1; + areaHit = NULL; + } else { + DMM_ASSERT_BREAK + } + } + } + tilerdump(__LINE__); + return fit; +} + +/* ========================================================================== */ +/** + * area_fit_to_right() + * + * @brief Checks if a specified free area is big enough for the required + * allocation, and if the specified free area is the best (smallest) choice + * of all discovered free areas. + * + * @param areaReq - struct dmmTILERContPageAreaT* - [in] Required allcoation + * area. + * + * @param atchAr - struct dmmTILERContPageAreaT* - [in] Area along which + * specified side the search for a new free area is performed. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @return MSP_BOOL: True if a qualified free zones is discovered. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +enum MSP_BOOL area_fit_to_right(struct dmmTILERContPageAreaT *areaReq, + struct dmmTILERContPageAreaT *atchAr, + struct dmmTILERContCtxT *tlrCtx) +{ + struct dmmTILERContPageAreaT *areaHit; + struct dmmTILERContPageAreaSpecT fitArea; + signed long X; + signed long Y; + signed long anchorX; + signed long anchorY; + enum MSP_BOOL fit; + + areaHit = NULL; + fitArea.anchrAr = atchAr; + X = atchAr->x1 + 1; + Y = atchAr->y0; + anchorX = X; + anchorY = Y; + fit = MSP_FALSE; + + if (X >= 0 && X <= tlrCtx->contSizeX && Y >= 0 && Y <= + tlrCtx->contSizeY) { + tilerdump(__LINE__); + while (Y <= atchAr->y1) { + anchorY = Y; + if (1 == point_free_test(tlrCtx, X, Y, &areaHit)) { + if (Y == atchAr->y0) { + /*If this is the first attempt to start + "expanding" a 2D space, then it should + be checked to the respective left,top, + right or bottom directions if there is + more points that can be concatenated to + the area to discover. This "expansion" + in certain direction is causing some + interesting behaviour patterns. */ + Y = (unsigned short)expand_top(tlrCtx, + X, Y, &areaHit); + } + + /* Once the vertical "top" and "bottom" free + points along the side of the current area + are found a "line" of free points is + defined. */ + fitArea.ttlExpndAr.y0 = (unsigned short)(Y); + fitArea.ttlExpndAr.y1 = + (unsigned short)expand_bottom(tlrCtx, X, Y, + &areaHit); + /* The just defined line of free points is then + "expanded" in the opposite direction of + the side of the current area to witch the 2D + space is being defined. */ + fitArea.ttlExpndAr.x0 = (unsigned short)(X); + fitArea.ttlExpndAr.x1 = + (unsigned short)expand_line_on_bottom_to_right( + tlrCtx, X, Y, fitArea.ttlExpndAr.y1 - Y); + + fitArea.ttlExpndAr.fitToSide = PSA_RIGHT; + /* Once the new 2D space is defined along the + specified side of the current 2D area, the + required 2D area to be allocated has to be + placed in the total expanded space as close to + the current 2D area as possible (aligning to one + of the corners of the new expanded space along + the side of the current 2D area). Manipulating + this function can change the allocation + behaviour pattern drastically, and after further + investigation will probably be target of + changes. */ + area_required_to_allocated(areaReq, + &(fitArea.ttlExpndAr), &(fitArea.plmntAr), + anchorX, anchorY, PSA_RIGHT); + + fit |= area_check_for_fit(&fitArea, + tlrCtx, areaReq); + + break; + } else if (areaHit != NULL) { + /* If an area is hit when the first free point + is being determined, it is safe to "jump" over + it and start the new checks for free points. */ + Y = areaHit->y1 + 1; + areaHit = NULL; + } else { + DMM_ASSERT_BREAK + } + } + } + tilerdump(__LINE__); + return fit; +} + +/* ========================================================================== */ +/** + * area_fit_to_top() + * + * @brief Checks if a specified free area is big enough for the required + * allocation, and if the specified free area is the best (smallest) choice + * of all discovered free areas. + * + * @param areaReq - struct dmmTILERContPageAreaT* - [in] Required allcoation + * area. + * + * @param atchAr - struct dmmTILERContPageAreaT* - [in] Area along which + * specified side the search for a new free area is performed. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @return MSP_BOOL: True if a qualified free zones is discovered. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +enum MSP_BOOL area_fit_to_top(struct dmmTILERContPageAreaT *areaReq, + struct dmmTILERContPageAreaT *atchAr, + struct dmmTILERContCtxT *tlrCtx) +{ + struct dmmTILERContPageAreaT *areaHit; + struct dmmTILERContPageAreaSpecT fitArea; + signed long X; + signed long Y; + signed long anchorX; + signed long anchorY; + enum MSP_BOOL fit; + + areaHit = NULL; + fitArea.anchrAr = atchAr; + X = atchAr->x0; + Y = atchAr->y0 - 1; + anchorX = X; + anchorY = Y; + fit = MSP_FALSE; + + if (X >= 0 && X < tlrCtx->contSizeX && Y >= 0 && + Y < tlrCtx->contSizeY) { + tilerdump(__LINE__); + while (X <= atchAr->x1) { + anchorX = X; + if (1 == point_free_test(tlrCtx, X, Y, &areaHit)) { + if (X == atchAr->x0) { + /* If this is the first attempt to start + "expanding" a 2D space, then it should + be checked to the respective left,top, + right or bottom directions if there is + more points that can be concatenated to + the area to discover. This "expansion" + in certain direction is causing some + interesting behaviour patterns. */ + X = (unsigned short)expand_left( + tlrCtx, X, Y, &areaHit); + } + + /* Once the vertical "left" and "right" free + points along the side of the current area + are found a "line" of free points is defined. */ + fitArea.ttlExpndAr.x0 = (unsigned short)(X); + fitArea.ttlExpndAr.x1 = + (unsigned short)expand_right(tlrCtx, + X, Y, &areaHit); + /* The just defined line of free points is then + "expanded" in the opposite direction of + the side of the current area to witch the 2D + space is being defined. */ + fitArea.ttlExpndAr.y0 = + (unsigned short)expand_line_on_right_to_top( + tlrCtx, X, Y, fitArea.ttlExpndAr.x1 - X); + fitArea.ttlExpndAr.y1 = (unsigned short)(Y); + + fitArea.ttlExpndAr.fitToSide = PSA_TOP; + /* Once the new 2D space is defined along the + specified side of the current 2D area, the + required 2D area to be allocated has to be + placed in the total expanded space as close + to the current 2D area as possible (aligning + to one of the corners of the new expanded space + along the side of the current 2D area). + Manipulating this function can change the + allocation behaviour pattern drastically, and + after further investigation will probably be + target of changes. */ + area_required_to_allocated(areaReq, + &(fitArea.ttlExpndAr), &(fitArea.plmntAr), + anchorX, anchorY, PSA_TOP); + + fit |= area_check_for_fit(&fitArea, tlrCtx, + areaReq); + + break; + } else if (areaHit != NULL) { + /* If an area is hit when the first free point + is being determined, it is safe to "jump" over + it and start the new checks for free points. */ + X = areaHit->x1 + 1; + areaHit = NULL; + } else { + DMM_ASSERT_BREAK + } + } + } + tilerdump(__LINE__); + return fit; +} + +/* ========================================================================== */ +/** + * area_fit_to_bottom() + * + * @brief Checks if a specified free area is big enough for the required + * allocation, and if the specified free area is the best (smallest) choice + * of all discovered free areas. + * + * @param areaReq - struct dmmTILERContPageAreaT* - [in] Required allcoation + * area. + * + * @param atchAr - struct dmmTILERContPageAreaT* - [in] Area along which + * specified side the search for a new free area is performed. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @return MSP_BOOL: True if a qualified free zones is discovered. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +enum MSP_BOOL area_fit_to_bottom(struct dmmTILERContPageAreaT *areaReq, + struct dmmTILERContPageAreaT *atchAr, + struct dmmTILERContCtxT *tlrCtx) +{ + struct dmmTILERContPageAreaT *areaHit; + struct dmmTILERContPageAreaSpecT fitArea; + signed long X; + signed long Y; + signed long anchorX; + signed long anchorY; + enum MSP_BOOL fit; + + areaHit = NULL; + fitArea.anchrAr = atchAr; + X = atchAr->x0; + Y = atchAr->y1 + 1; + anchorX = X; + anchorY = Y; + fit = MSP_FALSE; + + if (X >= 0 && X < tlrCtx->contSizeX && Y >= 0 && + Y < tlrCtx->contSizeY) { + tilerdump(__LINE__); + while (X <= atchAr->x1) { + if (1 == point_free_test(tlrCtx, X, Y, &areaHit)) { + anchorX = X; + if (X == atchAr->x0) { + /* If this is the first attempt to start + "expanding" a 2D space, then it should + be checked to the respective left,top, + right or bottom directions if there is + more points that can be concatenated to + the area to discover. This "expansion" + in certain direction is causing some + interesting behaviour patterns. */ + X = (unsigned short)expand_left( + tlrCtx, X, Y, &areaHit); + } + + /* Once the vertical "left" and "right" free + points along the side of the current area + are found a "line" of free points is defined. */ + fitArea.ttlExpndAr.x0 = (unsigned short)(X); + fitArea.ttlExpndAr.x1 = + (unsigned short)expand_right(tlrCtx, + X, Y, &areaHit); + /* The just defined line of free points is then + "expanded" in the opposite direction of + the side of the current area to witch the 2D + space is being defined. */ + fitArea.ttlExpndAr.y0 = (unsigned short)(Y); + fitArea.ttlExpndAr.y1 = + (unsigned short)expand_line_on_right_to_bottom( + tlrCtx, X, Y, fitArea.ttlExpndAr.x1 - X); + + fitArea.ttlExpndAr.fitToSide = PSA_BOTTOM; + /* Once the new 2D space is defined along the + specified side of the current 2D area, the + required 2D area to be allocated has to be + placed in the total expanded space as close to + the current 2D area as possible (aligning to + one of the corners of the new expanded space + along the side of the current 2D area). + Manipulating this function can change the + allocation behaviour pattern drastically, and + after further investigation will probably be + target of changes. */ + area_required_to_allocated(areaReq, + &(fitArea.ttlExpndAr), &(fitArea.plmntAr), + anchorX, anchorY, PSA_BOTTOM); + + fit |= area_check_for_fit(&fitArea, tlrCtx, + areaReq); + + break; + } else if (areaHit != NULL) { + /* If an area is hit when the first free point + is being determined, it is safe to "jump" over + it and start the new checks for free points. */ + X = areaHit->x1 + 1; + areaHit = NULL; + } else { + DMM_ASSERT_BREAK + } + } + } + tilerdump(__LINE__); + return fit; +} + +/* ========================================================================== */ +/** + * alloc_2D_area() + * + * @brief Allocates a 2D area and returns a pointer to it. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param areaReq - struct dmmTILERContPageAreaT* - [in] Required area for + * allcoation. + * + * @return struct dmmTILERContPageAreaT*: pointer to the allocated area. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +struct dmmTILERContPageAreaT * +alloc_2d_area(struct dmmTILERContCtxT *dmmTilerCtx, + struct dmmTILERContPageAreaT *areaReq) +{ + struct dmmTILERContPageLstT *allocatedArea = NULL; + struct dmmTILERContPageLstT *usedIter; + + usedIter = dmmTilerCtx->usdArList; + + dmmTilerCtx->tmpArSelect.ttlExpndAr.x0 = 0; + dmmTilerCtx->tmpArSelect.ttlExpndAr.y0 = 0; + dmmTilerCtx->tmpArSelect.ttlExpndAr.x1 = dmmTilerCtx->contSizeX - 1; + dmmTilerCtx->tmpArSelect.ttlExpndAr.y1 = dmmTilerCtx->contSizeY - 1; + + DMM_ENTER_CRITICAL_SECTION(); + tilerdump(__LINE__); + if (usedIter != NULL) { + int fit = 0; + while (usedIter != NULL) { + tilerdump(__LINE__); + fit |= area_fit_to_top(areaReq, + &(usedIter->pgAr), dmmTilerCtx); + tilerdump(__LINE__); + fit |= area_fit_to_right(areaReq, + &(usedIter->pgAr), dmmTilerCtx); + tilerdump(__LINE__); + fit |= area_fit_to_bottom(areaReq, + &(usedIter->pgAr), dmmTilerCtx); + tilerdump(__LINE__); + fit |= area_fit_to_left(areaReq, + &(usedIter->pgAr), dmmTilerCtx); + tilerdump(__LINE__); + usedIter = usedIter->pgArNext; + } + + if (fit > 0) { + tilerdump(dmmTilerCtx); + allocatedArea = kmalloc + (sizeof(struct dmmTILERContPageLstT), GFP_KERNEL); + memset(allocatedArea, 0x0, + sizeof(struct dmmTILERContPageLstT)); + allocatedArea->pgAr.x0 = + dmmTilerCtx->tmpArSelect.plmntAr.x0; + allocatedArea->pgAr.y0 = + dmmTilerCtx->tmpArSelect.plmntAr.y0; + allocatedArea->pgAr.x1 = + dmmTilerCtx->tmpArSelect.plmntAr.x1; + allocatedArea->pgAr.y1 = + dmmTilerCtx->tmpArSelect.plmntAr.y1; + allocatedArea->pgAr.fitToSide = + dmmTilerCtx->tmpArSelect.plmntAr.fitToSide; + allocatedArea->anchrAr = + dmmTilerCtx->tmpArSelect.anchrAr; + + usedIter = dmmTilerCtx->usdArList; + + while (usedIter->pgArNext != NULL) + usedIter = usedIter->pgArNext; + + tilerdump(allocatedArea); + usedIter->pgArNext = allocatedArea; + } + } else { + tilerdump(__LINE__); + allocatedArea = kmalloc + (sizeof(struct dmmTILERContPageLstT), GFP_KERNEL); + memset(allocatedArea, 0x0, sizeof(struct dmmTILERContPageLstT)); + allocatedArea->pgAr.x0 = 0; + allocatedArea->pgAr.y0 = 0; + allocatedArea->pgAr.x1 = areaReq->x1; + allocatedArea->pgAr.y1 = areaReq->y1; + allocatedArea->pgAr.fitToSide = PSA_BOTTOM; + allocatedArea->anchrAr = NULL; + allocatedArea->pgArNext = NULL; + + tilerdump(allocatedArea); + dmmTilerCtx->usdArList = allocatedArea; + } + + DMM_EXIT_CRITICAL_SETCTION + + if (allocatedArea == NULL) { + tilerdump(__LINE__); + return NULL; + } else { + tilerdump(&(allocatedArea->pgAr)); + return &(allocatedArea->pgAr); + } +} + +/* ========================================================================== */ +/** + * dealloc_2D_area() + * + * @brief Deletes a 2D area from the TILER context. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param areaRem - struct dmmTILERContPageAreaT* - [in] Area to remove. + * + * @return MSP_BOOL: True if the specified area is successfuly deleted. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +enum MSP_BOOL dealloc_2d_area(struct dmmTILERContCtxT *dmmTilerCtx, + struct dmmTILERContPageAreaT *areaRem) +{ + struct dmmTILERContPageLstT *delItm; + struct dmmTILERContPageLstT *usedIter; + struct dmmTILERContPageLstT *usedPrev; + + DMM_ENTER_CRITICAL_SECTION(); + + delItm = NULL; + usedIter = dmmTilerCtx->usdArList; + usedPrev = NULL; + + tilerdump(__LINE__); + while (usedIter != NULL) { + if (areaRem->x0 == usedIter->pgAr.x0 && + areaRem->y0 == usedIter->pgAr.y0) { + tilerdump(__LINE__); + delItm = usedIter; + if (usedPrev != NULL) + usedPrev->pgArNext = usedIter->pgArNext; + else + dmmTilerCtx->usdArList = + dmmTilerCtx->usdArList->pgArNext; + + break; + } + + usedPrev = usedIter; + usedIter = usedIter->pgArNext; + } + + DMM_EXIT_CRITICAL_SETCTION + + if (delItm != NULL) { + signed long i; + enum errorCodeT eCode = DMM_NO_ERROR; + unsigned long numPages = 0x0; + + tilerdump(__LINE__); + /* If the memory pages are provided by the dmm pages memory + pool, then free them. Otherwise leave them for the user + to free them. + */ + if (delItm->pgAr.patCustomPages == MSP_FALSE) { + tilerdump(__LINE__); + numPages = (delItm->pgAr.x1 - delItm->pgAr.x0 + 1)* + (delItm->pgAr.y1 - delItm->pgAr.y0 + 1); + /* Get the area to free associated physical memory pages + and free them - how can they be obtained from the + PAT lut?! + */ + /* Is it viable and easier that each allocated area has + a list of all the physical pages mapped to it? + */ + /* As currently the system is working with a memory page + pool leave it be. + */ + for (i = 0; + i < numPages && eCode == DMM_NO_ERROR; i++) { +#ifdef __NEWCODE__ + debug(i); +#endif + eCode = dmm_free_phys_page( + (unsigned long *) + (delItm->pgAr.patPageEntries[i])); + } + } + + if (eCode == DMM_NO_ERROR) { + tilerdump(__LINE__); +#ifndef __NEWCODE__ + kfree(delItm->pgAr.patPageEntriesSpace); + delItm->pgAr.patPageEntries = NULL; + delItm->pgAr.patPageEntriesSpace = NULL; +#else + debug(__LINE__); + iounmap(delItm->pgAr.page_list_virt); + __free_page(delItm->pgAr.page_list); + delItm->pgAr.patPageEntries = NULL; + delItm->pgAr.patPageEntriesSpace = NULL; +#endif + } + + tilerdump(__LINE__); + kfree(delItm); + return MSP_TRUE; + } else { + tilerdump(__LINE__); + return MSP_FALSE; + } +} + +/* ========================================================================== */ +/** + * search_2D_area() + * + * @brief Deletes a 2D area from the TILER context. + * + * @param tlrCtx - struct dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X coordinate of the search area. + * + * @param Y - signed long - [in] X coordinate of the search area. + * + * @param xInvert - MSP_BOOL - [in] X coordinate is inverted. + * + * @param yInvert - MSP_BOOL - [in] Y coordinate is inverted. + * + * @return struct dmmTILERContPageAreaT*: pointer to the discovered 2D area + * or NULL if no such area is found. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +struct dmmTILERContPageAreaT * +search_2d_area(struct dmmTILERContCtxT *dmmTilerCtx, + signed long X, + signed long Y, + enum MSP_BOOL xInvert, + enum MSP_BOOL yInvert) +{ + struct dmmTILERContPageLstT *usedIter; + usedIter = dmmTilerCtx->usdArList; + + if (xInvert && yInvert) { + while (usedIter != NULL) { + if (X == usedIter->pgAr.x1 && Y == usedIter->pgAr.y1) { + tilerdump(__LINE__); + return &(usedIter->pgAr); + } + + usedIter = usedIter->pgArNext; + } + } else if (!xInvert && yInvert) { + while (usedIter != NULL) { + if (X == usedIter->pgAr.x0 && Y == usedIter->pgAr.y1) { + tilerdump(__LINE__); + return &(usedIter->pgAr); + } + + usedIter = usedIter->pgArNext; + } + } else if (xInvert && !yInvert) { + while (usedIter != NULL) { + if (X == usedIter->pgAr.x1 && Y == usedIter->pgAr.y0) { + tilerdump(__LINE__); + return &(usedIter->pgAr); + } + + usedIter = usedIter->pgArNext; + } + } else { + tilerdump(__LINE__); + while (usedIter != NULL) { + if (X == usedIter->pgAr.x0 && Y == usedIter->pgAr.y0) { + tilerdump(__LINE__); + return &(usedIter->pgAr); + } + + usedIter = usedIter->pgArNext; + } + } + + tilerdump(__LINE__); + return NULL; +} diff --git a/drivers/media/video/tiler/dmm_2d_alloc.h b/drivers/media/video/tiler/dmm_2d_alloc.h new file mode 100644 index 000000000000..b3b9b1997d9a --- /dev/null +++ b/drivers/media/video/tiler/dmm_2d_alloc.h @@ -0,0 +1,152 @@ +/* + * dmm_2d_alloc.h + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _DMM_2D_ALLOC_H +#define _DMM_2D_ALLOC_H + +#define __NEWCODE__ + +enum MSP_BOOL { + MSP_FALSE = 0, + MSP_TRUE +}; + +enum SideAffinity { + PSA_NONE, + PSA_LEFT, + PSA_TOP, + PSA_BOTTOM, + PSA_RIGHT +}; + +struct dmmTILERContPageAreaT { + unsigned short x0; + unsigned short y0; + unsigned short x1; + unsigned short y1; + unsigned short xPageOfst; + unsigned short yPageOfst; + unsigned short xPageCount; + unsigned short yPageCount; + enum SideAffinity fitToSide; + int patCustomPages; + unsigned long *patPageEntriesSpace; +#ifdef __NEWCODE__ + struct page *page_list; + unsigned long *page_list_virt; +#endif + unsigned long *patPageEntries; +}; + +struct dmmTILERContPageAreaSpecT { + struct dmmTILERContPageAreaT ttlExpndAr; + struct dmmTILERContPageAreaT plmntAr; + struct dmmTILERContPageAreaT *anchrAr; +}; + +struct dmmTILERContPageLstT { + struct dmmTILERContPageLstT *pgArNext; + struct dmmTILERContPageAreaT *anchrAr; + struct dmmTILERContPageAreaT pgAr; +}; + +struct dmmTILERContCtxT { + signed long contSizeX; + signed long contSizeY; + struct dmmTILERContPageLstT *usdArList; + struct dmmTILERContPageAreaSpecT tmpArSelect; +}; + +/* ========================================================================== */ +/** + * alloc2DArea() + * + * @brief Allocates a 2D area and returns a pointer to it. + * + * @param tlrCtx - dmmTILERContCtxT* - [in] TILER context structure. + * + * @param areaReq - dmmTILERContPageAreaT* - [in] Required area for allcoation. + * + * @return dmmTILERContPageAreaT*: pointer to the allocated area. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +struct dmmTILERContPageAreaT *alloc_2d_area( + struct dmmTILERContCtxT *dmmTilerCtx, + struct dmmTILERContPageAreaT *areaReq); + +/* ========================================================================== */ +/** + * deAlloc2DArea() + * + * @brief Deletes a 2D area from the TILER context. + * + * @param tlrCtx - dmmTILERContCtxT* - [in] TILER context structure. + * + * @param areaRem - dmmTILERContPageAreaT* - [in] Area to remove. + * + * @return int: True if the specified area is successfuly deleted. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +enum MSP_BOOL dealloc_2d_area(struct dmmTILERContCtxT *dmmTilerCtx, + struct dmmTILERContPageAreaT *areaRem); + +/* ========================================================================== */ +/** + * search2DArea() + * + * @brief Deletes a 2D area from the TILER context. + * + * @param tlrCtx - dmmTILERContCtxT* - [in] TILER context structure. + * + * @param X - signed long - [in] X coordinate of the search area. + * + * @param Y - signed long - [in] X coordinate of the search area. + * + * @param xInvert - int - [in] X coordinate is inverted. + * + * @param yInvert - int - [in] Y coordinate is inverted. + * + * @return dmmTILERContPageAreaT*: pointer to the discovered 2D area + * or NULL if no such area is found. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +struct dmmTILERContPageAreaT *search_2d_area( + struct dmmTILERContCtxT *dmmTilerCtx, + signed long X, + signed long Y, + enum MSP_BOOL xInvert, + enum MSP_BOOL yInvert); + +#endif /* _DMM_2D_ALLOC_H */ + diff --git a/drivers/media/video/tiler/dmm_def.h b/drivers/media/video/tiler/dmm_def.h new file mode 100644 index 000000000000..f9ae0a17c8f7 --- /dev/null +++ b/drivers/media/video/tiler/dmm_def.h @@ -0,0 +1,302 @@ +/* + * dmm_def.h + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _DMM_DEF_H +#define _DMM_DEF_H + +#include "tiler.h" + +#define DMM_4KiB_SIZE (4096) +#define DMM_TILER_CONT_WIDTH_8 (16384) +#define DMM_TILER_CONT_WIDTH_16 (16384) +#define DMM_TILER_CONT_WIDTH_32 (8192) +#define DMM_TILER_CONT_HEIGHT_8 (8192) +#define DMM_TILER_CONT_HEIGHT_16 (4096) +#define DMM_TILER_CONT_HEIGHT_32 (4096) + +/** @struc MSP_Dmm_eventNotificationT +* Structure defining Dmm driver context for user event notification +* (user callback and application specific data pointer. */ +struct MSP_Dmm_eventNotificationT { + void *hMSP; + void *usrAppData; +}; + +/** @enum errorCodeT +* Defining enumarated identifiers for general dmm driver errors. */ +enum errorCodeT { + DMM_NO_ERROR, + DMM_WRONG_PARAM, + DMM_HRDW_CONFIG_FAILED, + DMM_HRDW_NOT_READY, + DMM_SYS_ERROR +}; + +/** @enum dmmPATStatusErrT +* Defining enumarated identifiers for PAT area status error field. */ +enum dmmPATStatusErrT { + NO_ERROR = 0x0, + INVALID_DESCR = 0x1, + INVALID_DATA_PTR = 0x2, + UNEXP_AREA_UPDATE = 0x4, + UNEXP_CONTROL_UPDATE = 0x8, + UNEXP_DATA_UPDATE = 0x10, + UNEXP_ACCESS = 0x20 +}; + +/** @struc dmmPATStatusT +* Structure defining PAT area status. */ +struct dmmPATStatusT { + enum dmmPATStatusErrT error; + unsigned char ready; + unsigned char validDescriptor; + unsigned char engineRunning; + unsigned char done; + unsigned char linkedReconfig; + unsigned char remainingLinesCounter; +}; + +/** @enum dmmMemSectionSizeT +* Defining enumarated identifiers for memory section sizes used by LISA. */ +enum dmmMemSectionSizeT { + SEC_16MiB, + SEC_32MiB, + SEC_64MiB, + SEC_128MiB, + SEC_256MiB, + SEC_512MiB, + SEC_1GiB, + SEC_2GiB +}; + +/** @enum dmmMemSdrcIntlModeT +* Defining enumarated identifiers for memory section interleaving +* used by LISA.*/ +enum dmmMemSdrcIntlModeT { + SEC_INTL_NONE, + SEC_INTL_128B, + SEC_INTL_256B, + SEC_INTL_512B +}; + +/** @enum dmmMemSectionMappingT +* Defining enumarated identifiers for memory section mapping used by LISA. */ +enum dmmMemSectionMappingT { + SEC_UNMAPPED, + SEC_MAPPED_SDRC0, + SEC_MAPPED_SDRC1, + SEC_MAPPED_INTL +}; + +/** @struc dmmLISAConfigT +* Structure defining LISA memory map configuration. */ +struct dmmLISAConfigT { + signed long lisaMemMapIndx; + unsigned long sysAddr; + enum dmmMemSectionSizeT sysSize; + enum dmmMemSdrcIntlModeT sdrcIntl; + unsigned long sdrcAddrspc; + enum dmmMemSectionMappingT sdrcMap; + unsigned long sdrcAddr; +}; + +/** @struc dmmLISAConfigLstT +* Structure defining LISA memory map configuration linked list. */ +struct dmmLISAConfigLstT { + struct dmmLISAConfigLstT *nextConf; + struct dmmLISAConfigT mapConf; +}; + +/** @enum dmmMemoryAccessT +* Defining enumarated identifiers for memory memory access types through TILER +* and PAT. */ +enum dmmMemoryAccessT { + MODE_8_BIT, + MODE_16_BIT, + MODE_32_BIT, + MODE_PAGE +}; + +/** @enum dmmPATTranslationT +* Defining enumarated identifiers for possible PAT address translation +* schemes. */ +enum dmmPATTranslationT { + DIRECT, + INDIRECT +}; + +/** @enum dmmPATEngineAccessT +* Defining enumarated identifiers for possible PAT engines memory access +* schemes. */ +enum dmmPATEngineAccessT { + NORMAL_MODE, + DIRECT_LUT +}; + +/** @enum dmmPATRefillMethodT +* Defining enumarated identifiers for possible PAT area refill methods. */ +enum dmmPATRefillMethodT { + MANUAL, + AUTO +}; + +/** @struc PATAreaT +* Structure defining PAT page-area register. */ +struct PATAreaT { + int x0:8; + int y0:8; + int x1:8; + int y1:8; +}; + +/** @struc PATCtrlT +* Structure defining PAT control register. */ +struct PATCtrlT { + int start:4; + int direction:4; + int lutID:8; + int sync:12; + int initiator:4; +}; + +/** @struc PATDescrT +* Structure defining PAT area descriptor, needed for area refill procedures. */ +struct PATDescrT { + struct PATDescrT *nextPatEntry; + struct PATAreaT area; + struct PATCtrlT ctrl; + unsigned long data; +}; + +/** @struc dmmPATIrqEventsT +* Structure defining PAT interrupt events. */ +struct dmmPATIrqEventsT { + unsigned char irqAreaSelect; + unsigned char lutMiss; + unsigned char updData; + unsigned char updCtrl; + unsigned char updArea; + unsigned char invData; + unsigned char invDsc; + unsigned char fillLst; + unsigned char fillDsc; +}; + +/** @struc dmmPatIrqConfigT +* Structure defining PAT interrupt configuration. */ +struct dmmPatIrqConfigT { + struct dmmPATIrqEventsT irqEvnts; + int clrEvents; +}; + +/** @struc dmmPATIrqConfigLstT +* Structure defining PAT interrupt configuration linked list. */ +struct dmmPATIrqConfigLstT { + struct dmmPATIrqConfigLstT *nextConf; + struct dmmPatIrqConfigT irqConf; +}; + +/** @struc dmmPATEngineConfigT +* Structure defining PAT engine configuration. */ +struct dmmPATEngineConfigT { + signed long dmmPatEngineSel; + enum dmmPATEngineAccessT engineMode; +} ; + +/** @struc dmmPATEngineConfigLstT +* Structure defining PAT engine configuration linked list. */ +struct dmmPATEngineConfigLstT { + struct dmmPATEngineConfigLstT *nextConf; + struct dmmPATEngineConfigT engineConf; +}; + +/** @struc dmmPATViewConfigT +* Structure defining PAT view configuration. */ +struct dmmPATViewConfigT { + signed long initiatorId; + signed long viewIndex; +}; + +/** @struc dmmPATViewConfigLstT +* Structure defining PAT alias view configuration linked list. */ +struct dmmPATViewConfigLstT { + struct dmmPATViewConfigLstT *nextConf; + struct dmmPATViewConfigT aliasViewConf; +}; + +/** @struc dmmPATViewMapConfigT +* Structure defining PAT view map configuration. */ +struct dmmPATViewMapConfigT { + unsigned long patViewMapIndx; + enum dmmMemoryAccessT memoryAccessMode; + unsigned long contX; + enum dmmPATTranslationT transType; + unsigned long dmmPATViewBase; +}; + +/** @struc dmmPATViewMapConfigLstT +* Structure defining PAT view map configuration linked list. */ +struct dmmPATViewMapConfigLstT { + struct dmmPATViewMapConfigLstT *nextConf; + struct dmmPATViewMapConfigT viewConf; +}; + +/** @struc dmmTILERConfigT +* Structure defining TILER alias view configuration. */ +struct dmmTILERConfigT { + signed long initiatorId; + struct dmmViewOrientT orient; +}; + +/** @struc dmmTILERConfigLstT +* Structure defining TILER alias view configuration linked list. */ +struct dmmTILERConfigLstT { + struct dmmTILERConfigLstT *nextConf; + struct dmmTILERConfigT aliasConf; +}; + +/** @struc dmmPEGConfigT +* Structure defining PEG priority configuration. */ +struct dmmPEGConfigT { + signed long initiatorId; + unsigned long prio; +}; + +/** @struc dmmPEGConfigLstT +* Structure defining PEG priority configuration linked list. */ +struct dmmPEGConfigLstT { + struct dmmPEGConfigLstT *nextConf; + struct dmmPEGConfigT prioConf; +}; + +/** @struc dmmPATStatusLstT +* Structure defining PAT area status linked list. */ +struct dmmPATStatusLstT { + struct dmmPATStatusLstT *nextConf; + signed long areaSelect; + struct dmmPATStatusT patAreaStatus; +}; + +/** @enum MSP_Dmm_Phy2VirtOpsT +* Defining all of the control commands related to physical to +* virtual transforms. */ +/*enum MSP_Dmm_Phy2VirtOpsT +{ + DMM_P2V_SWAP +}*/ + +#endif /* _DMM_DEF_H */ + diff --git a/drivers/media/video/tiler/dmm_drv.h b/drivers/media/video/tiler/dmm_drv.h new file mode 100644 index 000000000000..82460ee0065e --- /dev/null +++ b/drivers/media/video/tiler/dmm_drv.h @@ -0,0 +1,801 @@ +/* + * dmm_drv.h + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _DMM_DRV_H +#define _DMM_DRV_H + +#include "dmm_def.h" + +/* ========================================================================== */ +/** + * dmm_tiler_alias_orientation_set() + * + * @brief Set specific TILER alias orientation setting per initiator ID + * (alias view). + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which alias view will be editted. + * + * @param viewOrientation - dmmViewOrientT - [in] New alias view orientation + * setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmViewOrientT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_alias_orientation_set(signed long initiatorID, + struct dmmViewOrientT viewOrientation); + +/* ========================================================================== */ +/** + * dmm_tiler_alias_orientation_get() + * + * @brief Set specific TILER alias orientation setting per initiator ID + * (alias view). + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which alias view will be editted. + * + * @param viewOrientation - dmmViewOrientT* - [out] Pointer to write alias view + * orientation setting to. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmViewOrientT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_alias_orientation_get(signed long initiatorID, + struct dmmViewOrientT *viewOrientation); + +/* ========================================================================== */ +/** + * dmm_peg_priority_set() + * + * @brief Set specific PEG priority setting per initiator ID. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which priority will be editted. + * + * @param prio - unsigned long - [in] New priority setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_peg_priority_set(signed long initiatorID, + unsigned long prio); + +/* ========================================================================== */ +/** + * dmm_peg_priority_get() + * + * @brief Get specific PEG priority setting per initiator ID. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which priority will be editted. + * + * @param prio - unsigned long* - [out] Poitner to write the priority setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_peg_priority_get(signed long initiatorID, + unsigned long *prio); + +/* ========================================================================== */ +/** + * dmm_pat_area_refill() + * + * @brief Initiate a PAT area refill (or terminate an ongoing - consult + * documentation). + * + * @param patDesc - PATDescrT* - [in] Pointer to a PAT area descriptor that's + * needed to extract settings from for the refill procedure initation. + * + * @param dmmPatAreaSel - signed long - [in] Selects which PAT area will be + * configured for a area refill procedure. + * + * @param refillType - dmmPATRefillMethodT - [in] Selects the refill method - + * manual or automatic. + * + * @param forcedRefill - int - [in] Selects if forced refill should be used + * effectively terminating any ongoing area refills related to the selected + * area. + * + * @return errorCodeT + * + * @pre If forced mode is not used, no refills should be ongoing for the + * selected + * area - error status returned if this occurs. + * + * @post If non valid data is provided for patDesc and the refill engines fail + * to perform the request, an error status is returned. + * + * @see errorCodeT, PATDescrT, dmmPATRefillMethodT, int for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_area_refill(struct PATDescrT *patDesc, + signed long dmmPatAreaSel, + enum dmmPATRefillMethodT refillType, + int forcedRefill); + +/* ========================================================================== */ +/** + * dmm_pat_refill_area_status_get() + * + * @brief Gets the status for the selected PAT area. + * + * @param dmmPatAreaSel - signed long - [in] Selects which PAT area status will + * be queried. + * + * @param areaStatus - dmmPATStatusT* - [out] Structure containing the PAT area + * status that will be filled by dmmPatRefillAreaStatusGet(). + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post If the query fails the provided areaStatus structure is not updated at + * all! + * + * @see errorCodeT, dmmPATStatusT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_refill_area_status_get(signed long dmmPatAreaStatSel, + struct dmmPATStatusT *areaStatus); + +/* ========================================================================== */ +/** + * dmm_pat_refill_engine_config() + * + * @brief Configure the selected PAT refill engine. + * + * @param dmmPatEngineSel - signed long - [in] Selects which PAT engine will + * be configured. + * + * @param engineMode - dmmPATEngineAccessT - [in] New engine mode. + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATEngineAccessT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_refill_engine_config(signed long dmmPatEngineSel, + enum dmmPATEngineAccessT engineMode); + +/* ========================================================================== */ +/** + * dmm_pat_refill_engine_config_get() + * + * @brief Get the selected PAT refill engine configuration. + * + * @param dmmPatEngineSel - signed long - [in] Selects which PAT engine will be + * configured. + * + * @param engineMode - dmmPATEngineAccessT* - [out] Pointer to write the engine + * mode. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATEngineAccessT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_refill_engine_config_get(signed long dmmPatEngineSel, + enum dmmPATEngineAccessT *engineMode); + +/* ========================================================================== */ +/** + * dmm_pat_view_set() + * + * @brief Set specific PAT initiator view settings. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which PAT view will be editted. + * + * @param patViewIndx - signed long - [in] New view map setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_view_set(signed long initiatorID, + signed long patViewIndx); + +/* ========================================================================== */ +/** + * dmm_pat_view_get() + * + * @brief Get specific PAT initiator view settings. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which PAT view will be editted. + * + * @param patViewIndx - signed long* - [out] Pointer to write the view map + * setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_view_get(signed long initiatorID, + signed long *patViewIndx); + +/* ========================================================================== */ +/** + * dmm_pat_view_map_config() + * + * @brief Configure selected PAT view map. + * + * @param patViewMapIndx - signed long - [in] Index of the selected PAT view + * map. + * + * @param memoryAccessMode - dmmMemoryAccessT - [in] Type of memory access to + * perform through this view. + * + * @param contX - unsigned long - [in] CONT_x register value. + * + * @param transType - dmmPATTranslationT - [in] Address translation schemes. + * + * @param dmmPATViewBase - unsigned long - [in] View map base address + * (31-bit only considered). + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemoryAccessT, dmmPATTranslationT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_view_map_config(signed long patViewMapIndx, + enum dmmMemoryAccessT memoryAccessMode, + unsigned long contX, + enum dmmPATTranslationT transType, + unsigned long dmmPATViewBase); + +/* ========================================================================== */ +/** + * dmm_pat_view_map_config_get() + * + * @brief Get selected PAT view map configuration. + * + * @param patViewMapIndx - signed long - [in] Index of the selected PAT view + * map. + * + * @param memoryAccessMode - dmmMemoryAccessT - [int] Type of memory access + * that configuration is queried. + * + * @param contX - unsigned long* - [out] Pointer to write the CONT_x register + * value. + * + * @param transType - dmmPATTranslationT* - [out] Pointer to write the + * address translation schemes. + * + * @param dmmPATViewBase - unsigned long* - [out] Pointer to write the view + * map base address. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemoryAccessT, dmmPATTranslationT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_view_map_config_get(signed long patViewMapIndx, + enum dmmMemoryAccessT memoryAccessMode, + unsigned long *contX, + enum dmmPATTranslationT *transType, + unsigned long *dmmPATViewBase); + +/* ========================================================================== */ +/** + * dmm_lisa_memory_map_config() + * + * @brief Configure selected LISA memory map. + * + * @param lisaMemMapIndx - signed long - [in] Index of the selected LISA memory + * map. + * + * @param sysSize - dmmMemSectionSizeT - [in] Size of the memory section. + * + * @param sdrcIntl - dmmMemSdrcIntlModeT - [in] SDRAM controller interleaving + * mode + * + * @param sdrcAddrspc - unsigned long - [in] SDRAM controller address space. + * + * @param sdrcMap - dmmMemSectionMappingT - [in] SDRAM controller mapping. + * + * @param sdrcAddr - unsigned long - [in] SDRAM controller address MSB. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemSectionSizeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_lisa_memory_map_config(signed long lisaMemMapIndx, + unsigned long sysAddr, + enum dmmMemSectionSizeT sysSize, + enum dmmMemSdrcIntlModeT sdrcIntl, + unsigned long sdrcAddrspc, + enum dmmMemSectionMappingT sdrcMap, + unsigned long sdrcAddr); + +/* ========================================================================== */ +/** + * dmm_lisa_memory_map_config_get() + * + * @brief Get selected LISA memory map configuration. + * + * @param lisaMemMapIndx - signed long - [in] Index of the selected LISA + * memory map. + * + * @param sysSize - dmmMemSectionSizeT* - [out] Pointer to write size of + * the memory section. + * + * @param sdrcIntl - dmmMemSdrcIntlModeT* - [out] Pointer to write SDRAM + * controller interleaving mode. + * + * @param sdrcAddrspc - unsigned long* - [out] Pointer to write SDRAM + * controller address space. + * + * @param sdrcMap - dmmMemSectionMappingT* - [out] Pointer to write SDRAM + * controller mapping. + * + * @param sdrcAddr - unsigned long* - [out] Pointer to write SDRAM + * controller address MSB. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemSectionSizeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_lisa_memory_map_config_get(signed long lisaMemMapIndx, + unsigned long *sysAddr, + enum dmmMemSectionSizeT *sysSize, + enum dmmMemSdrcIntlModeT *sdrcIntl, + unsigned long *sdrcAddrspc, + enum dmmMemSectionMappingT *sdrcMap, + unsigned long *sdrcAddr); + +/* ========================================================================== */ +/** + * dmm_engage_lisa_lock() + * + * @brief Sets the LISA lock register preventing further writting to the + * LISA memory map registers. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post Only a software reset of the DMM module can clear the lock bit. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_engage_lisa_lock(void); + +/* ========================================================================== */ +/** + * dmm_sys_config_set() + * + * @brief DMM clock management configuration. + * + * @param dmmIdleMode - unsigned long - [in] Idle mode. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post Only a software reset of the DMM module can clear the lock bit. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_sys_config_set(unsigned long dmmIdleMode); + +/* ========================================================================== */ +/** + * dmm_sys_config_get() + * + * @brief DMM clock management configuration. + * + * @param dmmIdleMode - unsigned long* - [out] Idle mode status. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_sys_config_get(unsigned long *dmmIdleMode); + +/* ========================================================================== */ +/** + * dmm_pat_irq_mode_set() + * + * @brief Enable/disable PAT interrupt events. + * + * @param patIrqEvnt - dmmPATIrqEventsT* - [in] Interrupt events to set. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATIrqEventsT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_irq_mode_set(struct dmmPATIrqEventsT *patIrqEvnt); + +/* ========================================================================== */ +/** + * dmm_pat_irq_mode_get() + * + * @brief Enable/disable PAT interrupt events. + * + * @param patIrqEvnt - dmmPATIrqEventsT* - [in/out] Get interrupt events masks. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATIrqEventsT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_irq_mode_get(struct dmmPATIrqEventsT *patIrqEvnt); + +/* ========================================================================== */ +/** + * dmm_pat_irq_status_get() + * + * @brief Get the statuses of PAT interrupt events. + * + * @param patIrqEvnt - dmmPATIrqEventsT* - [in/out] Interrupt events statuses. + * + * @param clrEvents - int - [in] Celar events upon retrieval. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATIrqEventsT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_irq_status_get(struct dmmPATIrqEventsT *patIrqEvnt, + int clrEvents); + +/* ========================================================================== */ +/** + * dmm_module_config() + * + * @brief Configure selected DMM modules. + * + * @param tilerConf - dmmTILERConfigLstT* - [in] A NULL termintated linked list + * of all configurations for the specific DMM module or NULL if not to be + * configured. + * + * @param pegConf - dmmPEGConfigLstT* - [in] A NULL termintated linked list + * of all configurations for the specific DMM module or NULL if not to be + * configured. + * + * @param lisaConf - dmmLISAConfigLstT* - [in] A NULL termintated linked list + * of all configurations for the specific DMM module or NULL if not to be + * configured. + * + * @param patEngineConf - dmmPATEngineConfigLstT* - [in] A NULL termintated + * linked list of all configurations for the specific DMM module or NULL if + * not to be configured. + * + * @param patViewCOnf - dmmPATViewConfigLstT* - [in] A NULL termintated + * linked list of all configurations for the specific DMM module or NULL + * if not to be configured. + * + * @param patViewMapConf - dmmPATViewMapConfigLstT* - [in] A NULL termintated + * linked list of all configurations for the specific DMM module or NULL if + * not to be configured. + * + * @param dmmSysConfig - unsigned long* - [in] Clock configuration of DMM, + * NULL if not to be set. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERConfigLstT, dmmPEGConfigLstT, + * dmmLISAConfigLstT, dmmPATEngineConfigLstT, dmmPATViewConfigLstT, + * dmmPATViewMapConfigLstT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_module_config(struct dmmTILERConfigLstT *tilerConf, + struct dmmPEGConfigLstT *pegConf, + struct dmmLISAConfigLstT *lisaConf, + struct dmmPATEngineConfigLstT *patEngineConf, + struct dmmPATViewConfigLstT *patViewCOnf, + struct dmmPATViewMapConfigLstT *patViewMapConf, + unsigned long *dmmSysConfig); + +/* ========================================================================== */ +/** + * dmm_module_get_config() + * + * @brief Read the configuration of selected DMM modules. + * + * @param tilerConf - dmmTILERConfigLstT* - [out] A NULL termintated linked list + * of all configurations for the specific DMM module to be read to. + * + * @param pegConf - dmmPEGConfigLstT* - [out] A NULL termintated linked list + * of all configurations for the specific DMM module to be read to. + * + * @param lisaConf - dmmLISAConfigLstT* - [out] A NULL termintated linked list + * of all configurations for the specific DMM module to be read to. + * + * @param patEngineConf - dmmPATEngineConfigLstT* - [out] A NULL termintated + * linked list of all configurations for the specific DMM module to be read to. + * + * @param patViewCOnf - dmmPATViewConfigLstT* - [out] A NULL termintated linked + * list of all configurations for the specific DMM module to be read to. + * + * @param patViewMapConf - dmmPATViewMapConfigLstT* - [out] A NULL termintated + * linked list of all configurations for the specific DMM module to be read to. + * + * @param patAreaStatus - dmmPATStatusLstT* - [out] A NULL termintated linked + * list of all PAT area status' to be read. + * + * @param dmmSysConfig - unsigned long* - [out] Clock configuration of DMM, + * NULL if not to be read. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERConfigLstT, dmmPEGConfigLstT, + * dmmLISAConfigLstT, dmmPATEngineConfigLstT, dmmPATViewConfigLstT, + * dmmPATViewMapConfigLstT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_module_get_config(struct dmmTILERConfigLstT *tilerConf, + struct dmmPEGConfigLstT *pegConf, + struct dmmLISAConfigLstT *lisaConf, + struct dmmPATEngineConfigLstT *patEngineConf, + struct dmmPATViewConfigLstT *patViewCOnf, + struct dmmPATViewMapConfigLstT *patViewMapConf, + struct dmmPATStatusLstT *patAreaStatus, + unsigned long *dmmSysConfig); + +/* ========================================================================== */ +/** + * dmm_pat_irq_config_set() + * + * @brief Configures PAT interrupt masks. + * + * @param irqMaskConf - dmmPATIrqConfigLstT* - [in] A NULL termintated linked + * list of all interrupt masks. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATIrqConfigLstT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_irq_config_set(struct dmmPATIrqConfigLstT *irqMaskConf); + +/* ========================================================================== */ +/** + * dmm_pat_irq_config_get() + * + * @brief Configures PAT interrupt masks. + * + * @param irqMaskConf - dmmPATIrqConfigLstT* - [in/out] A NULL termintated + * linked list of all interrupt masks. + * + * @param irqStatusConf - dmmPATIrqConfigLstT* - [in/out] A NULL termintated + * linked list of all interrupt statuses to read. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATIrqConfigLstT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_irq_config_get(struct dmmPATIrqConfigLstT *irqMaskConf, + struct dmmPATIrqConfigLstT *irqStatusConf); + +/* ========================================================================== */ +/** + * dmm_instance_init() + * + * @brief Initializes the Tiler cotnext. + * + * @param dmmInstanceCtxPtr - void * - [in] Tiler context instance. + * + * @param contXSize - signed long - [in] Tiler container width. + * + * @param contYSize - signed long - [in] Tiler container height. + * + * @param hMSP - MSP_HANDLE - [in] MSP handle related to this dmm_drv cotnext. + * + * @param usrAppData - void * - [in] Pointer to user specific data structure. + * + * @param usrCallback - MSP_usrCallback - [in] Pointer to callback supplied by + * the user for notificiation events (interupts). + * + * @return int True if operation succeded. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +int dmm_instance_init(void *dmmInstanceCtxPtr, + signed long contXSize, + signed long contYSize, + void *hMSP, + void *usrAppData); + +/* ========================================================================== */ +/** + * dmm_instance_deinit() + * + * @brief Deinitializes the Tiler cotnext. + * + * @param dmmInstanceCtxPtr - void * - [in] Tiler context instance. + * + * @return int True if operation succeded. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +int dmm_instance_deinit(void *dmmInstanceCtxPtr); + +/* ========================================================================== */ +/** + * dmm_copy2tiler_alias_view() + * + * @brief Auxiliary function for copying data to the Tiler alias view. + * + * @param destPtr - void * - [in] Destination pointer in Tiler alias view. + * + * @param srcPtr - void * - [in] Data source pointer. + * + * @param width - signed long - [in] Data width. + * + * @param height - signed long - [in] Data height. + * + * @param stride - signed long - [in] Data stride. + * + * @param accType - dmmMemoryAccessT - [in] Tiler memory view access type. + * + * @return errorCodeT error if event can't be signaled. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_copy2tiler_alias_view(void *destPtr, + void *srcPtr, + signed long width, + signed long height, + signed long stride, + enum dmmMemoryAccessT accType); + +/* ========================================================================== */ +/** + * dmm_virtual_buffer_manipulations() + * + * @brief Manipulates virtual buffers. + * + * @param dmmInstanceCtxPtr - void * - [in] Dmm context instance. + * + * @param sysPtr - void * - [in] Tiler system pointer to a 2D area. + * + * @param patOp - MSP_Dmm_Phy2VirtOpsT - [in] Refill operaion to perform. + * + * @param affectedArea - PATAreaT* - [in] Area that will be affected. + * + * @param destinationArea - PATAreaT* - [in] Destination coordinates. + * + * @return void * pointer to the area targeted by the operation. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +void *dmm_virtual_buffer_manipulations(void *dmmInstanceCtxPtr, + void *sysPtr, + struct PATAreaT *affectedArea, + struct PATAreaT *destinationArea); + +#endif /* _DMM_DRV_H */ + diff --git a/drivers/media/video/tiler/dmm_hl_drv.c b/drivers/media/video/tiler/dmm_hl_drv.c new file mode 100755 index 000000000000..6cea19cb821a --- /dev/null +++ b/drivers/media/video/tiler/dmm_hl_drv.c @@ -0,0 +1,1456 @@ +/* + * dmm_hl_drv.c + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> /* vmalloc_to_page */ +#include <linux/mmzone.h> /* __page_to_phys */ +#include "dmm_def.h" +#include "dmm_2d_alloc.h" +#include "dmm_prv.h" +#include "tiler.h" + +#define __NEWCODE__ + +#ifdef __NEWCODE__ +#include <linux/io.h> +#include <linux/sched.h> /* current->mm */ + +static unsigned long get_phys_addr(unsigned long arg) +{ + pgd_t *pgd = NULL; + pmd_t *pmd = NULL; + pte_t *ptep = NULL, pte = 0x0; + pgd = pgd_offset(current->mm, arg); + if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { + pmd = pmd_offset(pgd, arg); + if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { + ptep = pte_offset_map(pmd, arg); + if (ptep) { + pte = *ptep; + if (pte_present(pte)) { + return (pte & PAGE_MASK) | + (~PAGE_MASK & arg); + } + } + } + } + return 0x0; /* va not in page table */ +} +#endif + +/* ========================================================================== */ +/** + * dmm_module_config() + * + * @brief Configure selected DMM modules. + * + * @param tilerConf - dmmTILERConfigLstT* - [in] A NULL termintated linked list + * of all configurations for the specific DMM module or NULL if not to be + * configured. + * + * @param pegConf - dmmPEGConfigLstT* - [in] A NULL termintated linked list + * of all configurations for the specific DMM module or NULL if not to be + * configured. + * + * @param lisaConf - dmmLISAConfigLstT* - [in] A NULL termintated linked list + * of all configurations for the specific DMM module or NULL if not to be + * configured. + * + * @param patEngineConf - dmmPATEngineConfigLstT* - [in] A NULL termintated + * linked list + * of all configurations for the specific DMM module or NULL if not to be + * configured. + * + * @param patViewCOnf - dmmPATViewConfigLstT* - [in] A NULL termintated + * linked list of all configurations for the specific DMM module or NULL if + * not to be configured. + * + * @param patViewMapConf - dmmPATViewMapConfigLstT* - [in] A NULL + * termintated linked list of all configurations for the specific DMM module + * or NULL if not to be configured. + * + * @param dmmSysConfig - unsigned long* - [in] Clock configuration of DMM, + * NULL if not to be set. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERConfigLstT, dmmPEGConfigLstT, + * dmmLISAConfigLstT, dmmPATEngineConfigLstT, dmmPATViewConfigLstT, + * dmmPATViewMapConfigLstT for further detail. + */ +/* ========================================================================== */ +/* DHS */ +enum errorCodeT dmm_module_config(struct dmmTILERConfigLstT *tilerConf, + struct dmmPEGConfigLstT *pegConf, + struct dmmLISAConfigLstT *lisaConf, + struct dmmPATEngineConfigLstT *patEngineConf, + struct dmmPATViewConfigLstT *patViewCOnf, + struct dmmPATViewMapConfigLstT *patViewMapConf, + unsigned long *dmmSysConfig) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + + while (tilerConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_tiler_alias_orientation_set( + tilerConf->aliasConf.initiatorId, + tilerConf->aliasConf.orient); + tilerConf = tilerConf->nextConf; + } + + while (pegConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_peg_priority_set(pegConf->prioConf.initiatorId, + pegConf->prioConf.prio); + pegConf = pegConf->nextConf; + } + + while (lisaConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_lisa_memory_map_config( + lisaConf->mapConf.lisaMemMapIndx, + lisaConf->mapConf.sysAddr, + lisaConf->mapConf.sysSize, + lisaConf->mapConf.sdrcIntl, + lisaConf->mapConf.sdrcAddrspc, + lisaConf->mapConf.sdrcMap, + lisaConf->mapConf.sdrcAddr); + lisaConf = lisaConf->nextConf; + } + + while (patEngineConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_refill_engine_config( + patEngineConf->engineConf.dmmPatEngineSel, + patEngineConf->engineConf.engineMode); + patEngineConf = patEngineConf->nextConf; + } + + while (patViewCOnf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_view_set(patViewCOnf->aliasViewConf.initiatorId, + patViewCOnf->aliasViewConf.viewIndex); + patViewCOnf = patViewCOnf->nextConf; + } + + while (patViewMapConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_view_map_config( + patViewMapConf->viewConf.patViewMapIndx, + patViewMapConf->viewConf.memoryAccessMode, + patViewMapConf->viewConf.contX, + patViewMapConf->viewConf.transType, + patViewMapConf->viewConf.dmmPATViewBase); + patViewMapConf = patViewMapConf->nextConf; + } + + if (dmmSysConfig != NULL && eCode == DMM_NO_ERROR) + eCode = dmm_sys_config_set(*dmmSysConfig); + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_module_get_config() + * + * @brief Read the configuration of selected DMM modules. + * + * @param tilerConf - dmmTILERConfigLstT* - [out] A NULL termintated linked list + * of all configurations for the specific DMM module to be read to. + * + * @param pegConf - dmmPEGConfigLstT* - [out] A NULL termintated linked list + * of all configurations for the specific DMM module to be read to. + * + * @param lisaConf - dmmLISAConfigLstT* - [out] A NULL termintated linked list + * of all configurations for the specific DMM module to be read to. + * + * @param patEngineConf - dmmPATEngineConfigLstT* - [out] A NULL termintated + * linked list of all configurations for the specific DMM module to be read to. + * + * @param patViewCOnf - dmmPATViewConfigLstT* - [out] A NULL termintated + * linked list of all configurations for the specific DMM module to be read to. + * + * @param patViewMapConf - dmmPATViewMapConfigLstT* - [out] A NULL + * termintated linked list of all configurations for the specific DMM module + * to be read to. + * + * @param patAreaStatus - dmmPATStatusLstT* - [out] A NULL termintated + * linked list of all PAT area status' to be read. + * + * @param dmmSysConfig - unsigned long* - [in] Clock configuration of + * DMM, NULL if not to be read. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERConfigLstT, dmmPEGConfigLstT, + * dmmLISAConfigLstT, dmmPATEngineConfigLstT, dmmPATViewConfigLstT, + * dmmPATViewMapConfigLstT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_module_get_config(struct dmmTILERConfigLstT *tilerConf, + struct dmmPEGConfigLstT *pegConf, + struct dmmLISAConfigLstT *lisaConf, + struct dmmPATEngineConfigLstT *patEngineConf, + struct dmmPATViewConfigLstT *patViewCOnf, + struct dmmPATViewMapConfigLstT *patViewMapConf, + struct dmmPATStatusLstT *patAreaStatus, + unsigned long *dmmSysConfig) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + + while (tilerConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_tiler_alias_orientation_get( + tilerConf->aliasConf.initiatorId, + &(tilerConf->aliasConf.orient)); + tilerConf = tilerConf->nextConf; + } + + while (pegConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_peg_priority_get(pegConf->prioConf.initiatorId, + &(pegConf->prioConf.prio)); + pegConf = pegConf->nextConf; + } + + while (lisaConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_lisa_memory_map_config_get( + lisaConf->mapConf.lisaMemMapIndx, + &(lisaConf->mapConf.sysAddr), + &(lisaConf->mapConf.sysSize), + &(lisaConf->mapConf.sdrcIntl), + &(lisaConf->mapConf.sdrcAddrspc), + &(lisaConf->mapConf.sdrcMap), + &(lisaConf->mapConf.sdrcAddr)); + lisaConf = lisaConf->nextConf; + } + + while (patEngineConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_refill_engine_config_get( + patEngineConf->engineConf.dmmPatEngineSel, + &(patEngineConf->engineConf.engineMode)); + patEngineConf = patEngineConf->nextConf; + } + + while (patViewCOnf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_view_get( + patViewCOnf->aliasViewConf.initiatorId, + &(patViewCOnf->aliasViewConf.viewIndex)); + patViewCOnf = patViewCOnf->nextConf; + } + + while (patViewMapConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_view_map_config_get( + patViewMapConf->viewConf.patViewMapIndx, + patViewMapConf->viewConf.memoryAccessMode, + &(patViewMapConf->viewConf.contX), + &(patViewMapConf->viewConf.transType), + &(patViewMapConf->viewConf.dmmPATViewBase)); + patViewMapConf = patViewMapConf->nextConf; + } + + while (patAreaStatus != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_refill_area_status_get( + patAreaStatus->areaSelect, + &(patAreaStatus->patAreaStatus)); + + patAreaStatus = patAreaStatus->nextConf; + } + + if (dmmSysConfig != NULL && eCode == DMM_NO_ERROR) + eCode = dmm_sys_config_get(dmmSysConfig); + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_irq_config_set() + * + * @brief Configures PAT interrupt masks. + * + * @param irqMaskConf - dmmPATIrqConfigLstT* - [in] A NULL termintated linked + * list of all interrupt masks. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATIrqConfigLstT for further detail. + */ +/* ========================================================================== */ +/* DHS */ +enum errorCodeT dmm_pat_irq_config_set(struct dmmPATIrqConfigLstT *irqMaskConf) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + + while (irqMaskConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_irq_mode_set(&(irqMaskConf->irqConf.irqEvnts)); + irqMaskConf = irqMaskConf->nextConf; + } + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_irq_config_get() + * + * @brief Configures PAT interrupt masks. + * + * @param irqMaskConf - dmmPATIrqConfigLstT* - [in/out] A NULL termintated + * linked list of all interrupt masks. + * + * @param irqStatusConf - dmmPATIrqConfigLstT* - [in/out] A NULL termintated + * linked list of all interrupt statuses to read. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATIrqConfigLstT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_irq_config_get(struct dmmPATIrqConfigLstT *irqMaskConf, + struct dmmPATIrqConfigLstT *irqStatusConf) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + + while (irqMaskConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_irq_mode_get(&(irqMaskConf->irqConf.irqEvnts)); + irqMaskConf = irqMaskConf->nextConf; + } + + while (irqStatusConf != NULL && eCode == DMM_NO_ERROR) { + eCode = dmm_pat_irq_status_get( + &(irqStatusConf->irqConf.irqEvnts), + irqStatusConf->irqConf.clrEvents); + irqStatusConf = irqStatusConf->nextConf; + } + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_start_refill() + * + * @param dmmInstanceCtxPtr - void * - [in] Tiler context instance. + * + * @param bufferMappedZone - dmmTILERContPageAreaT* - [in] The tiled buffer + * descriptor. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +/* DHS */ +enum errorCodeT dmm_pat_start_refill( + struct dmmTILERContPageAreaT *bufferMappedZone) +{ + struct PATDescrT areaDesc; + + areaDesc.area.x0 = bufferMappedZone->x0 + bufferMappedZone->xPageOfst; + areaDesc.area.y0 = bufferMappedZone->y0 + bufferMappedZone->yPageOfst; + areaDesc.area.x1 = bufferMappedZone->x0 + bufferMappedZone->xPageOfst + + bufferMappedZone->xPageCount - 1; + areaDesc.area.y1 = bufferMappedZone->y0 + bufferMappedZone->yPageOfst + + bufferMappedZone->yPageCount - 1; + + areaDesc.ctrl.direction = 0; + areaDesc.ctrl.initiator = 0; + areaDesc.ctrl.lutID = 0; + areaDesc.ctrl.start = 1; + areaDesc.ctrl.sync = 0; + + areaDesc.nextPatEntry = NULL; + +#ifndef __NEWCODE__ + areaDesc.data = (unsigned long)(bufferMappedZone->patPageEntries); +#else + areaDesc.data = (unsigned long)bufferMappedZone->patPageEntriesSpace; +#endif + + tilerdump(__LINE__); + return dmm_pat_area_refill(&areaDesc, 0, MANUAL, 0); +} + +/* ========================================================================== */ +/** + * dmm_pat_phy2virt_mapping() + * + * @brief Mapping physical memory pages to virtual containers. + * + * @param bufferMappedZone - dmmTILERContPageAreaT* - [in] The tiled buffer + * descriptor. + * + * @param custmPagesPtr - void * - [in] Pointer to the custom supplied physical + * pages. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +/* DHS */ +enum errorCodeT dmm_pat_phy2virt_mapping( + struct dmmTILERContPageAreaT *bufferMappedZone, + void *custmPagesPtr) +{ + unsigned long bfrPages; + enum errorCodeT eCode = DMM_NO_ERROR; + + tilerdump(__LINE__); + bfrPages = + (bufferMappedZone->xPageCount)*(bufferMappedZone->yPageCount); + + if (bfrPages == 0) { + eCode = DMM_SYS_ERROR; + } else { + tilerdump(__LINE__); +#ifdef __NEWCODE__ + unsigned long order = 0x0; + + order = ((bfrPages*4 + 16) + 4095) / 4096; + + order = 5; + debug(bfrPages*4 + 16); + debug(order); + + bufferMappedZone->page_list = NULL; + bufferMappedZone->page_list = + (struct page *)alloc_pages(GFP_DMA, order); + if (!bufferMappedZone->page_list) { + debug(__LINE__); return DMM_SYS_ERROR; + } + bufferMappedZone->patPageEntriesSpace = + (unsigned long *)page_to_phys(bufferMappedZone->page_list); + + bufferMappedZone->page_list_virt = + ioremap((unsigned long)bufferMappedZone->patPageEntriesSpace, + 0x1000 * 32); /* TODO: don't forget to unmap later */ + + bufferMappedZone->patPageEntries = + bufferMappedZone->page_list_virt; + memset(bufferMappedZone->patPageEntries, 0x0, 0x1000 * 32); + bufferMappedZone->patPageEntries = + (unsigned long *)((((unsigned long) + bufferMappedZone->patPageEntries) + 15) & ~15); +#endif + +#ifndef __NEWCODE__ + if (dmm_tiler_populate_pat_page_entry_data(bfrPages, + &(bufferMappedZone->patPageEntries), + &(bufferMappedZone->patPageEntriesSpace), + custmPagesPtr + ) != DMM_NO_ERROR) { +#else + if (dmm_tiler_populate_pat_page_entry_data(bfrPages, + NULL, + NULL, + (void *)bufferMappedZone->patPageEntries + ) != DMM_NO_ERROR) { +#endif + eCode = DMM_SYS_ERROR; + return eCode; + } + + if (custmPagesPtr != NULL) + bufferMappedZone->patCustomPages = 1; + else + bufferMappedZone->patCustomPages = 0; + tilerdump(__LINE__); + eCode = dmm_pat_start_refill(bufferMappedZone); + } + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_tiler_populate_pat_page_entry_data() + * + * @brief Populates an aray with PAT page address entries. + * + * @param bfrSize - unsigned long - [in] Size of the buffer which will be used + * to generate page entries for. + * + * @param pageEntries - unsigned long** - [out] The address to the allocated + * page entry data array, aligned due to hardware specification. + * + * @param pageEntriesSpace - unsigned long** - [out] The address to the + * allocated page entry data array for deallocation purposes. + * + * @param custmPagesPtr - void * - [in] Pointer to a custom created memory + * pages list. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_populate_pat_page_entry_data(unsigned long numPages, + unsigned long **pageEntries, + unsigned long **pageEntriesSpace, + void *custmPagesPtr) +{ + signed long iter; + unsigned long *patAreaEntries = NULL; + + tilerdump(__LINE__); + +#ifndef __NEWCODE__ + patAreaEntries = kmalloc( + (numPages*4 + 16), GFP_KERNEL); + /* Must be 16-byte aligned. */ + memset(patAreaEntries, 0x0, (numPages*4 + 16)); + *pageEntriesSpace = patAreaEntries; +#else + +#if 0 /* move to caller */ + struct page *page = NULL; + unsigned long *ioaddr = NULL; + unsigned long pa = 0x0; + unsigned long order = 0x0; + + order = ((numPages*4 + 16) + 4095) / 4096; + debug(numPages*4 + 16); + debug(order); + + /* page = (struct page *)alloc_page(GFP_DMA); */ + /*pa = page_to_phys(page);*/ + /*ioaddr = ioremap(pa, 0x1000);*/ + /*memset(ioaddr, 0x0, 0x1000);*/ + + page = (struct page *)alloc_pages(GFP_DMA, order); + if (!page) + return DMM_SYS_ERROR; + pa = page_to_phys(page); + ioaddr = ioremap(pa, 0x1000 * order); + memset(ioaddr, 0x0, 0x1000 * order); + + patAreaEntries = ioaddr; + *pageEntriesSpace = (unsigned long *)pa; + debug(*pageEntriesSpace); + debug(patAreaEntries); +#endif + +#if 0 /* not a good solution to use vmalloc */ + patAreaEntries = (unsigned long *)vmalloc(numPages*4 + 16); + memset(patAreaEntries, 0x0, numPages*4 + 16); + *pageEntriesSpace = patAreaEntries; + + debug(*pageEntriesSpace); + debug(patAreaEntries); + + /* test different allocation methods */ + unsigned long mem = 0x0; + struct page *pg = NULL; + unsigned long *io = NULL; + + pg = alloc_page(GFP_DMA); + mem = page_to_phys(pg); + io = ioremap(mem, 0x1000); + memset(io, 0x0, 0x1000); + debug(pg); + debug(mem); + debug(get_phys_addr(mem)); + debug(io); + iounmap(io); + __free_page(pg); + + mem = (unsigned long)kmalloc(0x1000, GFP_DMA); + debug(mem); + debug(get_phys_addr(mem)); + memset((unsigned long *)mem, 0x0, 0x1000); + kfree((void *)mem); + + mem = (unsigned long)vmalloc(0x1000); + debug(mem); + debug(get_phys_addr(mem)); + memset((unsigned long *)mem, 0x0, 0x1000); + vfree((void *)mem); + + mem = __get_free_page(GFP_DMA); + debug(mem); + debug(get_phys_addr(mem)); + memset((unsigned long *)mem, 0x0, 0x1000); + free_page(mem); +#endif +#endif + +#ifndef __NEWCODE__ + patAreaEntries = (unsigned long *) + ((((unsigned long)patAreaEntries) + 15) & ~15); +#else + patAreaEntries = (unsigned long *)custmPagesPtr; +#endif + + +#ifdef __NEWCODE__ + debug(patAreaEntries); + debug(numPages); +#endif + +#ifndef __NEWCODE__ + if (custmPagesPtr == NULL) { + for (iter = 0; iter < numPages; iter++) { + patAreaEntries[iter] = + (unsigned long)dmm_get_phys_page(); + if (patAreaEntries[iter] == 0x0) + return DMM_SYS_ERROR; + } + } else { + for (iter = 0; iter < numPages; iter++) { + patAreaEntries[iter] = (unsigned long) + (((unsigned long *)custmPagesPtr)[iter]); + } + } +#else +/* using custmPagesPtr arguement in a diffent way, so remove the conditions */ + for (iter = 0; iter < numPages; iter++) { + patAreaEntries[iter] = + (unsigned long)dmm_get_phys_page(); + if (patAreaEntries[iter] == 0x0) + return DMM_SYS_ERROR; + } +#endif + +#ifdef __NEWCODE__ + debug(__LINE__); + debug(patAreaEntries[0]); + debug(patAreaEntries[1]); + debug(patAreaEntries[2]); + debug(patAreaEntries[3]); +#endif + +#ifndef __NEWCODE__ + *pageEntries = patAreaEntries; +#endif + + return DMM_NO_ERROR; +} + +/* ========================================================================== */ +/** + * dmm_tiler_swap_pat_page_entry_data() + * + * @brief Swaps entries in an aray with PAT page address entries. + * + * @param bfrSize - unsigned long - [in] Size of the buffer which will be used + * to generate page entries for. + * + * @param pageEntries - unsigned long* - [in] The address to the allocated page + * entry data array, aligned due to hardware specification. + * + * @param affectedArea - PATAreaT* - [in] Area that will be affected. + * + * @param destX - unsigned short - [in] Destination coordinate X. + * + * @param destY - unsigned short - [in] Destination coordinate Y. + * + * @param stride - unsigned short - [in] Stride of the area. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_swap_pat_page_entry_data(unsigned long numPages, + unsigned long *pageEntries, + struct PATAreaT *affectedArea, + unsigned short destX, + unsigned short destY, + unsigned short stride) +{ + unsigned short row; + unsigned short column; + unsigned long entrySwap; + + unsigned short startX = affectedArea->x0; + unsigned short startY = affectedArea->y0; + + unsigned short endX = affectedArea->x1 + 1; + unsigned short enxY = affectedArea->y1 + 1; + + signed short ofstX = destX - startX; + signed short ofstY = destY - startY; + + for (row = startY; row < enxY; row++) { + for (column = startX; column < endX; column++) { + if (row*stride+column > numPages || + (row+ofstY)*stride+(column+ofstX) > numPages) + return DMM_WRONG_PARAM; + entrySwap = pageEntries[row*stride+column]; + pageEntries[row*stride+column] = + pageEntries[(row+ofstY)*stride+(column+ofstX)]; + pageEntries[(row+ofstY)*stride+(column+ofstX)] = + entrySwap; + } + } + + return DMM_NO_ERROR; +} + +/* ========================================================================== */ +/** + * dmm_tiler_container_map_area() + * + * @brief Allocates a 2D TILER buffer - virtual 2D allocation, + * descriptor and TILER system pointer updating. + * + * @param dmmTilerCtx - dmmTILERContCtxT* - [in] Tiler context instance. + * + * @param sizeWidth - unsigned short - [in] Width of buffer (in container + * elements). + * + * @param sizeHeight - unsigned short - [in] Height of buffer + * (in container elements). + * + * @param contMod - dmmMemoryAccessT - [in] Container access mode - for element + * sizes. + * + * @param allocedPtr - void ** - [out] The allocated buffer system pointer is + * provided through this double pointer. If no buffer is available this pointer + * is set to NULL. + * + * @param bufferMappedZone - dmmTILERContPageAreaT** - [out] Description of the + * 2D + * area that is mapped in the TILER container is provided in this structure. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemoryAccessT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +/* DHS */ +enum errorCodeT dmm_tiler_container_map_area( + struct dmmTILERContCtxT *dmmTilerCtx, + unsigned long sizeWidth, + unsigned long sizeHeight, + enum dmmMemoryAccessT contMod, + void **allocedPtr, + struct dmmTILERContPageAreaT **bufferMappedZone) +{ + struct dmmTILERContPageAreaT areaRequest; + + enum errorCodeT eCode = DMM_NO_ERROR; + signed long pageDimmensionX = 0; + signed long pageDimmensionY = 0; + unsigned long accessMode = 0; + unsigned long addrShiftAlign = 0; + unsigned short tiled_pages_per_ss_page = 0; + + tilerdump(__LINE__); + switch (contMod) { + case MODE_8_BIT: + accessMode = 0; + pageDimmensionX = DMM_PAGE_DIMM_X_MODE_8; + pageDimmensionY = DMM_PAGE_DIMM_Y_MODE_8; + addrShiftAlign = DMM_HOR_Y_ADDRSHIFT_8; + tiled_pages_per_ss_page = DMM_4KiB_SIZE / pageDimmensionX; + break; + case MODE_16_BIT: + accessMode = 1; + pageDimmensionX = DMM_PAGE_DIMM_X_MODE_16; + pageDimmensionY = DMM_PAGE_DIMM_Y_MODE_16; + addrShiftAlign = DMM_HOR_Y_ADDRSHIFT_16; + tiled_pages_per_ss_page = DMM_4KiB_SIZE / pageDimmensionX / 2; + break; + case MODE_32_BIT: + accessMode = 2; + pageDimmensionX = DMM_PAGE_DIMM_X_MODE_32; + pageDimmensionY = DMM_PAGE_DIMM_Y_MODE_32; + addrShiftAlign = DMM_HOR_Y_ADDRSHIFT_32; + tiled_pages_per_ss_page = DMM_4KiB_SIZE / pageDimmensionX / 4; + break; + case MODE_PAGE: + accessMode = 3; + pageDimmensionX = 64;/*DMM_PAGE_DIMM_X_MODE_8;*/ /* 64 */ + pageDimmensionY = 64;/*DMM_PAGE_DIMM_Y_MODE_8;*/ /* 64 */ + /* ((width + 4096 - 1) / 4096) */ + sizeWidth = ((sizeWidth + DMM_4KiB_SIZE - 1)/DMM_4KiB_SIZE); + tiled_pages_per_ss_page = 1; + + /* for 1D blocks larger than the container width, we need to + allocate multiple rows */ + if (sizeWidth > dmmTilerCtx->contSizeX) { + sizeHeight = (sizeWidth + dmmTilerCtx->contSizeX - 1) / + dmmTilerCtx->contSizeX; + sizeWidth = dmmTilerCtx->contSizeX; + } else { + sizeHeight = 1; + } + + sizeHeight *= pageDimmensionX; + sizeWidth *= pageDimmensionY; + + addrShiftAlign = DMM_HOR_Y_ADDRSHIFT_8; /* 14 */ + break; + default: + eCode = DMM_WRONG_PARAM; + break; + } + + areaRequest.x1 = (sizeWidth + pageDimmensionX - 1) / + pageDimmensionX - 1; + areaRequest.y1 = (sizeHeight + pageDimmensionY - 1) / + pageDimmensionY - 1; + + /* fill out to page boundaries */ + if (0) + printk(KERN_ERR "areaRequest(%u (was %u%%%u) by %u)\n", + (areaRequest.x1 + 64) & ~63, areaRequest.x1 + 1, + tiled_pages_per_ss_page, areaRequest.y1 + 1); + + /* since all containers are collapsed, we need to take the most + conservative value for pages per SS page */ + tiled_pages_per_ss_page = 64; + areaRequest.x1 = ((areaRequest.x1 + tiled_pages_per_ss_page) & + ~(tiled_pages_per_ss_page - 1)) - 1; + tilerdump(__LINE__); + + if (areaRequest.x1 > dmmTilerCtx->contSizeX || + areaRequest.y1 > dmmTilerCtx->contSizeY) { + eCode = DMM_WRONG_PARAM; + } + + if (eCode == DMM_NO_ERROR) { + *bufferMappedZone = alloc_2d_area(dmmTilerCtx, &areaRequest); + /* if we could not allocate, we set the return code */ + if (*bufferMappedZone == NULL) + eCode = DMM_SYS_ERROR; + /* else + printk(KERN_ERR "=>%d-%d,%d-%d\n", + (*bufferMappedZone)->x0, + (*bufferMappedZone)->x1, + (*bufferMappedZone)->y0, + (*bufferMappedZone)->y1);*/ + } + + /* DBG_OVERLAP_TEST(dmmTilerCtx); */ + tilerdump(__LINE__); + if (eCode == DMM_NO_ERROR) { + tilerdump(__LINE__); + if (accessMode == 0) { + *allocedPtr = + DMM_COMPOSE_TILER_ALIAS_PTR( + (((*bufferMappedZone)->x0 << 6) | + ((*bufferMappedZone)->y0 << 20)), accessMode); + } else if (accessMode == 3) { + *allocedPtr = + DMM_COMPOSE_TILER_ALIAS_PTR( + (((*bufferMappedZone)->x0 | + ((*bufferMappedZone)->y0 << 8)) << 12), + accessMode); + } else { + *allocedPtr = + DMM_COMPOSE_TILER_ALIAS_PTR( + (((*bufferMappedZone)->x0 << 7) | + ((*bufferMappedZone)->y0 << 20)), accessMode); + } + tilerdump(__LINE__); + } else { + tilerdump(__LINE__); + *allocedPtr = NULL; + } + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_tiler_container_unmap_area() + * + * @brief Frees a 2D virtual TILER container buffer. \ + * + * @param dmmTilerCtx - dmmTILERContCtxT* - [in] Tiler context instance. + * + * @param bufferMappedZone - dmmTILERContPageAreaT* - [in] Description of the 2D + * area that is mapped in the TILER container is provided in this structure. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +/* DHS */ +enum errorCodeT dmm_tiler_container_unmap_area( + struct dmmTILERContCtxT *dmmTilerCtx, + struct dmmTILERContPageAreaT *bufferMappedZone) +{ + if (dealloc_2d_area(dmmTilerCtx, bufferMappedZone) != 1) + return DMM_WRONG_PARAM; + else + return DMM_NO_ERROR; +} + +/* ========================================================================== */ +/** + * dmm_tiler_get_area_from_sysptr() + * + * @brief Gets a 2D area descriptor from the Tiler system pointer. + * + * @param dmmTilerCtx - dmmTILERContCtxT* - [in] Tiler context instance. + * + * @param sysPtr - void * - [in] Tiler system pointer to a 2D area. + * + * @return dmmTILERContPageAreaT* Pointer to the 2D area descriptor, NULL if + * error is encountered during extraction. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +/* DHS */ +struct dmmTILERContPageAreaT *dmm_tiler_get_area_from_sysptr( + struct dmmTILERContCtxT *dmmTilerCtx, void *sysPtr) { + unsigned long X; + unsigned long Y; + enum dmmMemoryAccessT accessModeM; + struct dmmTILERContPageAreaT *found = NULL; + + tilerdump(sysPtr); + accessModeM = DMM_GET_ACC_MODE(sysPtr); + + if (DMM_GET_ROTATED(sysPtr) == 0) { + tilerdump(__LINE__); + if (accessModeM == MODE_PAGE) { + X = ((long)sysPtr & 0x7FFFFFF) >> 12; + Y = X / 256; + X = X & 255; + } else if (accessModeM == MODE_8_BIT) { + X = DMM_HOR_X_PAGE_COOR_GET_8(sysPtr); + Y = DMM_HOR_Y_PAGE_COOR_GET_8(sysPtr); + tilerdump(__LINE__); + } else if (accessModeM == MODE_16_BIT) { + tilerdump(__LINE__); + X = DMM_HOR_X_PAGE_COOR_GET_16(sysPtr); + Y = DMM_HOR_Y_PAGE_COOR_GET_16(sysPtr); + } else if (accessModeM == MODE_32_BIT) { + tilerdump(__LINE__); + X = DMM_HOR_X_PAGE_COOR_GET_32(sysPtr); + Y = DMM_HOR_Y_PAGE_COOR_GET_32(sysPtr); + } + } else { + tilerdump(__LINE__); + if (accessModeM == MODE_PAGE) { + X = ((long)sysPtr & 0x7FFFFFF) >> 12; + Y = X / 256; + X = X & 255; + } else if (accessModeM == MODE_8_BIT) { + tilerdump(__LINE__); + X = DMM_VER_X_PAGE_COOR_GET_8(sysPtr); + Y = DMM_VER_Y_PAGE_COOR_GET_8(sysPtr); + } else if (accessModeM == MODE_16_BIT) { + tilerdump(__LINE__); + X = DMM_VER_X_PAGE_COOR_GET_16(sysPtr); + Y = DMM_VER_Y_PAGE_COOR_GET_16(sysPtr); + } else if (accessModeM == MODE_32_BIT) { + tilerdump(__LINE__); + X = DMM_VER_X_PAGE_COOR_GET_32(sysPtr); + Y = DMM_VER_Y_PAGE_COOR_GET_32(sysPtr); + } + } + + tilerdump(dmmTilerCtx); + tilerdump(X); + tilerdump(Y); + tilerdump(DMM_GET_X_INVERTED(sysPtr)); + tilerdump(DMM_GET_Y_INVERTED(sysPtr)); + /* printk(KERN_ERR " ? %p => x=%ld,y=%ld\n", sysPtr, X, Y); */ + found = search_2d_area(dmmTilerCtx, X, Y, DMM_GET_X_INVERTED(sysPtr), + DMM_GET_Y_INVERTED(sysPtr)); + if (found) { + /*printk(KERN_ERR " >(x=%d-%d=%d+%d,y=%d-%d=%d+%d)\n", + found->x0, found->x1, found->xPageCount, + found->xPageOfst, + found->y0, found->y1, found->yPageCount, + found->yPageOfst);*/ + } + return found; +} + +/* ========================================================================== */ +/** + * dmm_instance_init() + * + * @brief Initializes the Tiler cotnext. + * + * @param dmmInstanceCtxPtr - void * - [in] Tiler context instance. + * + * @param contXSize - signed long - [in] Tiler container width. + * + * @param contYSize - signed long - [in] Tiler container height. + * + * @param hMSP - void * - [in] MSP handle related to this dmm_drv cotnext. + * + * @param usrAppData - void * - [in] Pointer to user specific data structure. + * + * @param usrCallback - MSP_usrCallback - [in] Pointer to callback supplied by + * the user for notificiation events (interupts). + * + * @return int True if operation succeded. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +int dmm_instance_init(void *dmmInstanceCtxPtr, + signed long contXSize, + signed long contYSize, + void *hMSP, + void *usrAppData) +{ + struct dmmTILERContCtxT *dmmTilerCtx = + &((struct dmmInstanceCtxT *)dmmInstanceCtxPtr)->dmmTilerCtx; + struct dmmHwdCtxT *dmmHwdCtx = + &((struct dmmInstanceCtxT *)dmmInstanceCtxPtr)->dmmHwdCtx; + /* struct MSP_Dmm_eventNotificationT * dmmMspCtx = + &((struct dmmInstanceCtxT*)dmmInstanceCtxPtr)->dmmMspCtx; */ + + if (contXSize > 256 || contYSize > 128) + return 0; + + if (dmmHwdCtx->dmmOpenInstances == 0) { + + dmmTilerCtx->usdArList = NULL; + + dmmTilerCtx->tmpArSelect.plmntAr.x0 = 0; + dmmTilerCtx->tmpArSelect.plmntAr.y0 = 0; + dmmTilerCtx->tmpArSelect.plmntAr.x1 = 0; + dmmTilerCtx->tmpArSelect.plmntAr.y1 = 0; + dmmTilerCtx->tmpArSelect.anchrAr = NULL; + + dmmTilerCtx->contSizeX = contXSize; + dmmTilerCtx->contSizeY = contYSize; + + /* Hwi_Params_init (&dmmHwdCtx->dmmIrqIntParams); */ + /* dmmHwdCtx->dmmIrqIntHandle = + Hwi_create (DMM_PAT_AREA_IRQ, dmmPatIrqHandler, + &dmmHwdCtx->dmmIrqIntParams, NULL); */ + + dmmHwdCtx->patIrqEvnt0.irqAreaSelect = 0; + dmmHwdCtx->patIrqEvnt1.irqAreaSelect = 1; + dmmHwdCtx->patIrqEvnt2.irqAreaSelect = 2; + dmmHwdCtx->patIrqEvnt3.irqAreaSelect = 3; + + /* dmmMspCtx->hMSP = hMSP; */ + /* dmmMspCtx->usrAppData = usrAppData; */ + /* dmmMspCtx->usrCallback = usrCallback; */ + + if (dmm_phys_page_rep_init() != DMM_NO_ERROR) + return 0; + } + + dmmHwdCtx->dmmOpenInstances++; + + return 1; +} + +/* ========================================================================== */ +/** + * dmm_instance_deinit() + * + * @brief Deinitializes the Tiler cotnext. + * + * @param dmmInstanceCtxPtr - void * - [in] Tiler context instance. + * + * @return int True if operation succeded. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +int dmm_instance_deinit(void *dmmInstanceCtxPtr) +{ + struct dmmTILERContCtxT *dmmTilerCtx = + &((struct dmmInstanceCtxT *)dmmInstanceCtxPtr)->dmmTilerCtx; + struct dmmHwdCtxT *dmmHwdCtx = + &((struct dmmInstanceCtxT *)dmmInstanceCtxPtr)->dmmHwdCtx; + struct MSP_Dmm_eventNotificationT *dmmMspCtx = + &((struct dmmInstanceCtxT *)dmmInstanceCtxPtr)->dmmMspCtx; + dmmHwdCtx->dmmOpenInstances--; + if (dmmHwdCtx->dmmOpenInstances < 0) + return 0; + + if (dmmHwdCtx->dmmOpenInstances == 0) { + while (dmmTilerCtx->usdArList != NULL) { + if (dmm_tiler_container_unmap_area( + dmmTilerCtx, + &(dmmTilerCtx->usdArList->pgAr)) != + DMM_NO_ERROR) + return 0; + } + + dmmTilerCtx->usdArList = NULL; + + dmmTilerCtx->tmpArSelect.plmntAr.x0 = 0; + dmmTilerCtx->tmpArSelect.plmntAr.y0 = 0; + dmmTilerCtx->tmpArSelect.plmntAr.x1 = 0; + dmmTilerCtx->tmpArSelect.plmntAr.y1 = 0; + dmmTilerCtx->tmpArSelect.anchrAr = NULL; + + dmmTilerCtx->contSizeX = 0; + dmmTilerCtx->contSizeY = 0; + + dmmMspCtx->hMSP = NULL; + dmmMspCtx->usrAppData = NULL; + } + + return 1; +} + +/* ========================================================================== */ +/** +* dmm_pat_irq_handler() +* +* @brief Interrupt handler for PAT area status updates. +* +* @param data - UArg - +* +* @return none +* +* @pre There is no pre conditions. +* +* @post There is no post conditions. +* +* @see +* +*/ +/* ========================================================================== */ +void dmm_pat_irq_handler(unsigned long data) +{ +} + +/* ========================================================================== */ +/** + * dmm_copy2tiler_alias_view() + * + * @brief Auxiliary function for copying data to the Tiler alias view. + * + * @param destPtr - void * - [in] Destination pointer in Tiler alias view. + * + * @param srcPtr - void * - [in] Data source pointer. + * + * @param width - signed long - [in] Data width. + * + * @param height - signed long - [in] Data height. + * + * @param stride - signed long - [in] Data stride. + * + * @param accType - dmmMemoryAccessT - [in] Tiler memory view access type. + * + * @return errorCodeT error if event can't be signaled. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_copy2tiler_alias_view(void *destPtr, + void *srcPtr, + signed long width, + signed long height, + signed long stride, + enum dmmMemoryAccessT accType) +{ + signed long row; + + switch (accType) { + case MODE_8_BIT: { + unsigned char *destPtr8 = (unsigned char *)destPtr; + unsigned char *srcPtr8 = (unsigned char *)srcPtr; + for (row = 0; row < height; row++) { + memcpy(&destPtr8[row*DMM_TILER_CONT_WIDTH_8], + srcPtr8, width); + srcPtr8 += stride; + } + } + break; + case MODE_16_BIT: { + unsigned short *destPtr16 = (unsigned short *)destPtr; + unsigned short *srcPtr16 = (unsigned short *)srcPtr; + for (row = 0; row < height; row++) { + memcpy(&destPtr16[row*DMM_TILER_CONT_WIDTH_16], + srcPtr16, width*2); + srcPtr16 += stride; + } + } + break; + case MODE_32_BIT: { + unsigned long *destPtr32 = (unsigned long *)destPtr; + unsigned long *srcPtr32 = (unsigned long *)srcPtr; + for (row = 0; row < height; row++) { + memcpy(&destPtr32[row*DMM_TILER_CONT_WIDTH_32], + srcPtr32, width*4); + srcPtr32 += stride; + } + } + break; + case MODE_PAGE: { + unsigned char *destPtr8 = (unsigned char *)destPtr; + unsigned char *srcPtr8 = (unsigned char *)srcPtr; + for (row = 0; row < stride*height; row++) { + memcpy(&destPtr8[row], srcPtr8, stride); + srcPtr8 += stride; + } + } + break; + default: + return DMM_SYS_ERROR; + } + + return DMM_NO_ERROR; +} + +/* ========================================================================== */ +/** + * dmm_copy_from_tiler_alias_view() + * + * @brief Auxiliary function for copying data to the Tiler alias view. + * + * @param destPtr - void * - [in] Destination pointer in Tiler alias view. + * + * @param srcPtr - void * - [in] Data source pointer. + * + * @param width - signed long - [in] Data width. + * + * @param height - signed long - [in] Data height. + * + * @param stride - signed long - [in] Data stride. + * + * @param accType - dmmMemoryAccessT - [in] Tiler memory view access type. + * + * @return errorCodeT error if event can't be signaled. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_copy_from_tiler_alias_view(void *destPtr, + void *srcPtr, + signed long width, + signed long height, + signed long stride, + enum dmmMemoryAccessT accType) +{ + signed long row; + + switch (accType) { + case MODE_8_BIT: { + unsigned char *destPtr8 = (unsigned char *)destPtr; + unsigned char *srcPtr8 = (unsigned char *)srcPtr; + for (row = 0; row < height; row++) { + memcpy(destPtr8, &srcPtr8[row*DMM_TILER_CONT_WIDTH_8], + width); + destPtr8 += stride; + } + } + break; + case MODE_16_BIT: { + unsigned short *destPtr16 = (unsigned short *)destPtr; + unsigned short *srcPtr16 = (unsigned short *)srcPtr; + for (row = 0; row < height; row++) { + memcpy(destPtr16, + &srcPtr16[row*DMM_TILER_CONT_WIDTH_16], + width*2); + destPtr16 += stride; + } + } + break; + case MODE_32_BIT: { + unsigned long *destPtr32 = (unsigned long *)destPtr; + unsigned long *srcPtr32 = (unsigned long *)srcPtr; + for (row = 0; row < height; row++) { + memcpy(destPtr32, + &srcPtr32[row*DMM_TILER_CONT_WIDTH_32], + width*4); + destPtr32 += stride; + } + } + break; + case MODE_PAGE: { + unsigned char *destPtr8 = (unsigned char *)destPtr; + unsigned char *srcPtr8 = (unsigned char *)srcPtr; + for (row = 0; row < stride*height; row++) { + memcpy(destPtr8, &srcPtr8[row], stride); + destPtr8 += stride; + } + } + break; + default: + return DMM_SYS_ERROR; + } + + return DMM_NO_ERROR; +} + +/* ========================================================================== */ +/** + * dmmVirtualBufferManipulations() + * + * @brief Manipulates virtual buffers. + * + * @param dmmInstanceCtxPtr - void * - [in] Dmm context instance. + * + * @param sysPtr - void * - [in] Tiler system pointer to a 2D area. + * + * @param patOp - MSP_Dmm_Phy2VirtOpsT - [in] Refill operaion to perform. + * + * @param affectedArea - PATAreaT* - [in] Area that will be affected. + * + * @param destinationArea - PATAreaT* - [in] Destination coordinates. + * + * @return void * pointer to the area targeted by the operation. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +void *dmm_virtual_buffer_manipulations(void *dmmInstanceCtxPtr, + void *sysPtr, + struct PATAreaT *affectedArea, + struct PATAreaT *destinationArea) +{ + unsigned long bfrPages = 0x0; + struct dmmTILERContPageAreaT *bufferMappedZone = NULL; + enum errorCodeT eCode = DMM_NO_ERROR; + struct dmmTILERContCtxT *dmmTilerCtx = + &((struct dmmInstanceCtxT *)dmmInstanceCtxPtr)->dmmTilerCtx; + + enum dmmMemoryAccessT accessModeM = -1; + struct dmmViewOrientT orient = {0}; + unsigned long addrAlignment = 0x0; + unsigned long contWidth = 0x0; + unsigned long contHeight = 0x0; + + bufferMappedZone = dmm_tiler_get_area_from_sysptr(dmmTilerCtx, sysPtr); + bfrPages = + (bufferMappedZone->xPageCount)*(bufferMappedZone->yPageCount); + + eCode = dmm_tiler_swap_pat_page_entry_data(bfrPages, + bufferMappedZone->patPageEntries, affectedArea, + destinationArea->x0, destinationArea->y0, + bufferMappedZone->xPageCount); + + eCode = dmm_pat_start_refill(bufferMappedZone); + + if (eCode != DMM_NO_ERROR) + return NULL; + + accessModeM = DMM_GET_ACC_MODE(sysPtr); + orient.dmm90Rotate = (unsigned char)DMM_GET_ROTATED(sysPtr); + orient.dmmXInvert = (unsigned char)DMM_GET_X_INVERTED(sysPtr); + orient.dmmYInvert = (unsigned char)DMM_GET_Y_INVERTED(sysPtr); + + switch (accessModeM) { + case MODE_8_BIT: + case MODE_PAGE: + if (orient.dmm90Rotate) { + contWidth = DMM_TILER_CONT_HEIGHT_8; + contHeight = DMM_TILER_CONT_WIDTH_8; + } else { + contWidth = DMM_TILER_CONT_WIDTH_8; + contHeight = DMM_TILER_CONT_HEIGHT_8; + } + addrAlignment = 0; + break; + case MODE_16_BIT: + if (orient.dmm90Rotate) { + contWidth = DMM_TILER_CONT_HEIGHT_16; + contHeight = DMM_TILER_CONT_WIDTH_16; + } else { + contWidth = DMM_TILER_CONT_WIDTH_16; + contHeight = DMM_TILER_CONT_HEIGHT_16; + } + addrAlignment = 1; + break; + case MODE_32_BIT: + if (orient.dmm90Rotate) { + contWidth = DMM_TILER_CONT_HEIGHT_32; + contHeight = DMM_TILER_CONT_WIDTH_32; + } else { + contWidth = DMM_TILER_CONT_WIDTH_32; + contHeight = DMM_TILER_CONT_HEIGHT_32; + } + addrAlignment = 2; + break; + } + + if (orient.dmm90Rotate) { + sysPtr = (void *)((destinationArea->x0*contHeight + + destinationArea->y0) << + ((unsigned long)addrAlignment)); + } else { + sysPtr = (void *)((destinationArea->y0*contWidth + + destinationArea->x0) << + ((unsigned long)addrAlignment)); + } + sysPtr = DMM_COMPOSE_TILER_PTR(sysPtr, orient.dmm90Rotate, + orient.dmmYInvert, + orient.dmmXInvert, accessModeM); + + return sysPtr; +} + diff --git a/drivers/media/video/tiler/dmm_ll_drv.c b/drivers/media/video/tiler/dmm_ll_drv.c new file mode 100644 index 000000000000..6161fc678686 --- /dev/null +++ b/drivers/media/video/tiler/dmm_ll_drv.c @@ -0,0 +1,874 @@ +/* + * dmm_ll_drv.c + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <linux/module.h> +#include <linux/io.h> +#include "dmm_def.h" +#include "dmm_2d_alloc.h" +#include "dmm_prv.h" +#include "dmm_reg.h" +MODULE_LICENSE("GPL v2"); + +#define __NEWCODE__ +#ifdef __NEWCODE__ +/* extern unsigned long entry_data; */ +static void pat_config_set(); + +static void pat_config_set()/* (struct pat_config_set *config, char id) */ +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_CONFIG register */ + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr + | (unsigned long)PAT_CONFIG); + reg_val = __raw_readl(reg); + regdump("PAT_CONFIG", reg_val); + + bit_field = BITFIELD(31, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)0) << field_pos) & bit_field); + __raw_writel(0x0000000F, reg); /* __raw_writel(new_val, reg); */ + + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_CONFIG); + reg_val = __raw_readl(reg); + regdump("PAT_CONFIG", reg_val); +} + +struct pat_area { + int x0:8; + int y0:8; + int x1:8; + int y1:8; +}; + +struct pat_ctrl { + int start:4; + int direction:4; + int lut_id:8; + int sync:12; + int initiator:4; +}; + +struct pat_desc { + struct pat_desc *next; + struct pat_area area; + struct pat_ctrl ctrl; + unsigned long data; +}; + +static void pat_ctrl_set(struct pat_ctrl *ctrl, char id) +{ + void __iomem *reg = NULL; + unsigned long reg_val = 0x0; + unsigned long new_val = 0x0; + unsigned long bit_field = 0x0; + unsigned long field_pos = 0x0; + + /* set PAT_CTRL register */ + /* TODO: casting as unsigned long */ + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_CTRL__0); + reg_val = __raw_readl(reg); + regdump("PAT_CTRL__0", reg_val); + + bit_field = BITFIELD(31, 28); + field_pos = 28; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->initiator) << + field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(16, 16); + field_pos = 16; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->sync) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(9, 8); + field_pos = 8; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->lut_id) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(6, 4); + field_pos = 4; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->direction) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg_val = __raw_readl(reg); + bit_field = BITFIELD(0, 0); + field_pos = 0; + new_val = (reg_val & (~(bit_field))) | + ((((unsigned long)ctrl->start) << field_pos) & bit_field); + __raw_writel(new_val, reg); + + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | (unsigned long)PAT_CTRL__0); + reg_val = __raw_readl(reg); + regdump("PAT_CTRL__0", reg_val); +} +#endif + +/* ========================================================================== */ +/** + * dmm_tiler_alias_orientation_set() + * + * @brief Set specific TILER alias orientation setting per initiator ID + * (alias view). + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which alias view will be editted. + * + * @param viewOrientation - dmmViewOrientT - [in] New alias view orientation + * setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmViewOrientT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_alias_orientation_set(signed long initiatorID, + struct dmmViewOrientT viewOrientation) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + void __iomem *reg = NULL; + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x220ul)); + __raw_writel(0x88888888, reg); + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x220ul+0x4)); + __raw_writel(0x88888888, reg); + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_tiler_alias_orientation_get() + * + * @brief Get specific TILER alias orientation setting per initiator ID + * (alias view). + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which alias view will be editted. + * + * @param viewOrientation - dmmViewOrientT* - [out] Pointer to write alias view + * orientation setting to. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmViewOrientT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_alias_orientation_get(signed long initiatorID, + struct dmmViewOrientT *viewOrientation) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_peg_priority_set() + * + * @brief Set specific PEG priority setting per initiator ID. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which priority will be editted. + * + * @param prio - unsigned long - [in] New priority setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_peg_priority_set(signed long initiatorID, + unsigned long prio) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_peg_priority_get() + * + * @brief Get specific PEG priority setting per initiator ID. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which priority will be editted. + * + * @param prio - unsigned long* - [out] Poitner to write the priority setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_peg_priority_get(signed long initiatorID, + unsigned long *prio) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_area_refill() + * + * @brief Initiate a PAT area refill (or terminate an ongoing - consult + * documentation). + * + * @param patDesc - PATDescrT* - [in] Pointer to a PAT area descriptor that's + * needed to extract settings from for the refill procedure initation. + * + * @param dmmPatAreaSel - signed long - [in] Selects which PAT area will be + * configured for a area refill procedure. + * + * @param refillType - dmmPATRefillMethodT - [in] Selects the refill method - + * manual or automatic. + * + * @param forcedRefill - int - [in] Selects if forced refill should be used + * effectively terminating any ongoing area refills related to the selected area + * + * @return errorCodeT + * + * @pre If forced mode is not used, no refills should be ongoing for the + * selected area - error status returned if this occurs. + * + * @post If non valid data is provided for patDesc and the refill engines fail + * to perform the request, an error status is returned. + * + * @see errorCodeT, PATDescrT, dmmPATRefillMethodT, int for further detail + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_area_refill(struct PATDescrT *patDesc, + signed long dmmPatAreaSel, + enum dmmPATRefillMethodT refillType, + int forcedRefill) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + void __iomem *reg = NULL; + unsigned long regval = 0xffffffff; + unsigned long writeval = 0xffffffff; + unsigned long f = 0xffffffff; /* field */ + unsigned long fp = 0xffffffff; /* field position */ + + struct dmmPATStatusT areaStat; + + tilerdump(__LINE__); + if (forcedRefill == 0) { + eCode = dmm_pat_refill_area_status_get( + dmmPatAreaSel, &areaStat); + + if (eCode == DMM_NO_ERROR) { + if (areaStat.ready == 0 || areaStat.engineRunning) { + printk(KERN_ALERT "hw not ready\n"); + eCode = DMM_HRDW_NOT_READY; + } + } + } + + if (dmmPatAreaSel < 0 || dmmPatAreaSel > 3) { + eCode = DMM_WRONG_PARAM; + } else if (eCode == DMM_NO_ERROR) { + if (refillType == AUTO) { + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | + (0x500ul + 0x0)); + regval = __raw_readl(reg); + f = BITFIELD(31, 4); + fp = 4; + writeval = (regval & (~(f))) | + ((((unsigned long)patDesc) << fp) & f); + __raw_writel(writeval, reg); + } else if (refillType == MANUAL) { + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | + (0x500ul + 0x0)); + regval = __raw_readl(reg); + f = BITFIELD(31, 4); + fp = 4; + writeval = (regval & (~(f))) | + ((((unsigned long)NULL) << fp) & f); + __raw_writel(writeval, reg); + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | + (0x500ul + 0x4)); + regval = __raw_readl(reg); + f = BITFIELD(30, 24); + fp = 24; + writeval = (regval & (~(f))) | + ((((char)patDesc->area.y1) << fp) & f); + __raw_writel(writeval, reg); + + regval = __raw_readl(reg); + f = BITFIELD(23, 16); + fp = 16; + writeval = (regval & (~(f))) | + ((((char)patDesc->area.x1) << fp) & f); + __raw_writel(writeval, reg); + + regval = __raw_readl(reg); + f = BITFIELD(14, 8); + fp = 8; + writeval = (regval & (~(f))) | + ((((char)patDesc->area.y0) << fp) & f); + __raw_writel(writeval, reg); + + regval = __raw_readl(reg); + f = BITFIELD(7, 0); + fp = 0; + writeval = (regval & (~(f))) | + ((((char)patDesc->area.x0) << fp) & f); + __raw_writel(writeval, reg); +#ifndef __NEWCODE__ + /* Apply 4 bit lft shft to counter the 4 bit rt shft */ + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | (0x500ul + 0xc)); + regval = __raw_readl(reg); + f = BITFIELD(31, 4); + fp = 4; + writeval = (regval & (~(f))) | + ((((unsigned long)(patDesc->data >> 4)) << + fp) & f); + __raw_writel(writeval, reg); + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | + (0x500ul + 0x8)); + regval = __raw_readl(reg); + f = BITFIELD(31, 28); + fp = 28; + writeval = (regval & (~(f))) | + (((patDesc->ctrl.initiator) << fp) & f); + __raw_writel(writeval, reg); + + f = BITFIELD(16, 16); + fp = 16; + writeval = (regval & (~(f))) | + (((patDesc->ctrl.sync) << fp) & f); + __raw_writel(writeval, reg); + + f = BITFIELD(6, 4); + fp = 4; + writeval = (regval & (~(f))) | + (((patDesc->ctrl.direction) << fp) & f); + __raw_writel(writeval, reg); + + f = BITFIELD(0, 0); + fp = 0; + writeval = (regval & (~(f))) | + (((patDesc->ctrl.start) << fp) & f); + __raw_writel(writeval, reg); +#else + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_STATUS__0); + regdump("PAT_STATUS__0", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_IRQSTATUS_RAW); + regdump("PAT_IRQSTATUS_RAW", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_IRQSTATUS); + regdump("PAT_IRQSTATUS", __raw_readl(reg)); + + /* read and print data register */ + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | (0x500ul + 0xc)); + regval = __raw_readl(reg); + regdump("PAT_DATA__0", regval); + + /* Apply 4 bit lft shft to counter the 4 bit rt shft */ + f = BITFIELD(31, 4); + fp = 4; + writeval = (regval & (~(f))) | ((((unsigned long) + (patDesc->data >> 4)) << fp) & f); + __raw_writel(writeval, reg); + + /* read and print data register again */ + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | (0x500ul + 0xc)); + regval = __raw_readl(reg); + regdump("PAT_DATA__0", regval); + + struct pat_desc pat_desc = {0}; + pat_desc.ctrl.start = 1; + pat_desc.ctrl.direction = 0; + pat_desc.ctrl.lut_id = 0; + pat_desc.ctrl.sync = 0; + pat_desc.ctrl.initiator = 0; + pat_ctrl_set(&pat_desc.ctrl, 0); + + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_STATUS__0); + regdump("PAT_STATUS__0", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_IRQSTATUS_RAW); + regdump("PAT_IRQSTATUS_RAW", __raw_readl(reg)); + + reg = (void __iomem *)( + (unsigned long)dmm_virt_base_addr | + (unsigned long)PAT_IRQSTATUS); + regdump("PAT_IRQSTATUS", __raw_readl(reg)); + + /* pat_config_set(); */ +#endif + } else { + eCode = DMM_WRONG_PARAM; + } + if (eCode == DMM_NO_ERROR) { + eCode = dmm_pat_refill_area_status_get(dmmPatAreaSel, + &areaStat); + + if (eCode == DMM_NO_ERROR) { + if (areaStat.validDescriptor == 0) { + eCode = DMM_HRDW_CONFIG_FAILED; + printk(KERN_ALERT "hw config fail\n"); + } + } + + while (!areaStat.done && !areaStat.ready && + eCode == DMM_NO_ERROR) { + eCode = dmm_pat_refill_area_status_get( + dmmPatAreaSel, + &areaStat); + } + + if (areaStat.error) { + eCode = DMM_HRDW_CONFIG_FAILED; + printk(KERN_ALERT "hw config fail\n"); + } + } + } + tilerdump(__LINE__); + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_refill_area_status_get() + * + * @brief Gets the status for the selected PAT area. + * + * @param dmmPatAreaSel - signed long - [in] Selects which PAT area status will + * be queried. + * + * @param areaStatus - dmmPATStatusT* - [out] Structure containing the PAT area + * status that will be filled by dmmPatRefillAreaStatusGet(). + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post If the query fails the provided areaStatus structure is not updated + * at all! + * + * @see errorCodeT, dmmPATStatusT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_refill_area_status_get(signed long dmmPatAreaStatSel, + struct dmmPATStatusT *areaStatus) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + + unsigned long stat = 0xffffffff; + void __iomem *statreg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | 0x4c0ul); + + if (dmmPatAreaStatSel >= 0 && dmmPatAreaStatSel <= 3) { + stat = __raw_readl(statreg); + + areaStatus->remainingLinesCounter = stat & BITFIELD(24, 16); + areaStatus->error = (enum dmmPATStatusErrT) + (stat & BITFIELD(15, 10)); + areaStatus->linkedReconfig = stat & BITFIELD(4, 4); + areaStatus->done = stat & BITFIELD(3, 3); + areaStatus->engineRunning = stat & BITFIELD(2, 2); + areaStatus->validDescriptor = stat & BITFIELD(1, 1); + areaStatus->ready = stat & BITFIELD(0, 0); + + } else { + eCode = DMM_WRONG_PARAM; + printk(KERN_ALERT "wrong parameter\n"); + } + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_refill_engine_config() + * + * @brief Configure the selected PAT refill engine. + * + * @param dmmPatEngineSel - signed long - [in] Selects which PAT engine will be + * configured. + * + * @param engineMode - dmmPATEngineAccessT - [in] New engine mode. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATEngineAccessT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_refill_engine_config(signed long dmmPatEngineSel, + enum dmmPATEngineAccessT engineMode) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + void __iomem *reg = NULL; + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x410ul)); + __raw_writel(0x00000000, reg); + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_refill_engine_config_get() + * + * @brief Get the selected PAT refill engine configuration. + * + * @param dmmPatEngineSel - signed long - [in] Selects which PAT engine will be + * configured. + * + * @param engineMode - dmmPATEngineAccessT* - [out] Pointer to write the engine + * mode. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmPATEngineAccessT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_refill_engine_config_get(signed long dmmPatEngineSel, + enum dmmPATEngineAccessT *engineMode) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_view_set() + * + * @brief Set specific PAT initiator view settings. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which PAT view will be editted. + * + * @param patViewIndx - signed long - [in] New view map setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_view_set(signed long initiatorID, + signed long patViewIndx) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + void __iomem *reg = NULL; + + if (patViewIndx < 0 || patViewIndx > 3) { + eCode = DMM_WRONG_PARAM; + } else { + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | (0x420ul)); + __raw_writel(0xDDDDDDDD, reg); + + reg = (void __iomem *) + ((unsigned long)dmm_virt_base_addr | (0x420ul+0x4)); + __raw_writel(0xDDDDDDDD, reg); + } + + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_view_get() + * + * @brief Get specific PAT initiator view settings. + * + * @param initiatorID - signed long - [in] OCP id of DMM transfer initiator + * which PAT view will be editted. + * + * @param patViewIndx - signed long* - [out] Pointer to write the view map + * setting. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_view_get( + signed long initiatorID, signed long *patViewIndx) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_pat_view_map_config() + * + * @brief Configure selected PAT view map. + * + * @param patViewMapIndx - signed long - [in] Index of the selected PAT view + * map. + * + * @param memoryAccessMode - dmmMemoryAccessT - [in] Type of memory access to + * perform through this view. + * + * @param contX - unsigned long - [in] CONT_x register value. + * + * @param transType - dmmPATTranslationT - [in] Address translation schemes. + * + * @param dmmPATViewBase - unsigned long - [in] View map base address + * (31-bit only considered). + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemoryAccessT, dmmPATTranslationT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_view_map_config(signed long patViewMapIndx, + enum dmmMemoryAccessT memoryAccessMode, + unsigned long contX, + enum dmmPATTranslationT transType, + unsigned long dmmPATViewBase) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + void __iomem *reg = NULL; + unsigned long regval = 0xffffffff; + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x460ul)); + regval = __raw_readl(reg); + __raw_writel(0xFFFFFFFF, reg); + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x440ul)); + regval = __raw_readl(reg); + __raw_writel(0x80808080, reg); + + return eCode; +} + +enum errorCodeT dmm_pat_view_map_config_get(signed long patViewMapIndx, + enum dmmMemoryAccessT memoryAccessMode, + unsigned long *contX, + enum dmmPATTranslationT *transType, + unsigned long *dmmPATViewBase) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_lisa_memory_map_config() + * + * @brief Configure selected LISA memory map. + * + * @param lisaMemMapIndx - signed long - [in] Index of the selected LISA memory + * map. + * + * @param sysSize - dmmMemSectionSizeT - [in] Size of the memory section. + * + * @param sdrcIntl - dmmMemSdrcIntlModeT - [in] SDRAM controller interleaving + * mode + * + * @param sdrcAddrspc - unsigned long - [in] SDRAM controller address space. + * + * @param sdrcMap - dmmMemSectionMappingT - [in] SDRAM controller mapping. + * + * @param sdrcAddr - unsigned long - [in] SDRAM controller address MSB. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemSectionSizeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_lisa_memory_map_config(signed long lisaMemMapIndx, + unsigned long sysAddr, + enum dmmMemSectionSizeT sysSize, + enum dmmMemSdrcIntlModeT sdrcIntl, + unsigned long sdrcAddrspc, + enum dmmMemSectionMappingT sdrcMap, + unsigned long sdrcAddr) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + void __iomem *reg = NULL; + + if (lisaMemMapIndx != 0) + printk(KERN_WARNING "lisaMemMapIndx != 0\n"); + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x40ul + (0x4*lisaMemMapIndx))); + __raw_writel(0x80710100, reg); + + return eCode; +} + +enum errorCodeT dmm_lisa_memory_map_config_get(signed long lisaMemMapIndx, + unsigned long *sysAddr, + enum dmmMemSectionSizeT *sysSize, + enum dmmMemSdrcIntlModeT *sdrcIntl, + unsigned long *sdrcAddrspc, + enum dmmMemSectionMappingT *sdrcMap, + unsigned long *sdrcAddr) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +/* ========================================================================== */ +/** + * dmm_engage_lisa_lock() + * + * @brief Sets the LISA lock register preventing further writting to the + * LISA memory map registers. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post Only a software reset of the DMM module can clear the lock bit. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_engage_lisa_lock() +{ + void __iomem *reg = NULL; + unsigned long regval = 0xffffffff; + unsigned long writeval = 0xffffffff; + unsigned long f = 0xffffffff; /* field */ + unsigned long fp = 0xffffffff; /* field position */ + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x1cul)); + regval = __raw_readl(reg); + + f = BITFIELD(0, 0); + fp = 0; + writeval = (regval & (~(f))) | (((0x1ul) << fp) & f); + __raw_writel(writeval, reg); + + return DMM_NO_ERROR; +} + +enum errorCodeT dmm_sys_config_set(unsigned long dmmIdleMode) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +enum errorCodeT dmm_sys_config_get(unsigned long *dmmIdleMode) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +enum errorCodeT dmm_pat_irq_mode_set(struct dmmPATIrqEventsT *patIrqEvnt) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + void __iomem *reg = NULL; + + reg = (void __iomem *)((unsigned long)dmm_virt_base_addr | + (0x4a0ul)); + + __raw_writel(0x0000FFFF, reg); /* clr all irq registers */ + return eCode; +} + +enum errorCodeT dmm_pat_irq_mode_get(struct dmmPATIrqEventsT *patIrqEvnt) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} + +enum errorCodeT dmm_pat_irq_status_get(struct dmmPATIrqEventsT *patIrqEvnt, + int clrEvents) +{ + enum errorCodeT eCode = DMM_NO_ERROR; + return eCode; +} diff --git a/drivers/media/video/tiler/dmm_page_rep.c b/drivers/media/video/tiler/dmm_page_rep.c new file mode 100644 index 000000000000..7073a9fa6169 --- /dev/null +++ b/drivers/media/video/tiler/dmm_page_rep.c @@ -0,0 +1,358 @@ +/* + * dmm_page_rep.c + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <linux/vmalloc.h> +#include <linux/mm.h> /* vmalloc_to_page */ +#include <linux/mmzone.h> /* __page_to_phys */ +#include "dmm_prv.h" + +#define __NEWCODE__ + +#ifdef CHECK_STACK +#define lajosdump(x) printk(KERN_NOTICE "%s::%s():%d: %s=%p\n",\ + __FILE__, __func__, __LINE__, #x, x); +#else +#define lajosdump(x) +#endif + +unsigned long freePageCnt; +unsigned char *dmmPhysPages; +struct dmmPhysPgLLT *freePagesStack; +struct dmmPhysPgLLT *usedPagesStack; + +#ifdef CHECK_STACK +static void print_stack(struct dmmPhysPgLLT *stack, char *prefix, int line) +{ + while (stack) { + printk(KERN_ERR "%s%p<-%p->%p", prefix, stack->prevPhysPg, + stack, stack->nextPhysPg); + stack = stack->prevPhysPg; + prefix = ""; + } + printk(KERN_ERR "in line %d.", line); +} + +static void check_stack(struct dmmPhysPgLLT *stack, char *prefix, int line) +{ + if (!stack) + return; + if (stack->nextPhysPg != NULL) + print_stack(stack, prefix, line); + else { + struct dmmPhysPgLLT *ptr = stack; + while (ptr) { + if (ptr->prevPhysPg != NULL && + ptr->prevPhysPg->nextPhysPg != ptr) { + print_stack(stack, prefix, line); + return; + } + ptr = ptr->prevPhysPg; + } + } +} +#endif + +/*--------function prototypes ---------------------------------*/ + +/* ========================================================================== */ +/** + * dmmPhysPageRepRefil() + * + * @brief Refills the physical page repository with the predefined page amount. + * + * @return none + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void dmm_phys_page_rep_refil(void) +{ + unsigned long iter; + + tilerdump(__LINE__); + for (iter = 0; iter < DMM_MNGD_PHYS_PAGES; iter++) { + struct dmmPhysPgLLT *tmpPgNode = kmalloc + (sizeof(struct dmmPhysPgLLT), GFP_KERNEL); + memset(tmpPgNode, 0x0, sizeof(struct dmmPhysPgLLT)); + + if (tmpPgNode != NULL) { + + tmpPgNode->nextPhysPg = NULL; + tmpPgNode->prevPhysPg = NULL; +#ifndef __NEWCODE__ + tmpPgNode->physPgPtr = + (unsigned long *)__get_free_page( + GFP_KERNEL | GFP_DMA); +#else + struct page *page = NULL; + + page = (struct page *)alloc_page(GFP_KERNEL | GFP_DMA); + tmpPgNode->physPgPtr = + (unsigned long *)page_to_phys(page); + tmpPgNode->page_addr = page; +#endif + + /* add to end */ + if (freePagesStack != NULL) { + if (freePagesStack->nextPhysPg != NULL) + lajosdump(freePagesStack->nextPhysPg); + freePagesStack->nextPhysPg = tmpPgNode; + tmpPgNode->prevPhysPg = freePagesStack; + } + + freePagesStack = tmpPgNode; + freePageCnt++; + } else { + printk(KERN_ERR "error\n"); + } + } + +#ifdef CHECK_STACK + check_stack(freePagesStack, "free: ", __LINE__); +#endif +} + +/* ========================================================================== */ +/** + * dmmPhysPageRepInit() + * + * @brief Initializes the physical memory page repository instance. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_phys_page_rep_init(void) +{ + /* DMM_ENTER_CRITICAL_SECTION */ + + freePagesStack = NULL; + usedPagesStack = NULL; + + + + freePageCnt = 0; + + dmm_phys_page_rep_refil(); + + /* DMM_EXIT_CRITICAL_SETCTION */ + return DMM_NO_ERROR; +} + +/* ========================================================================== */ +/** + * dmmPhysPageRepDeinit() + * + * @brief Releases all resources held by the physical memory page repository + * instance. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_phys_page_rep_deinit(void) +{ + /* DMM_ENTER_CRITICAL_SECTION */ + struct dmmPhysPgLLT *tmpPgNode = NULL; + + while (usedPagesStack != NULL) { + tmpPgNode = usedPagesStack->prevPhysPg; +#ifndef __NEWCODE__ + free_page((unsigned long)usedPagesStack->physPgPtr); + kfree(usedPagesStack); +#else + __free_page(usedPagesStack->page_addr); + kfree(usedPagesStack); +#endif + usedPagesStack = tmpPgNode; + } + + while (freePagesStack != NULL) { + tmpPgNode = freePagesStack->prevPhysPg; +#ifndef __NEWCODE__ + free_page((unsigned long)freePagesStack->physPgPtr); + kfree(freePagesStack); +#else + __free_page(freePagesStack->page_addr); + kfree(freePagesStack); +#endif + freePagesStack = tmpPgNode; + } + + freePageCnt = 0; + + /* DMM_EXIT_CRITICAL_SETCTION */ + return DMM_NO_ERROR; +} + +/* ========================================================================== */ +/** + * dmmGetPhysPage() + * + * @brief Return a pointer to a physical memory page and mark it as used. + * + * @return MSP_U32* pointer to a physical memory page, NULL if error occurs. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +unsigned long *dmm_get_phys_page(void) +{ + unsigned long *physPgPtr = NULL; + + /* DMM_ENTER_CRITICAL_SECTION */ + + if (freePagesStack == NULL) + dmm_phys_page_rep_refil(); + + if (freePagesStack != NULL) { + struct dmmPhysPgLLT *tmpPgNode = freePagesStack; + + /* remove page from top of freepages */ + if (freePagesStack->nextPhysPg != NULL) + lajosdump(freePagesStack->nextPhysPg); + freePagesStack = freePagesStack->prevPhysPg; + if (freePagesStack != NULL) + freePagesStack->nextPhysPg = NULL; + + /* add page to top of usedpages */ + tmpPgNode->prevPhysPg = usedPagesStack; + if (usedPagesStack != NULL) { + if (usedPagesStack->nextPhysPg != NULL) + lajosdump(usedPagesStack->nextPhysPg); + usedPagesStack->nextPhysPg = tmpPgNode; + } + tmpPgNode->nextPhysPg = NULL; + usedPagesStack = tmpPgNode; + + physPgPtr = tmpPgNode->physPgPtr; + freePageCnt--; + } + +#ifdef CHECK_STACK + check_stack(freePagesStack, "free: ", __LINE__); + check_stack(usedPagesStack, "used: ", __LINE__); +#endif + /* DMM_EXIT_CRITICAL_SETCTION */ + return physPgPtr; +} + +/* ========================================================================== */ +/** + * dmmFreePhysPage() + * + * @brief Frees a specified physical memory page. + * + * @param physPgPtr - MSP_U32* - [in] The address of the allocated physical page + * that should be freed. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_free_phys_page(unsigned long *physPgPtr) +{ + struct dmmPhysPgLLT *iterPgNode = usedPagesStack; + + /* DMM_ENTER_CRITICAL_SECTION */ + + while (iterPgNode != NULL) { + + if (iterPgNode->physPgPtr == physPgPtr) { + /* find address in used pages stack */ + + /* remove from list */ + if (iterPgNode->prevPhysPg != NULL) { + iterPgNode->prevPhysPg->nextPhysPg = + iterPgNode->nextPhysPg; + } + + if (iterPgNode->nextPhysPg != NULL) { + iterPgNode->nextPhysPg->prevPhysPg = + iterPgNode->prevPhysPg; + } else if (iterPgNode == usedPagesStack) { + usedPagesStack = usedPagesStack->prevPhysPg; + } else { + /* DMM_EXIT_CRITICAL_SETCTION */ + lajosdump(iterPgNode); + return DMM_SYS_ERROR; + } + + /* add to end of freepages */ + if (freePagesStack != NULL) + freePagesStack->nextPhysPg = iterPgNode; + iterPgNode->prevPhysPg = freePagesStack; + freePagesStack = iterPgNode; + freePageCnt++; + + while (freePageCnt > DMM_MNGD_PHYS_PAGES && + freePagesStack != NULL) { + iterPgNode = freePagesStack->prevPhysPg; +#ifndef __NEWCODE__ + free_page((unsigned long) + freePagesStack->physPgPtr); + kfree(freePagesStack); +#else + __free_page(freePagesStack->page_addr); + kfree(freePagesStack); +#endif + freePagesStack = iterPgNode; + freePageCnt--; + } + freePagesStack->nextPhysPg = NULL; + + /* DMM_EXIT_CRITICAL_SETCTION */ +#ifdef CHECK_STACK + check_stack(freePagesStack, "free: ", __LINE__); + check_stack(usedPagesStack, "used: ", __LINE__); +#endif + return DMM_NO_ERROR; + } + + iterPgNode = iterPgNode->prevPhysPg; + } + + /* DMM_EXIT_CRITICAL_SETCTION */ +#ifdef CHECK_STACK + check_stack(freePagesStack, "free: ", __LINE__); + check_stack(usedPagesStack, "used: ", __LINE__); +#endif + return DMM_WRONG_PARAM; +} + diff --git a/drivers/media/video/tiler/dmm_prv.h b/drivers/media/video/tiler/dmm_prv.h new file mode 100644 index 000000000000..24f775d4be33 --- /dev/null +++ b/drivers/media/video/tiler/dmm_prv.h @@ -0,0 +1,774 @@ +/* + * dmm_prv.h + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _DMM_PRV_H +#define _DMM_PRV_H + +#include "dmm_2d_alloc.h" +#include "dmm_drv.h" +#include "dmm_reg.h" + +#define __NEWCODE__ +#ifdef __NEWCODE__ + +#if 0 +#define debug(x) printk(KERN_NOTICE "%s()::%d:%s=(0x%08x)\n", \ + __func__, __LINE__, #x, (int)x); + +#define regdump(x, y) printk(KERN_NOTICE "%s()::%d:%s=(0x%08x)\n", \ + __func__, __LINE__, x, (int)y); +#else +#define debug(x) +#define regdump(x, y) +#endif + +#define DMM_BASE 0x4E000000 +#define DMM_REVISION 0x000 +#define DMM_HWINFO 0x004 +#define LISA_HWINFO 0x008 +#define DMM_SYSCONFIG 0x010 +#define LISA_LOCK 0x01C +#define LISA_MAP__0 0x040 +#define LISA_MAP__1 0x044 +#define TILER_HWINFO 0x208 +#define TILER_OR__0 0x220 +#define TILER_OR__1 0x224 +#define PAT_HWINFO 0x408 +#define PAT_GEOMETRY 0x40C +#define PAT_CONFIG 0x410 +#define PAT_VIEW__0 0x420 +#define PAT_VIEW__1 0x424 +#define PAT_VIEW_MAP__0 0x440 +#define PAT_VIEW_MAP_BASE 0x460 +#define PAT_IRQ_EOI 0x478 +#define PAT_IRQSTATUS_RAW 0x480 +#define PAT_IRQSTATUS 0x490 +#define PAT_IRQENABLE_SET 0x4A0 +#define PAT_IRQENABLE_CLR 0x4B0 +#define PAT_STATUS__0 0x4C0 +#define PAT_STATUS__1 0x4C4 +#define PAT_STATUS__2 0x4C8 +#define PAT_STATUS__3 0x4CC +#define PAT_DESCR__0 0x500 +#define PAT_AREA__0 0x504 +#define PAT_CTRL__0 0x508 +#define PAT_DATA__0 0x50C +#define PEG_HWINFO 0x608 +#define PEG_PRIO 0x620 +#define PEG_PRIO_PAT 0x640 +#endif + +#undef __DEBUG__ + +#ifdef __DEBUG__ +#define tilerdump(x) printk(KERN_NOTICE "%s():%s=(0x%08x)\n", \ + __func__, #x, (int)x); +#else +#define tilerdump(x) +#endif + +#define DMM__DMM DMM__DMM +#define DMM_PAT_AREA_IRQ (0) + +#ifndef __NEWCODE__ +#define DMM_MNGD_PHYS_PAGES (256) +#else +#define DMM_MNGD_PHYS_PAGES (16) +#endif + +#define DMM_TILE_DIMM_X_MODE_8 (32) +#define DMM_TILE_DIMM_Y_MODE_8 (32) + +#define DMM_TILE_DIMM_X_MODE_16 (32) +#define DMM_TILE_DIMM_Y_MODE_16 (16) + +#define DMM_TILE_DIMM_X_MODE_32 (16) +#define DMM_TILE_DIMM_Y_MODE_32 (16) + +#define DMM_PAGE_DIMM_X_MODE_8 (DMM_TILE_DIMM_X_MODE_8*2) +#define DMM_PAGE_DIMM_Y_MODE_8 (DMM_TILE_DIMM_Y_MODE_8*2) + +#define DMM_PAGE_DIMM_X_MODE_16 (DMM_TILE_DIMM_X_MODE_16*2) +#define DMM_PAGE_DIMM_Y_MODE_16 (DMM_TILE_DIMM_Y_MODE_16*2) + +#define DMM_PAGE_DIMM_X_MODE_32 (DMM_TILE_DIMM_X_MODE_32*2) +#define DMM_PAGE_DIMM_Y_MODE_32 (DMM_TILE_DIMM_Y_MODE_32*2) + +#define DMM_HOR_X_ADDRSHIFT_8 (0) +#define DMM_HOR_X_ADDRMASK_8 (0x3FFF) +#define DMM_HOR_X_COOR_GET_8(x)\ + (((unsigned long)x >> DMM_HOR_X_ADDRSHIFT_8) & DMM_HOR_X_ADDRMASK_8) +#define DMM_HOR_X_PAGE_COOR_GET_8(x)\ + (DMM_HOR_X_COOR_GET_8(x)/DMM_PAGE_DIMM_X_MODE_8) + +#define DMM_HOR_Y_ADDRSHIFT_8 (14) +#define DMM_HOR_Y_ADDRMASK_8 (0x1FFF) +#define DMM_HOR_Y_COOR_GET_8(x)\ + (((unsigned long)x >> DMM_HOR_Y_ADDRSHIFT_8) & DMM_HOR_Y_ADDRMASK_8) +#define DMM_HOR_Y_PAGE_COOR_GET_8(x)\ + (DMM_HOR_Y_COOR_GET_8(x)/DMM_PAGE_DIMM_Y_MODE_8) + +#define DMM_HOR_X_ADDRSHIFT_16 (1) +#define DMM_HOR_X_ADDRMASK_16 (0x7FFE) +#define DMM_HOR_X_COOR_GET_16(x) (((unsigned long)x >> \ + DMM_HOR_X_ADDRSHIFT_16) & DMM_HOR_X_ADDRMASK_16) +#define DMM_HOR_X_PAGE_COOR_GET_16(x) (DMM_HOR_X_COOR_GET_16(x) / \ + DMM_PAGE_DIMM_X_MODE_16) + +#define DMM_HOR_Y_ADDRSHIFT_16 (15) +#define DMM_HOR_Y_ADDRMASK_16 (0xFFF) +#define DMM_HOR_Y_COOR_GET_16(x) (((unsigned long)x >> \ + DMM_HOR_Y_ADDRSHIFT_16) & DMM_HOR_Y_ADDRMASK_16) +#define DMM_HOR_Y_PAGE_COOR_GET_16(x) (DMM_HOR_Y_COOR_GET_16(x) / \ + DMM_PAGE_DIMM_Y_MODE_16) + +#define DMM_HOR_X_ADDRSHIFT_32 (2) +#define DMM_HOR_X_ADDRMASK_32 (0x7FFC) +#define DMM_HOR_X_COOR_GET_32(x) (((unsigned long)x >> \ + DMM_HOR_X_ADDRSHIFT_32) & DMM_HOR_X_ADDRMASK_32) +#define DMM_HOR_X_PAGE_COOR_GET_32(x) (DMM_HOR_X_COOR_GET_32(x) / \ + DMM_PAGE_DIMM_X_MODE_32) + +#define DMM_HOR_Y_ADDRSHIFT_32 (15) +#define DMM_HOR_Y_ADDRMASK_32 (0xFFF) +#define DMM_HOR_Y_COOR_GET_32(x) (((unsigned long)x >> \ + DMM_HOR_Y_ADDRSHIFT_32) & DMM_HOR_Y_ADDRMASK_32) +#define DMM_HOR_Y_PAGE_COOR_GET_32(x) (DMM_HOR_Y_COOR_GET_32(x) / \ + DMM_PAGE_DIMM_Y_MODE_32) + +/* :TODO: to be determined */ + +#define DMM_VER_X_ADDRSHIFT_8 (14) +#define DMM_VER_X_ADDRMASK_8 (0x1FFF) +#define DMM_VER_X_COOR_GET_8(x)\ + (((unsigned long)x >> DMM_VER_X_ADDRSHIFT_8) & DMM_VER_X_ADDRMASK_8) +#define DMM_VER_X_PAGE_COOR_GET_8(x)\ + (DMM_VER_X_COOR_GET_8(x)/DMM_PAGE_DIMM_X_MODE_8) + +#define DMM_VER_Y_ADDRSHIFT_8 (0) +#define DMM_VER_Y_ADDRMASK_8 (0x3FFF) +#define DMM_VER_Y_COOR_GET_8(x)\ + (((unsigned long)x >> DMM_VER_Y_ADDRSHIFT_8) & DMM_VER_Y_ADDRMASK_8) +#define DMM_VER_Y_PAGE_COOR_GET_8(x)\ + (DMM_VER_Y_COOR_GET_8(x)/DMM_PAGE_DIMM_Y_MODE_8) + +#define DMM_VER_X_ADDRSHIFT_16 (14) +#define DMM_VER_X_ADDRMASK_16 (0x1FFF) +#define DMM_VER_X_COOR_GET_16(x) (((unsigned long)x >> \ + DMM_VER_X_ADDRSHIFT_16) & DMM_VER_X_ADDRMASK_16) +#define DMM_VER_X_PAGE_COOR_GET_16(x) (DMM_VER_X_COOR_GET_16(x) / \ + DMM_PAGE_DIMM_X_MODE_16) + +#define DMM_VER_Y_ADDRSHIFT_16 (0) +#define DMM_VER_Y_ADDRMASK_16 (0x3FFF) +#define DMM_VER_Y_COOR_GET_16(x) (((unsigned long)x >> \ + DMM_VER_Y_ADDRSHIFT_16) & DMM_VER_Y_ADDRMASK_16) +#define DMM_VER_Y_PAGE_COOR_GET_16(x) (DMM_VER_Y_COOR_GET_16(x) / \ + DMM_PAGE_DIMM_Y_MODE_16) + +#define DMM_VER_X_ADDRSHIFT_32 (15) +#define DMM_VER_X_ADDRMASK_32 (0xFFF) +#define DMM_VER_X_COOR_GET_32(x) (((unsigned long)x >> \ + DMM_VER_X_ADDRSHIFT_32) & DMM_VER_X_ADDRMASK_32) +#define DMM_VER_X_PAGE_COOR_GET_32(x) (DMM_VER_X_COOR_GET_32(x) / \ + DMM_PAGE_DIMM_X_MODE_32) + +#define DMM_VER_Y_ADDRSHIFT_32 (0) +#define DMM_VER_Y_ADDRMASK_32 (0x7FFF) +#define DMM_VER_Y_COOR_GET_32(x) (((unsigned long)x >> \ + DMM_VER_Y_ADDRSHIFT_32) & DMM_VER_Y_ADDRMASK_32) +#define DMM_VER_Y_PAGE_COOR_GET_32(x) (DMM_VER_Y_COOR_GET_32(x) / \ + DMM_PAGE_DIMM_Y_MODE_32) + +#define DMM_TILER_ALIAS_BASE (0x60000000) +#define DMM_ALIAS_VIEW_CLEAR (~0xE0000000) + +#define DMM_ACC_MODE_SHIFT (27) +#define DMM_ACC_MODE_MASK (3) +#define DMM_GET_ACC_MODE(x) ((enum dmmMemoryAccessT)\ + (((unsigned long)x &\ + (DMM_ACC_MODE_MASK<<DMM_ACC_MODE_SHIFT))>>DMM_ACC_MODE_SHIFT)) + +#define DMM_X_INVERT_SHIFT (29) +#define DMM_GET_X_INVERTED(x)\ + ((((unsigned long)x & (1<<DMM_X_INVERT_SHIFT)) > 0) ? 1 : 0) + +#define DMM_Y_INVERT_SHIFT (30) +#define DMM_GET_Y_INVERTED(x)\ + ((((unsigned long)x & (1<<DMM_Y_INVERT_SHIFT)) > 0) ? 1 : 0) + +#define DMM_ROTATION_SHIFT (31) +#define DMM_GET_ROTATED(x)\ +((((unsigned long)x & ((unsigned long)1<<DMM_ROTATION_SHIFT)) > 0) ? 1 : 0) + +#define DMM_COMPOSE_TILER_ALIAS_PTR(x, accM)\ +((void *)(DMM_TILER_ALIAS_BASE | \ + (unsigned long)x | (accM << DMM_ACC_MODE_SHIFT))) +#define DMM_COMPOSE_TILER_PTR(x, rot, yInv, xInv, accM)\ +((void *)((unsigned long)x | (rot << DMM_ROTATION_SHIFT) |\ +(yInv << DMM_Y_INVERT_SHIFT) | (xInv << DMM_X_INVERT_SHIFT) |\ +(accM << DMM_ACC_MODE_SHIFT))) + +/*--------data declarations -----------------------------------*/ +/** @struc dmmPhysPgLLT +* Structure defining Dmm physical memory pages managment linked list. */ +struct dmmPhysPgLLT { + struct dmmPhysPgLLT *nextPhysPg; + struct dmmPhysPgLLT *prevPhysPg; + unsigned long *physPgPtr; +#ifdef __NEWCODE__ + struct page *page_addr; +#endif +}; + +/** @struc dmmHwdCtxT +* Structure defining Dmm driver hardware context. */ +struct dmmHwdCtxT { + signed long dmmOpenInstances; + struct dmmPATIrqEventsT patIrqEvnt0; + struct dmmPATIrqEventsT patIrqEvnt1; + struct dmmPATIrqEventsT patIrqEvnt2; + struct dmmPATIrqEventsT patIrqEvnt3; +}; + +/** @struc dmmInstanceCtxT +* Structure defining Dmm driver context. */ +struct dmmInstanceCtxT { + struct dmmHwdCtxT dmmHwdCtx; + struct MSP_Dmm_eventNotificationT dmmMspCtx; + struct dmmTILERContCtxT dmmTilerCtx; +}; + +/** @struc dmmPatRefillManip +* Enumeration of possible area refill manipulations. */ +enum dmmPatRefillManipT { + DMMP_REMAP, + DMMP_SWAP, + DMMP_MOVE +}; + +#ifdef __DMM_DEBUG_BUILD__ +#define DBG_OVERLAP_TEST(x) overlapping_test(x) + +struct DMM_rStruct_t { + unsigned long DMM_REVISION; + unsigned long DMM_HWINFO; + unsigned long DMM_LISA_HWINFO; + unsigned long DMM_SYSCONFIG; + unsigned long DMM_LISA_LOCK; + unsigned char filler1[32]; + unsigned long DMM_LISA_MAP__0; + unsigned long DMM_LISA_MAP__1; + unsigned long DMM_LISA_MAP__2; + unsigned long DMM_LISA_MAP__3; + unsigned char filler2[440]; + unsigned long DMM_TILER_HWINFO; + unsigned char filler3[20]; + unsigned long DMM_TILER_OR0; + unsigned long DMM_TILER_OR1; + unsigned char filler4[476]; + unsigned long DMM_PAT_HWINFO; + unsigned long DMM_PAT_GEOMETRY; + unsigned char filler5[16]; + unsigned long DMM_PAT_VIEW0; + unsigned long DMM_PAT_VIEW1; + unsigned char filler6[24]; + unsigned long DMM_PAT_VIEW_MAP__0; + unsigned long DMM_PAT_VIEW_MAP__1; + unsigned long DMM_PAT_VIEW_MAP__2; + unsigned long DMM_PAT_VIEW_MAP__3; + unsigned char filler7[16]; + unsigned long DMM_PAT_VIEW_MAP_BASE; + unsigned char filler8[28]; + unsigned long DMM_PAT_STATUS__0; + unsigned long DMM_PAT_STATUS__1; + unsigned long DMM_PAT_STATUS__2; + unsigned long DMM_PAT_STATUS__3; + unsigned char filler9[48]; + unsigned long DMM_PAT_IRQSTATUS_RAW; + unsigned char filler10[12]; + unsigned long DMM_PAT_IRQSTATUS; + unsigned char filler11[12]; + unsigned long DMM_PAT_IRQENABLE_SET; + unsigned char filler12[12]; + unsigned long DMM_PAT_IRQENABLE_CLR; + unsigned char filler13[12]; + unsigned long DMM_PAT_DESCR__0; + unsigned long DMM_PAT_AREA__0; + unsigned long DMM_PAT_CTRL__0; + unsigned long DMM_PAT_DATA__0; + unsigned long DMM_PAT_DESCR__1; + unsigned long DMM_PAT_AREA__1; + unsigned long DMM_PAT_CTRL__1; + unsigned long DMM_PAT_DATA__1; + unsigned long DMM_PAT_DESCR__2; + unsigned long DMM_PAT_AREA__2; + unsigned long DMM_PAT_CTRL__2; + unsigned long DMM_PAT_DATA__2; + unsigned long DMM_PAT_DESCR__3; + unsigned long DMM_PAT_AREA__3; + unsigned long DMM_PAT_CTRL__3; + unsigned long DMM_PAT_DATA__3; + unsigned char filler14[200]; + unsigned long DMM_PEG_HWINFO; + unsigned char filler15[20]; + unsigned long DMM_PEG_PRIO0; + unsigned long DMM_PEG_PRIO1; + unsigned char filler16[24]; + unsigned long DMM_PEG_PRIO_PAT; +}; + +#else + +#define DBG_OVERLAP_TEST(x) + +#endif /* __DMM_DEBUG_BUILD__ */ + +/*--------function prototypes ---------------------------------*/ +/* ========================================================================== */ +/** + * dmm_tiler_container_map_area() + * + * @brief Allocates a 2D TILER buffer - virtual 2D allocation, + * descriptor and TILER system pointer updating. + * + * @param dmmTilerCtx - dmmTILERContCtxT* - [in] Tiler context instance. + * + * @param sizeWidth - unsigned short - [in] Width of buffer + * (in container elements). + * + * @param sizeHeight - unsigned short - [in] Height of buffer (in container + * elements). + * + * @param contMod - dmmMemoryAccessT - [in] Container access mode - for element + * sizes. + * + * @param allocedPtr - void ** - [out] The allocated buffer system pointer is + * provided through this double pointer. If no buffer is available this pointer + * is set to NULL. + * + * @param bufferMappedZone - dmmTILERContPageAreaT** - [out] Description of the + * 2D area that is mapped in the TILER container is provided in this structure. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmMemoryAccessT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_container_map_area( + struct dmmTILERContCtxT *dmmTilerCtx, + unsigned long sizeWidth, + unsigned long sizeHeight, + enum dmmMemoryAccessT contMod, + void **allocedPtr, + struct dmmTILERContPageAreaT **bufferMappedZone); + +/* ========================================================================== */ +/** + * dmm_tiler_populate_pat_page_entry_data() + * + * @brief Populates an aray with PAT page address entries. + * + * @param bfrSize - unsigned long - [in] Size of the buffer which will be used + * to generate page entries for. + * + * @param pageEntries - unsigned long** - [out] The address to the allocated + * page entry data array, aligned due to hardware specification. + * + * @param pageEntriesSpace - unsigned long** - [out] The address to the + * allocated page entry data array for deallocation purposes. + * + * @param custmPagesPtr - void * - [in] Pointer to a custom created memory + * pages list. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_populate_pat_page_entry_data(unsigned long bfrSize, + unsigned long **pageEntries, + unsigned long **pageEntriesSpace, + void *custmPagesPtr); +/* ========================================================================== */ +/** + * dmm_tiler_swap_pat_page_entry_data() + * + * @brief Swaps entries in an aray with PAT page address entries. + * + * @param bfrSize - unsigned long - [in] Size of the buffer which will be used + * to generate page entries for. + * + * @param pageEntries - unsigned long* - [in] The address to the allocated + * page entry data array, aligned due to hardware specification. + * + * @param affectedArea - PATAreaT* - [in] Area that will be affected. + * + * @param destX - unsigned short - [in] Destination coordinate X. + * + * @param destY - unsigned short - [in] Destination coordinate Y. + * + * @param stride - unsigned short - [in] Stride of the area. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_swap_pat_page_entry_data(unsigned long numPages, + unsigned long *pageEntries, + struct PATAreaT *affectedArea, + unsigned short destX, + unsigned short destY, + unsigned short stride); + +/* ========================================================================== */ +/** + * dmm_tiler_container_unmap_area() + * + * @brief Frees a 2D virtual TILER container buffer. \ + * + * @param dmmTilerCtx - dmmTILERContCtxT* - [in] Tiler context instance. + * + * @param bufferMappedZone - dmmTILERContPageAreaT* - [in] Description of the 2D + * area that is mapped in the TILER container is provided in this structure. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_tiler_container_unmap_area( + struct dmmTILERContCtxT *dmmTilerCtx, + struct dmmTILERContPageAreaT *bufferMappedZone); + +/* ========================================================================== */ +/** + * dmm_tiler_get_area_from_sysptr() + * + * @brief Gets a 2D area descriptor from the Tiler system pointer. + * + * @param dmmTilerCtx - dmmTILERContCtxT* - [in] Tiler context instance. + * + * @param sysPtr - void * - [in] Tiler system pointer to a 2D area. + * + * @return dmmTILERContPageAreaT* Pointer to the 2D area descriptor, NULL if + * error is encountered during extraction. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +struct dmmTILERContPageAreaT *dmm_tiler_get_area_from_sysptr( + struct dmmTILERContCtxT *dmmTilerCtx, void *sysPtr); + +/* ========================================================================== */ +/** + * dmmEventReport() + * + * @brief Signal events to the user modules (wrapper to the user supplied + * callback). + * + * @param dmmInstanceCtxPtr - void * - [in] Dmm context instance. + * + * @param mspEvent - MSP_EVENT_TYPE - [in] MSP event type. + * + * @param eData - void * - [in] MSP event data pointer. + * + * @return errorCodeT error if event can't be signaled. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see dmmTILERContCtxT for further detail. + */ +/* ========================================================================== */ +/* enum errorCodeT dmmEventReport(void * dmmInstanceCtxPtr, + MSP_EVENT_TYPE mspEvent, void * eData); */ + +/* ========================================================================== */ +/** +* dmmPatIrqHandler() +* +* @brief Interrupt handler for PAT area status updates. +* +* @param data - UArg - +* +* @return none +* +* @pre There is no pre conditions. +* +* @post There is no post conditions. +* +* @see +* +*/ +/* ========================================================================== */ +/* Void dmmPatIrqHandler(UArg data); */ + +/* ========================================================================== */ +/** + * dmm_phys_page_rep_init() + * + * @brief Initializes the physical memory page repository instance. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_phys_page_rep_init(void); + +/* ========================================================================== */ +/** + * dmmPhysPageRepDeinit() + * + * @brief Releases all resources held by the physical memory page repository + * instance. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_phys_page_rep_deinit(void); + +/* ========================================================================== */ +/** + * dmm_get_phys_pages() + * + * @brief Return a pointer to a physical memory page and mark it as used. + * + * @return unsigned long* pointer to a physical memory page, NULL if error + * occurs. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +unsigned long *dmm_get_phys_page(void); + +/* ========================================================================== */ +/** + * dmm_free_phys_page() + * + * @brief Frees a specified physical memory page. + * + * @param physPgPtr - unsigned long* - [in] The address of the allocated + * physical page that should be freed. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_free_phys_page(unsigned long *physPgPtr); + +/* ========================================================================== */ +/** + * dmm_pat_phy2virt_mapping() + * + * @brief Mapping physical memory pages to virtual containers. + * + * @param bufferMappedZone - dmmTILERContPageAreaT* - [in] The tiled buffer + * descriptor. + * + * @param custmPagesPtr - void * - [in] Pointer to the custom supplied + * physical pages. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_phy2virt_mapping( + struct dmmTILERContPageAreaT *bufferMappedZone, + void *custmPagesPtr); + +/* ========================================================================== */ +/** + * dmm_pat_start_refill() + * + * @brief Creates a PAT area descriptor and starts the refill. + * + * @param bufferMappedZone - dmmTILERContPageAreaT* - [in] The tiled buffer + * descriptor. + * + * @return errorCodeT + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see errorCodeT, dmmTILERContPageAreaT for further detail. + */ +/* ========================================================================== */ +enum errorCodeT dmm_pat_start_refill( + struct dmmTILERContPageAreaT *bufferMappedZone); + +/*--------function prototypes ---------------------------------*/ + +/* ========================================================================== */ +/** + * dmm_enter_critical_section() + * + * @brief Enter a critical code execution section. + * + * @return none + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void dmm_enter_critical_section(void); + +/* ========================================================================== */ +/** + * dmm_leave_critical_section() + * + * @brief Exit a critical code execution section. + * + * @return none + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void dmm_leave_critical_section(void); + +/* ========================================================================== */ +/** + * dmm_memory_size_alloc() + * + * @brief Allocates a physical memory page. + * + * @param pageNum - unsigned long - [in] Number of physical pages to allocate. + * + * @return void * pointer to the physical memory. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void *dmm_memory_size_alloc(unsigned long memSize); + +/* ========================================================================== */ +/** + * dmm_memory_page_alloc() + * + * @brief Allocates a physical memory page. + * + * @param pageNum - unsigned long - [in] Number of physical pages to allocate. + * + * @return void * pointer to the physical memory. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void *dmm_memory_page_alloc(unsigned long pageNum); + +/* ========================================================================== */ +/** + * dmm_memory_page_free() + * + * @brief Frees a physical memory page. + * + * @param pagePtr - void * - [in] Pointer to the physical pages to free. + * + * @return none + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void dmm_memory_page_free(void *pagePtr); + +/* ========================================================================== */ +/** + * overlapping_test() + * + * @brief Performs an area overlap test for errors. Debug only. + * + * @param tlrCtx - dmmTILERContCtxT* - [in] TILER context structure. + * + * @return int: 1 if overlapping is detected. + * + * @pre There is no pre conditions. + * + * @post There is no post conditions. + * + * @see + */ +/* ========================================================================== */ +void overlapping_test(struct dmmTILERContCtxT *dmmTilerCtx); + +#endif /* _DMM_PRV_H */ + diff --git a/drivers/media/video/tiler/dmm_reg.h b/drivers/media/video/tiler/dmm_reg.h new file mode 100644 index 000000000000..dbd8683c58b4 --- /dev/null +++ b/drivers/media/video/tiler/dmm_reg.h @@ -0,0 +1,29 @@ +/* + * dmm_reg.h + * + * DMM driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef DMM_REG_H +#define DMM_REG_H + +#define BITS_32(in_NbBits) \ + ((((unsigned long)1 << in_NbBits) - 1) | ((unsigned long)1 << in_NbBits)) + +#define BITFIELD_32(in_UpBit, in_LowBit) \ + (BITS_32(in_UpBit) & ~((BITS_32(in_LowBit)) >> 1)) + +#define BITFIELD BITFIELD_32 + +extern unsigned long *dmm_virt_base_addr; +#endif + diff --git a/drivers/media/video/tiler/tiler.h b/drivers/media/video/tiler/tiler.h new file mode 100755 index 000000000000..54fa294f38d7 --- /dev/null +++ b/drivers/media/video/tiler/tiler.h @@ -0,0 +1,112 @@ +/* + * tiler.h + * + * TILER driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _TILER_H_ +#define _TILER_H_ + +#define TILER_MEM_8BIT 0x60000000 +#define TILER_MEM_16BIT 0x68000000 +#define TILER_MEM_32BIT 0x70000000 +#define TILER_MEM_PAGED 0x78000000 +#define TILER_MEM_END 0x80000000 + +#define TILER_PAGESIZE 0x1000 +#define TILER_WIDTH 256 +#define TILER_HEIGHT 128 +#define TILER_BLOCK_WIDTH 64 +#define TILER_BLOCK_HEIGHT 64 +#define TILER_LENGTH (TILER_WIDTH * TILER_HEIGHT * TILER_PAGESIZE) + +#define TILER_DEVICE_PATH "/dev/tiler" +#define TILER_MAX_NUM_BLOCKS 16 + +extern int errno; + +enum tiler_fmt { + TILFMT_MIN = -1, + TILFMT_INVALID = -1, + TILFMT_NONE = 0, + TILFMT_8BIT = 1, + TILFMT_16BIT = 2, + TILFMT_32BIT = 3, + TILFMT_PAGE = 4, + TILFMT_MAX = 4 +}; + +struct area { + unsigned short width; + unsigned short height; +}; + +struct tiler_block_info { + enum tiler_fmt fmt; + union { + struct area area; + unsigned long len; + } dim; + unsigned long stride; + void *ptr; + unsigned long ssptr; +}; + +struct tiler_buf_info { + int num_blocks; + struct tiler_block_info blocks[TILER_MAX_NUM_BLOCKS]; + int offset; +}; + +#define TILIOC_OPEN _IOWR('z', 100, unsigned long) +#define TILIOC_GBUF _IOWR('z', 101, unsigned long) +#define TILIOC_FBUF _IOWR('z', 102, unsigned long) +#define TILIOC_CLOSE _IOWR('z', 103, unsigned long) +#define TILIOC_GSSP _IOWR('z', 104, unsigned long) +#define TILIOC_MBUF _IOWR('z', 105, unsigned long) +#define TILIOC_QBUF _IOWR('z', 106, unsigned long) +#define TILIOC_RBUF _IOWR('z', 109, unsigned long) +#define TILIOC_URBUF _IOWR('z', 110, unsigned long) +#define TILIOC_QUERY_BLK _IOWR('z', 111, unsigned long) + +struct dmmViewOrientT { + unsigned char dmm90Rotate; + unsigned char dmmXInvert; + unsigned char dmmYInvert; +}; + +int +tiler_alloc_buf(enum tiler_fmt fmt, + unsigned long width, + unsigned long height, + void **sysptr); + +int +tiler_free_buf(unsigned long sysptr); + +unsigned long +tiler_reorient_addr(unsigned long tsptr, struct dmmViewOrientT orient); + +unsigned long +tiler_get_natural_addr(void *sysPtr); + +unsigned long +tiler_reorient_topleft(unsigned long tsptr, + struct dmmViewOrientT orient, + unsigned int validDataWidth, + unsigned int validDataHeight); + +void +tiler_rotate_view(struct dmmViewOrientT *orient, unsigned long rotation); + +#endif diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index b91d66a767d7..3579a0a887ad 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -421,6 +421,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; case V4L2_CID_COLOR_KILLER: return "Color Killer"; case V4L2_CID_COLORFX: return "Color Effects"; + case V4L2_CID_ROTATE: return "Rotate"; + case V4L2_CID_BG_COLOR: return "Background color"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -546,6 +548,13 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; min = max = step = def = 0; break; + case V4L2_CID_BG_COLOR: + qctrl->type = V4L2_CTRL_TYPE_INTEGER; + step = 1; + min = 0; + /* Max is calculated as RGB888 that is 2^12*/ + max = 0xFFFFFF; + break; default: qctrl->type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index f2afc4e08379..eb0e962a02a0 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -277,6 +277,10 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT", [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", + + [_IOC_NR(VIDIOC_S_COL_SPC_CONV)] = "VIDIOC_S_COL_SPC_CONV", + [_IOC_NR(VIDIOC_G_COL_SPC_CONV)] = "VIDIOC_G_COL_SPC_CONV", + #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -1784,6 +1788,26 @@ static long __video_do_ioctl(struct file *file, break; } + /*---------------Color space conversion------------------------------*/ + case VIDIOC_S_COL_SPC_CONV: + { + struct v4l2_color_space_conversion *p = arg; + if (!ops->vidioc_s_color_space_conv) + break; + + ret = ops->vidioc_s_color_space_conv(file, fh, p); + break; + } + + case VIDIOC_G_COL_SPC_CONV: + { + struct v4l2_color_space_conversion *p = arg; + if (!ops->vidioc_g_color_space_conv) + break; + ret = ops->vidioc_g_color_space_conv(file, fh, p); + break; + } + default: { if (!ops->vidioc_default) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3b54b3940178..e09367a60446 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2149,6 +2149,7 @@ config FB_BROADSHEET a bridge adapter. source "drivers/video/omap/Kconfig" +source "drivers/video/omap2/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 01a819f47371..01bf7a50ec12 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -123,6 +123,7 @@ obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ +obj-$(CONFIG_OMAP2_DSS) += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 44408850e2eb..0c8f3a52865e 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,6 +1,7 @@ config FB_OMAP tristate "OMAP frame buffer support (EXPERIMENTAL)" - depends on FB && ARCH_OMAP + depends on FB && ARCH_OMAP && (OMAP2_DSS = "n") + select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -9,7 +10,7 @@ config FB_OMAP config FB_OMAP_BOOTLOADER_INIT bool "Check bootloader initialization" - depends on FB_OMAP + depends on FB_OMAP || FB_OMAP2 help Say Y here if you want to enable checking if the bootloader has already initialized the display controller. In this case the diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c index 9dfcf39d3367..782b48843a7d 100644 --- a/drivers/video/omap/blizzard.c +++ b/drivers/video/omap/blizzard.c @@ -25,9 +25,9 @@ #include <linux/fb.h> #include <linux/delay.h> #include <linux/clk.h> +#include <linux/omapfb.h> #include <mach/dma.h> -#include <mach/omapfb.h> #include <mach/blizzard.h> #include "dispc.h" diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index cbd3f8d7fab1..9490d72be263 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -24,9 +24,9 @@ #include <linux/vmalloc.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/omapfb.h> #include <mach/sram.h> -#include <mach/omapfb.h> #include <mach/board.h> #include "dispc.h" @@ -35,6 +35,17 @@ #define DSS_BASE 0x48050000 #define DSS_SYSCONFIG 0x0010 +#ifndef CONFIG_ARCH_OMAP4 + /* DSS */ + #define DSS_BASE 0x48050000 + /* DISPLAY CONTROLLER */ + #define DISPC_BASE 0x48050400 +#else + /* DSS */ + #define DSS_BASE 0x48042000 + /* DISPLAY CONTROLLER */ + #define DISPC_BASE 0x48043000 +#endif #define DISPC_BASE 0x48050400 @@ -856,6 +867,7 @@ void omap_dispc_free_irq(void) } EXPORT_SYMBOL(omap_dispc_free_irq); +#ifndef CONFIG_ARCH_OMAP4 static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) { u32 stat = dispc_read_reg(DISPC_IRQSTATUS); @@ -877,30 +889,32 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) return IRQ_HANDLED; } +#endif static int get_dss_clocks(void) { - dispc.dss_ick = clk_get(dispc.fbdev->dev, "ick"); - if (IS_ERR(dispc.dss_ick)) { - dev_err(dispc.fbdev->dev, "can't get ick\n"); - return PTR_ERR(dispc.dss_ick); - } + if (!cpu_is_omap44xx()) { + dispc.dss_ick = clk_get(dispc.fbdev->dev, "ick"); + if (IS_ERR(dispc.dss_ick)) { + dev_err(dispc.fbdev->dev, "can't get ick\n"); + return PTR_ERR(dispc.dss_ick); + } - dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck"); - if (IS_ERR(dispc.dss1_fck)) { - dev_err(dispc.fbdev->dev, "can't get dss1_fck\n"); - clk_put(dispc.dss_ick); - return PTR_ERR(dispc.dss1_fck); - } + dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck"); + if (IS_ERR(dispc.dss1_fck)) { + dev_err(dispc.fbdev->dev, "can't get dss1_fck\n"); + clk_put(dispc.dss_ick); + return PTR_ERR(dispc.dss1_fck); + } - dispc.dss_54m_fck = clk_get(dispc.fbdev->dev, "tv_fck"); - if (IS_ERR(dispc.dss_54m_fck)) { - dev_err(dispc.fbdev->dev, "can't get tv_fck\n"); - clk_put(dispc.dss_ick); - clk_put(dispc.dss1_fck); - return PTR_ERR(dispc.dss_54m_fck); + dispc.dss_54m_fck = clk_get(dispc.fbdev->dev, "tv_fck"); + if (IS_ERR(dispc.dss_54m_fck)) { + dev_err(dispc.fbdev->dev, "can't get tv_fck\n"); + clk_put(dispc.dss_ick); + clk_put(dispc.dss1_fck); + return PTR_ERR(dispc.dss_54m_fck); + } } - return 0; } diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c index 5d4f34887a22..d865b55b8269 100644 --- a/drivers/video/omap/hwa742.c +++ b/drivers/video/omap/hwa742.c @@ -25,9 +25,9 @@ #include <linux/fb.h> #include <linux/delay.h> #include <linux/clk.h> +#include <linux/omapfb.h> #include <mach/dma.h> -#include <mach/omapfb.h> #include <mach/hwa742.h> #define HWA742_REV_CODE_REG 0x0 diff --git a/drivers/video/omap/lcd_4430sdp.c b/drivers/video/omap/lcd_4430sdp.c new file mode 100644 index 000000000000..1065d2acfbef --- /dev/null +++ b/drivers/video/omap/lcd_4430sdp.c @@ -0,0 +1,128 @@ +/* + * LCD panel support for the TI 4430SDP board + * + * Copyright (C) 2007 MontaVista + * Author: Hunyue Yau <hyau@mvista.com> + * + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/omapfb.h> + +#include <mach/mux.h> +#include <asm/mach-types.h> + +#define LCD_PIXCLOCK_MAX 5400 /* freq 5.4 MHz */ + +static int sdp4430_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void sdp4430_panel_cleanup(struct lcd_panel *panel) +{ + return; +} + +static int sdp4430_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void sdp4430_panel_disable(struct lcd_panel *panel) +{ + return; +} + +static unsigned long sdp4430_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel sdp4430_panel = { + .name = "sdp4430", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = LCD_PIXCLOCK_MAX, + + .init = sdp4430_panel_init, + .cleanup = sdp4430_panel_cleanup, + .enable = sdp4430_panel_enable, + .disable = sdp4430_panel_disable, + .get_caps = sdp4430_panel_get_caps, +}; + +static int sdp4430_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&sdp4430_panel); + return 0; +} + +static int sdp4430_panel_remove(struct platform_device *pdev) +{ + return 0; +} +static int sdp4430_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int sdp4430_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver sdp4430_panel_driver = { + .probe = sdp4430_panel_probe, + .remove = sdp4430_panel_remove, + .suspend = sdp4430_panel_suspend, + .resume = sdp4430_panel_resume, + + .driver = { + .name = "sdp4430_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init sdp4430_panel_drv_init(void) +{ + return platform_driver_register(&sdp4430_panel_driver); +} + +static void __exit sdp4430_panel_drv_exit(void) +{ + platform_driver_unregister(&sdp4430_panel_driver); + return; +} + +module_init(sdp4430_panel_drv_init); +module_exit(sdp4430_panel_drv_exit); diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c index 2486237ebba5..f7264ea35d35 100644 --- a/drivers/video/omap/lcd_h3.c +++ b/drivers/video/omap/lcd_h3.c @@ -22,9 +22,9 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/i2c/tps65010.h> +#include <linux/omapfb.h> #include <mach/gpio.h> -#include <mach/omapfb.h> #define MODULE_NAME "omapfb-lcd_h3" diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c index 6ff56430341b..d72df0c16d79 100644 --- a/drivers/video/omap/lcd_h4.c +++ b/drivers/video/omap/lcd_h4.c @@ -21,8 +21,7 @@ #include <linux/module.h> #include <linux/platform_device.h> - -#include <mach/omapfb.h> +#include <linux/omapfb.h> static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) { diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c index 6953ed4b5820..f6e05d7f080b 100644 --- a/drivers/video/omap/lcd_inn1510.c +++ b/drivers/video/omap/lcd_inn1510.c @@ -22,9 +22,9 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/omapfb.h> #include <mach/fpga.h> -#include <mach/omapfb.h> static int innovator1510_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c index 4c4f7ee6d733..c599e41f9a42 100644 --- a/drivers/video/omap/lcd_inn1610.c +++ b/drivers/video/omap/lcd_inn1610.c @@ -21,9 +21,9 @@ #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/omapfb.h> #include <mach/gpio.h> -#include <mach/omapfb.h> #define MODULE_NAME "omapfb-lcd_h3" diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c index 379c96d36da5..d6b193ec6a82 100644 --- a/drivers/video/omap/lcd_osk.c +++ b/drivers/video/omap/lcd_osk.c @@ -22,10 +22,10 @@ #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/omapfb.h> #include <mach/gpio.h> #include <mach/mux.h> -#include <mach/omapfb.h> static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) { diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c index 218317366e6e..dcb456c39959 100644 --- a/drivers/video/omap/lcd_palmte.c +++ b/drivers/video/omap/lcd_palmte.c @@ -22,9 +22,9 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/omapfb.h> #include <mach/fpga.h> -#include <mach/omapfb.h> static int palmte_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcd_palmtt.c b/drivers/video/omap/lcd_palmtt.c index 57b0f6cf6a5a..e8adab8a76fa 100644 --- a/drivers/video/omap/lcd_palmtt.c +++ b/drivers/video/omap/lcd_palmtt.c @@ -28,9 +28,9 @@ GPIO13 - screen blanking #include <linux/platform_device.h> #include <linux/module.h> #include <linux/io.h> +#include <linux/omapfb.h> #include <mach/gpio.h> -#include <mach/omapfb.h> static int palmtt_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcd_palmz71.c b/drivers/video/omap/lcd_palmz71.c index d33d78b11723..d5b3f82b26a2 100644 --- a/drivers/video/omap/lcd_palmz71.c +++ b/drivers/video/omap/lcd_palmz71.c @@ -23,8 +23,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> - -#include <mach/omapfb.h> +#include <linux/omapfb.h> static int palmz71_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c index ab3949256677..633e33ce54a6 100644 --- a/drivers/video/omap/lcdc.c +++ b/drivers/video/omap/lcdc.c @@ -28,9 +28,9 @@ #include <linux/dma-mapping.h> #include <linux/vmalloc.h> #include <linux/clk.h> +#include <linux/omapfb.h> #include <mach/dma.h> -#include <mach/omapfb.h> #include <asm/mach-types.h> diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 8862233d57b6..f67c3c61eb42 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -27,9 +27,9 @@ #include <linux/platform_device.h> #include <linux/mm.h> #include <linux/uaccess.h> +#include <linux/omapfb.h> #include <mach/dma.h> -#include <mach/omapfb.h> #include "lcdc.h" #include "dispc.h" diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c index 9332d6ca6456..690a48bbc6de 100644 --- a/drivers/video/omap/rfbi.c +++ b/drivers/video/omap/rfbi.c @@ -26,8 +26,7 @@ #include <linux/interrupt.h> #include <linux/clk.h> #include <linux/io.h> - -#include <mach/omapfb.h> +#include <linux/omapfb.h> #include "dispc.h" diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c index a76946220249..cab398f81ecc 100644 --- a/drivers/video/omap/sossi.c +++ b/drivers/video/omap/sossi.c @@ -23,9 +23,9 @@ #include <linux/clk.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/omapfb.h> #include <mach/dma.h> -#include <mach/omapfb.h> #include "lcdc.h" diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig new file mode 100644 index 000000000000..89bf2107bb2a --- /dev/null +++ b/drivers/video/omap2/Kconfig @@ -0,0 +1,3 @@ +source "drivers/video/omap2/dss/Kconfig" +source "drivers/video/omap2/displays/Kconfig" +source "drivers/video/omap2/omapfb/Kconfig" diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile new file mode 100644 index 000000000000..72134dbcb165 --- /dev/null +++ b/drivers/video/omap2/Makefile @@ -0,0 +1,4 @@ +# OMAP2/3 Display Subsystem +obj-y += dss/ +obj-y += displays/ +obj-y += omapfb/ diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig new file mode 100644 index 000000000000..1c582bdacdc0 --- /dev/null +++ b/drivers/video/omap2/displays/Kconfig @@ -0,0 +1,49 @@ +menu "OMAP2/3 Display Device Drivers" + depends on OMAP2_DSS + +config PANEL_GENERIC + tristate "Generic Panel" + help + Generic panel driver. + Used for DVI output for Beagle and OMAP3 SDP. + +config PANEL_SAMSUNG_LTE430WQ_F0C + tristate "Samsung LTE430WQ-F0C LCD Panel" + depends on OMAP2_DSS + help + LCD Panel used on Overo Palo43 + +config PANEL_SHARP_LS037V7DW01 + tristate "Sharp LS037V7DW01 LCD Panel" + depends on OMAP2_DSS + help + LCD Panel used in TI's SDP3430 and EVM boards + +config PANEL_N800 + tristate "Panel N8x0" + help + N8x0 LCD (hack) + +config CTRL_BLIZZARD + tristate "Blizzard Controller" + help + Blizzard Controller (hack) + +config PANEL_ACX565AKM + tristate "ACX565AKM LCD Panel" + depends on OMAP2_DSS_SDI + select BACKLIGHT_CLASS_DEVICE + help + LCD Panel used in RX51 + +config LCD_4430SDP + tristate "OMAP4 LCD Panel" + help + LCD Panel used in OMAP4 + +config PANEL_4430SDP_TAAL + tristate "OMAP4 LCD TAAL Panel" + help + DSI LCD Panel used in OMAP4 + +endmenu diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile new file mode 100644 index 000000000000..bab01e1dbc20 --- /dev/null +++ b/drivers/video/omap2/displays/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o +obj-$(CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C) += panel-samsung-lte430wq-f0c.o +obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o + +obj-$(CONFIG_CTRL_BLIZZARD) += ctrl-blizzard.o +obj-$(CONFIG_PANEL_N800) += panel-n800.o + +obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o + +obj-$(CONFIG_LCD_4430SDP) += lcd_4430sdp.o +obj-$(CONFIG_PANEL_4430SDP_TAAL) += panel-taal.o diff --git a/drivers/video/omap2/displays/ctrl-blizzard.c b/drivers/video/omap2/displays/ctrl-blizzard.c new file mode 100644 index 000000000000..6698e4df8368 --- /dev/null +++ b/drivers/video/omap2/displays/ctrl-blizzard.c @@ -0,0 +1,279 @@ + +//#define DEBUG + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <mach/display.h> +#include <mach/dma.h> + +#ifdef DEBUG +#define DBG(format, ...) printk(KERN_DEBUG "Blizzard: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define BLIZZARD_REV_CODE 0x00 +#define BLIZZARD_CONFIG 0x02 +#define BLIZZARD_PLL_DIV 0x04 +#define BLIZZARD_PLL_LOCK_RANGE 0x06 +#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08 +#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a +#define BLIZZARD_PLL_MODE 0x0c +#define BLIZZARD_CLK_SRC 0x0e +#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 +#define BLIZZARD_MEM_BANK0_STATUS 0x14 +#define BLIZZARD_PANEL_CONFIGURATION 0x28 +#define BLIZZARD_HDISP 0x2a +#define BLIZZARD_HNDP 0x2c +#define BLIZZARD_VDISP0 0x2e +#define BLIZZARD_VDISP1 0x30 +#define BLIZZARD_VNDP 0x32 +#define BLIZZARD_HSW 0x34 +#define BLIZZARD_VSW 0x38 +#define BLIZZARD_DISPLAY_MODE 0x68 +#define BLIZZARD_INPUT_WIN_X_START_0 0x6c +#define BLIZZARD_DATA_SOURCE_SELECT 0x8e +#define BLIZZARD_DISP_MEM_DATA_PORT 0x90 +#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92 +#define BLIZZARD_POWER_SAVE 0xE6 +#define BLIZZARD_NDISP_CTRL_STATUS 0xE8 + +/* Data source select */ +/* For S1D13745 */ +#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00 +#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01 +#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04 +#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05 +/* For S1D13744 */ +#define BLIZZARD_SRC_WRITE_LCD 0x00 +#define BLIZZARD_SRC_BLT_LCD 0x06 + +#define BLIZZARD_COLOR_RGB565 0x01 +#define BLIZZARD_COLOR_YUV420 0x09 + +#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */ +#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */ + +#define BLIZZARD_AUTO_UPDATE_TIME (HZ / 20) + + + +static struct { + int version; +} blizzard; + + +static inline void blizzard_cmd(u8 cmd) +{ + omap_rfbi_write_command(&cmd, 1); +} + +static inline void blizzard_write(u8 cmd, const u8 *buf, int len) +{ + omap_rfbi_write_command(&cmd, 1); + omap_rfbi_write_data(buf, len); +} + +static inline void blizzard_read(u8 cmd, u8 *buf, int len) +{ + omap_rfbi_write_command(&cmd, 1); + omap_rfbi_read_data(buf, len); +} + +static u8 blizzard_read_reg(u8 cmd) +{ + u8 data; + blizzard_read(cmd, &data, 1); + return data; +} + +static int blizzard_ctrl_init(struct omap_display *display) +{ + DBG("blizzard_ctrl_init\n"); + + return 0; +} + + +static int blizzard_ctrl_enable(struct omap_display *display) +{ + int r = 0; + u8 rev, conf; + + DBG("blizzard_ctrl_enable\n"); + + if (display->hw_config.ctrl_enable) { + r = display->hw_config.ctrl_enable(display); + if (r) + return r; + } + + msleep(100); + + rev = blizzard_read_reg(BLIZZARD_CLK_SRC); + printk("CLK_SRC %x\n", rev); + + rev = blizzard_read_reg(BLIZZARD_PLL_DIV); + printk("PLLDIV %x\n", rev); + + rev = blizzard_read_reg(BLIZZARD_REV_CODE); + conf = blizzard_read_reg(BLIZZARD_CONFIG); + + printk("rev %x, conf %x\n", rev, conf); + + switch (rev & 0xfc) { + case 0x9c: + blizzard.version = BLIZZARD_VERSION_S1D13744; + pr_info("omapfb: s1d13744 LCD controller rev %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + break; + case 0xa4: + blizzard.version = BLIZZARD_VERSION_S1D13745; + pr_info("omapfb: s1d13745 LCD controller rev %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + break; + default: + printk("invalid s1d1374x revision %02x\n", + rev); + r = -ENODEV; + } + + return r; +} + +static void blizzard_ctrl_disable(struct omap_display *display) +{ + DBG("blizzard_ctrl_disable\n"); + + if (display->hw_config.ctrl_disable) + display->hw_config.ctrl_disable(display); +} + +int rfbi_configure(int rfbi_module, int bpp, int lines); + +static void blizzard_ctrl_setup_update(struct omap_display *display, + u16 x, u16 y, u16 w, u16 h) +{ + u8 tmp[18]; + int x_end, y_end; + + DBG("blizzard_ctrl_setup_update\n"); + + x_end = x + w - 1; + y_end = y + h - 1; + + tmp[0] = x; + tmp[1] = x >> 8; + tmp[2] = y; + tmp[3] = y >> 8; + tmp[4] = x_end; + tmp[5] = x_end >> 8; + tmp[6] = y_end; + tmp[7] = y_end >> 8; + + /* scaling? */ + tmp[8] = x; + tmp[9] = x >> 8; + tmp[10] = y; + tmp[11] = y >> 8; + tmp[12] = x_end; + tmp[13] = x_end >> 8; + tmp[14] = y_end; + tmp[15] = y_end >> 8; + + tmp[16] = BLIZZARD_COLOR_RGB565; //color_mode; + + if (blizzard.version == BLIZZARD_VERSION_S1D13745) + tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND; + else + tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ? + BLIZZARD_SRC_WRITE_LCD : + BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE; + + rfbi_configure(display->hw_config.u.rfbi.channel, + 16, + 8); + + blizzard_write(BLIZZARD_INPUT_WIN_X_START_0, tmp, 18); + + rfbi_configure(display->hw_config.u.rfbi.channel, + 16, + 16); +} + +static int blizzard_ctrl_enable_te(struct omap_display *display, bool enable) +{ + return 0; +} + +static int blizzard_ctrl_rotate(struct omap_display *display, u8 rotate) +{ + return 0; +} + +static int blizzard_ctrl_mirror(struct omap_display *display, bool enable) +{ + return 0; +} + +static int blizzard_run_test(struct omap_display *display, int test_num) +{ + return 0; +} + +static struct omap_ctrl blizzard_ctrl = { + .owner = THIS_MODULE, + .name = "ctrl-blizzard", + .init = blizzard_ctrl_init, + .enable = blizzard_ctrl_enable, + .disable = blizzard_ctrl_disable, + .setup_update = blizzard_ctrl_setup_update, + .enable_te = blizzard_ctrl_enable_te, + .set_rotate = blizzard_ctrl_rotate, + .set_mirror = blizzard_ctrl_mirror, + .run_test = blizzard_run_test, + .pixel_size = 16, + + .timings = { + .cs_on_time = 0, + + .we_on_time = 9000, + .we_off_time = 18000, + .we_cycle_time = 36000, + + .re_on_time = 9000, + .re_off_time = 27000, + .re_cycle_time = 36000, + + .access_time = 27000, + .cs_off_time = 36000, + + .cs_pulse_width = 0, + }, +}; + + +static int __init blizzard_init(void) +{ + DBG("blizzard_init\n"); + omap_dss_register_ctrl(&blizzard_ctrl); + return 0; +} + +static void __exit blizzard_exit(void) +{ + DBG("blizzard_exit\n"); + + omap_dss_unregister_ctrl(&blizzard_ctrl); +} + +module_init(blizzard_init); +module_exit(blizzard_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("Blizzard Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/lcd_4430sdp.c b/drivers/video/omap2/displays/lcd_4430sdp.c new file mode 100644 index 000000000000..6840333a1f26 --- /dev/null +++ b/drivers/video/omap2/displays/lcd_4430sdp.c @@ -0,0 +1,126 @@ +/* + * lcd4430_sdp panel support + * + * Copyright (C) 2009 Texas Instruments + * Author: Mayuresh Janorkar <mayur@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl.h> +#include <linux/spi/spi.h> + +#include <mach/gpio.h> +#include <mach/gpio.h> +#include <mach/mux.h> +#include <asm/mach-types.h> +#include <mach/control.h> + +#include <mach/display.h> + +#define LCD_XRES 800 +#define LCD_YRES 480 +#define LCD_PIXCLOCK_MAX 21625 + +/*NEC NL8048HL11-01B Manual +* defines HFB, HSW, HBP, VFP, VSW, VBP as shown below +*/ + +static struct omap_video_timings lcd_4430_sdp_panel_timings = { + /* 800 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = LCD_XRES, + .y_res = LCD_YRES, + .pixel_clock = LCD_PIXCLOCK_MAX, + .hfp = 6, + .hsw = 1, + .hbp = 4, + .vfp = 3, + .vsw = 1, + .vbp = 4, +}; + +static int lcd_4430_sdp_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS; + dssdev->panel.timings = lcd_4430_sdp_panel_timings; + + return 0; +} + +static void lcd_4430_sdp_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int lcd_4430_sdp_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + mdelay(4); + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + /* Enable LCD-Enable bit in DISPC_CONTROL */ + omap_writel((omap_readl(0x48043040) | 0x1), 0x48043040); + return r; +} + +static void lcd_4430_sdp_panel_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + mdelay(4); +} + +static int lcd_4430_sdp_panel_suspend(struct omap_dss_device *dssdev) +{ + lcd_4430_sdp_panel_disable(dssdev); + return 0; +} + +static int lcd_4430_sdp_panel_resume(struct omap_dss_device *dssdev) +{ + return lcd_4430_sdp_panel_enable(dssdev); +} + +static struct omap_dss_driver lcd_4430_sdp_driver = { + .probe = lcd_4430_sdp_panel_probe, + .remove = lcd_4430_sdp_panel_remove, + + .enable = lcd_4430_sdp_panel_enable, + .disable = lcd_4430_sdp_panel_disable, + .suspend = lcd_4430_sdp_panel_suspend, + .resume = lcd_4430_sdp_panel_resume, + + .driver = { + .name = "sdp4430_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init lcd_4430_sdp_lcd_init(void) +{ + + return omap_dss_register_driver(&lcd_4430_sdp_driver); +} + +static void __exit lcd_4430_sdp_lcd_exit(void) +{ + return omap_dss_unregister_driver(&lcd_4430_sdp_driver); +} + +module_init(lcd_4430_sdp_lcd_init); +module_exit(lcd_4430_sdp_lcd_exit); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c new file mode 100644 index 000000000000..2679d6c2a6dc --- /dev/null +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -0,0 +1,712 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/backlight.h> +#include <linux/fb.h> + +#include <mach/display.h> +#include <mach/dma.h> + +#include "panel-acx565akm.h" + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 +#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51 +#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52 +#define MIPID_CMD_WRITE_CTRL_DISP 0x53 + +#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5) +#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4) +#define CTRL_DISP_BACKLIGHT_ON (1 << 2) +#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1) + +#define MIPID_CMD_READ_CTRL_DISP 0x54 +#define MIPID_CMD_WRITE_CABC 0x55 +#define MIPID_CMD_READ_CABC 0x56 + +#define MIPID_VER_LPH8923 3 +#define MIPID_VER_LS041Y3 4 +#define MIPID_VER_L4F00311 8 +#define MIPID_VER_ACX565AKM 9 + +struct acx565akm_device { + struct backlight_device *bl_dev; + int enabled; + int model; + int revision; + u8 display_id[3]; + int has_bc:1; + int has_cabc:1; + unsigned int saved_bklight_level; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct spi_device *spi; + struct mutex mutex; + struct omap_panel panel; + struct omap_display *display; +}; + +static int acx565akm_bl_update_status(struct backlight_device *dev); + +static void acx565akm_transfer(struct acx565akm_device *md, int cmd, + const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[5]; + int r; + + BUG_ON(md->spi == NULL); + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + + if (rlen > 1 && wlen == 0) { + /* + * Between the command and the response data there is a + * dummy clock cycle. Add an extra bit after the command + * word to account for this. + */ + x->bits_per_word = 10; + cmd <<= 1; + } + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = rbuf; + x->len = rlen; + spi_message_add_tail(x, &m); + } + + r = spi_sync(md->spi, &m); + if (r < 0) + dev_dbg(&md->spi->dev, "spi_sync %d\n", r); +} + +static inline void acx565akm_cmd(struct acx565akm_device *md, int cmd) +{ + acx565akm_transfer(md, cmd, NULL, 0, NULL, 0); +} + +static inline void acx565akm_write(struct acx565akm_device *md, + int reg, const u8 *buf, int len) +{ + acx565akm_transfer(md, reg, buf, len, NULL, 0); +} + +static inline void acx565akm_read(struct acx565akm_device *md, + int reg, u8 *buf, int len) +{ + acx565akm_transfer(md, reg, NULL, 0, buf, len); +} + +static void hw_guard_start(struct acx565akm_device *md, int guard_msec) +{ + md->hw_guard_wait = msecs_to_jiffies(guard_msec); + md->hw_guard_end = jiffies + md->hw_guard_wait; +} + +static void hw_guard_wait(struct acx565akm_device *md) +{ + unsigned long wait = md->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= md->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static void set_sleep_mode(struct acx565akm_device *md, int on) +{ + int cmd, sleep_time = 50; + + if (on) + cmd = MIPID_CMD_SLEEP_IN; + else + cmd = MIPID_CMD_SLEEP_OUT; + hw_guard_wait(md); + acx565akm_cmd(md, cmd); + hw_guard_start(md, 120); + /* + * When we enable the panel, it seems we _have_ to sleep + * 120 ms before sending the init string. When disabling the + * panel we'll sleep for the duration of 2 frames, so that the + * controller can still provide the PCLK,HS,VS signals. */ + if (!on) + sleep_time = 120; + msleep(sleep_time); +} + +static void set_display_state(struct acx565akm_device *md, int enabled) +{ + int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; + + acx565akm_cmd(md, cmd); +} + +static int panel_enabled(struct acx565akm_device *md) +{ + u32 disp_status; + int enabled; + + acx565akm_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + dev_dbg(&md->spi->dev, + "LCD panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static void enable_backlight_ctrl(struct acx565akm_device *md, int enable) +{ + u16 ctrl; + + acx565akm_read(md, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1); + if (enable) { + ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON; + } else { + ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON); + } + + ctrl |= 1 << 8; + acx565akm_write(md, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2); +} + +static void set_cabc_mode(struct acx565akm_device *md, int mode) +{ + u16 cabc_ctrl; + + cabc_ctrl = 0; + acx565akm_read(md, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1); + cabc_ctrl &= ~3; + cabc_ctrl |= (1 << 8) | (mode & 3); + acx565akm_write(md, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2); +} + +static int get_cabc_mode(struct acx565akm_device *md) +{ + u8 cabc_ctrl; + + acx565akm_read(md, MIPID_CMD_READ_CABC, &cabc_ctrl, 1); + return cabc_ctrl & 3; +} + +static int panel_detect(struct acx565akm_device *md) +{ + acx565akm_read(md, MIPID_CMD_READ_DISP_ID, md->display_id, 3); + dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n", + md->display_id[0], md->display_id[1], md->display_id[2]); + + switch (md->display_id[0]) { + case 0x10: + md->model = MIPID_VER_ACX565AKM; + md->panel.name = "acx565akm"; + md->has_bc = 1; + md->has_cabc = 1; + break; + case 0x29: + md->model = MIPID_VER_L4F00311; + md->panel.name = "l4f00311"; + break; + case 0x45: + md->model = MIPID_VER_LPH8923; + md->panel.name = "lph8923"; + break; + case 0x83: + md->model = MIPID_VER_LS041Y3; + md->panel.name = "ls041y3"; + break; + default: + md->panel.name = "unknown"; + dev_err(&md->spi->dev, "invalid display ID\n"); + return -ENODEV; + } + + md->revision = md->display_id[1]; + + pr_info("omapfb: %s rev %02x LCD detected\n", + md->panel.name, md->revision); + + return 0; +} + +static int acx565akm_panel_enable(struct omap_display *display) +{ + struct acx565akm_device *md = + (struct acx565akm_device *)display->panel->priv; + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + if (display->hw_config.panel_enable) + display->hw_config.panel_enable(display); + + md->enabled = panel_enabled(md); + + if (md->enabled) { + dev_dbg(&md->spi->dev, "panel already enabled\n"); + mutex_unlock(&md->mutex); + return 0; + } + + set_sleep_mode(md, 0); + md->enabled = 1; + set_display_state(md, 1); + + mutex_unlock(&md->mutex); + + return acx565akm_bl_update_status(md->bl_dev); +} + +static void acx565akm_panel_disable(struct omap_display *display) +{ + struct acx565akm_device *md = + (struct acx565akm_device *)display->panel->priv; + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + if (!md->enabled) { + mutex_unlock(&md->mutex); + return; + } + set_display_state(md, 0); + set_sleep_mode(md, 1); + md->enabled = 0; + + if (display->hw_config.panel_disable) + display->hw_config.panel_disable(display); + + mutex_unlock(&md->mutex); +} + +#if 0 +static void acx565akm_set_mode(struct omap_display *display, + int x_res, int y_res, int bpp) +{ + struct acx565akm_device *md = + (struct acx565akm_device *)display->panel->priv; + u16 par; + + switch (bpp) { + case 16: + par = 0x150; + break; + case 18: + par = 0x160; + break; + case 24: + par = 0x170; + break; + } + + acx565akm_write(md, 0x3a, (u8 *)&par, 2); +} +#endif + +static int acx565akm_panel_suspend(struct omap_display *display) +{ + acx565akm_panel_disable(display); + return 0; +} + +static int acx565akm_panel_resume(struct omap_display *display) +{ + return acx565akm_panel_enable(display); +} + +static void acx565akm_set_brightness(struct acx565akm_device *md, int level) +{ + int bv; + + bv = level | (1 << 8); + acx565akm_write(md, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2); + + if (level) + enable_backlight_ctrl(md, 1); + else + enable_backlight_ctrl(md, 0); +} + +static int acx565akm_get_actual_brightness(struct acx565akm_device *md) +{ + u8 bv; + + acx565akm_read(md, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1); + + return bv; +} + +static int acx565akm_bl_update_status(struct backlight_device *dev) +{ + struct acx565akm_device *md = dev_get_drvdata(&dev->dev); + struct omap_display *display = md->display; + int r; + int level; + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + if (display->hw_config.set_backlight == NULL) + return -ENODEV; + + mutex_lock(&md->mutex); + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + r = 0; + if (md->has_bc) + acx565akm_set_brightness(md, level); + else + if (display->hw_config.set_backlight != NULL) + r = display->hw_config.set_backlight(display, level); + else + r = -ENODEV; + + mutex_unlock(&md->mutex); + + return r; +} + +static int acx565akm_bl_get_intensity(struct backlight_device *dev) +{ + struct acx565akm_device *md = dev_get_drvdata(&dev->dev); + struct omap_display *display = md->display; + + dev_dbg(&dev->dev, "%s\n", __func__); + + if (md->has_bc && display->hw_config.set_backlight == NULL) + return -ENODEV; + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) { + if (md->has_bc) + return acx565akm_get_actual_brightness(md); + else + return dev->props.brightness; + } + + return 0; +} + +static struct backlight_ops acx565akm_bl_ops = { + .get_brightness = acx565akm_bl_get_intensity, + .update_status = acx565akm_bl_update_status, +}; + +static const char *cabc_modes[] = { + "off", /* used also always when CABC is not supported */ + "ui", + "still-image", + "moving-image", +}; + +static ssize_t show_cabc_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + const char *mode_str; + int mode; + int len; + + if (!md->has_cabc) + mode = 0; + else + mode = get_cabc_mode(md); + mode_str = "unknown"; + if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) + mode_str = cabc_modes[mode]; + len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); + + return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; +} + +static ssize_t store_cabc_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { + const char *mode_str = cabc_modes[i]; + int cmp_len = strlen(mode_str); + + if (count > 0 && buf[count - 1] == '\n') + count--; + if (count != cmp_len) + continue; + + if (strncmp(buf, mode_str, cmp_len) == 0) + break; + } + + if (i == ARRAY_SIZE(cabc_modes)) + return -EINVAL; + + if (!md->has_cabc && i != 0) + return -EINVAL; + + mutex_lock(&md->mutex); + set_cabc_mode(md, i); + mutex_unlock(&md->mutex); + + return count; +} + +static ssize_t show_cabc_available_modes(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + int len; + int i; + + if (!md->has_cabc) + return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]); + + for (i = 0, len = 0; + len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) + len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", + i ? " " : "", cabc_modes[i], + i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); + + return len < PAGE_SIZE ? len : PAGE_SIZE - 1; +} + +static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, + show_cabc_mode, store_cabc_mode); +static DEVICE_ATTR(cabc_available_modes, S_IRUGO, + show_cabc_available_modes, NULL); + +static struct attribute *bldev_attrs[] = { + &dev_attr_cabc_mode.attr, + &dev_attr_cabc_available_modes.attr, + NULL, +}; + +static struct attribute_group bldev_attr_group = { + .attrs = bldev_attrs, +}; + +static int acx565akm_panel_init(struct omap_display *display) +{ + struct omap_panel *panel = display->panel; + struct acx565akm_panel_data *panel_data = display->hw_config.panel_data; + struct acx565akm_device *md = (struct acx565akm_device *)panel->priv; + + struct backlight_device *bldev; + int brightness; + int max_brightness; + int r; + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + if (!panel_data) { + dev_err(&md->spi->dev, "no panel data\n"); + return -ENODEV; + } + + mutex_init(&md->mutex); + md->display = display; + + if (display->hw_config.panel_enable) + display->hw_config.panel_enable(display); + + md->enabled = panel_enabled(md); + + r = panel_detect(md); + if (r) { + if (!md->enabled && display->hw_config.panel_disable) + display->hw_config.panel_disable(display); + mutex_unlock(&md->mutex); + return r; + } + + if (!panel_data->bc_connected) { + md->has_bc = 0; + md->has_cabc = 0; + } + +#if 0 + acx565akm_set_mode(display, panel->timings.x_res, panel->timings.y_res, + panel->bpp); +#endif + + if (!md->enabled) + display->hw_config.panel_disable(display); + + bldev = backlight_device_register("acx565akm", &md->spi->dev, + md, &acx565akm_bl_ops); + md->bl_dev = bldev; + + if (md->has_cabc) { + r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group); + if (r) { + dev_err(&bldev->dev, "failed to create sysfs files\n"); + backlight_device_unregister(bldev); + return r; + } + } + + bldev->props.fb_blank = FB_BLANK_UNBLANK; + bldev->props.power = FB_BLANK_UNBLANK; + + if (md->has_bc) + max_brightness = 255; + else + max_brightness = display->hw_config.max_backlight_level; + + if (md->has_bc) + brightness = acx565akm_get_actual_brightness(md); + else { + if (display->hw_config.get_backlight != NULL) + brightness = display->hw_config.get_backlight(display); + else + brightness = 0; + } + + bldev->props.max_brightness = max_brightness; + bldev->props.brightness = brightness; + acx565akm_bl_update_status(bldev); + + return 0; +} + +static struct omap_panel acx565akm_panel = { + .name = "panel-acx565akm", + .init = acx565akm_panel_init, + .suspend = acx565akm_panel_suspend, + .resume = acx565akm_panel_resume, + .enable = acx565akm_panel_enable, + .disable = acx565akm_panel_disable, + + .timings = { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 24000, + + .hsw = 4, + .hfp = 16, + .hbp = 12, + + .vsw = 3, + .vfp = 3, + .vbp = 3, + }, + + .config = OMAP_DSS_LCD_TFT, + + .recommended_bpp = 16, + + /* + * supported modes: 12bpp(444), 16bpp(565), 18bpp(666), 24bpp(888) + * resolutions. + */ +}; + +static int acx565akm_spi_probe(struct spi_device *spi) +{ + struct acx565akm_device *md; + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + md = kzalloc(sizeof(*md), GFP_KERNEL); + if (md == NULL) { + dev_err(&spi->dev, "out of memory\n"); + return -ENOMEM; + } + + spi->mode = SPI_MODE_3; + md->spi = spi; + dev_set_drvdata(&spi->dev, md); + md->panel = acx565akm_panel; + acx565akm_panel.priv = md; + + omap_dss_register_panel(&acx565akm_panel); + + return 0; +} + +static int acx565akm_spi_remove(struct spi_device *spi) +{ + struct acx565akm_device *md = dev_get_drvdata(&spi->dev); + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + sysfs_remove_group(&md->bl_dev->dev.kobj, &bldev_attr_group); + backlight_device_unregister(md->bl_dev); + omap_dss_unregister_panel(&acx565akm_panel); + + kfree(md); + + return 0; +} + +static struct spi_driver acx565akm_spi_driver = { + .driver = { + .name = "acx565akm", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = acx565akm_spi_probe, + .remove = __devexit_p(acx565akm_spi_remove), +}; + +static int __init acx565akm_init(void) +{ + return spi_register_driver(&acx565akm_spi_driver); +} + +static void __exit acx565akm_exit(void) +{ + spi_unregister_driver(&acx565akm_spi_driver); +} + +module_init(acx565akm_init); +module_exit(acx565akm_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("acx565akm LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-acx565akm.h b/drivers/video/omap2/displays/panel-acx565akm.h new file mode 100644 index 000000000000..6d3727b4c7af --- /dev/null +++ b/drivers/video/omap2/displays/panel-acx565akm.h @@ -0,0 +1,9 @@ +#ifndef __DRIVERS_VIDEO_OMAP2_DISPLAYS_PANEL_ACX565AKM_H +#define __DRIVERS_VIDEO_OMAP2_DISPLAYS_PANEL_ACX565AKM_H + +struct acx565akm_panel_data { + unsigned bc_connected:1; +}; + +#endif + diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c new file mode 100644 index 000000000000..738147eee49c --- /dev/null +++ b/drivers/video/omap2/displays/panel-generic.c @@ -0,0 +1,104 @@ +/* + * Generic panel support + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> + +#include <mach/display.h> + +static struct omap_video_timings generic_panel_timings = { + /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = 640, + .y_res = 480, + .pixel_clock = 23500, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 7, +}; + +static int generic_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.config = OMAP_DSS_LCD_TFT; + dssdev->panel.timings = generic_panel_timings; + + return 0; +} + +static void generic_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int generic_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + + return r; +} + +static void generic_panel_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); +} + +static int generic_panel_suspend(struct omap_dss_device *dssdev) +{ + generic_panel_disable(dssdev); + return 0; +} + +static int generic_panel_resume(struct omap_dss_device *dssdev) +{ + return generic_panel_enable(dssdev); +} + +static struct omap_dss_driver generic_driver = { + .probe = generic_panel_probe, + .remove = generic_panel_remove, + + .enable = generic_panel_enable, + .disable = generic_panel_disable, + .suspend = generic_panel_suspend, + .resume = generic_panel_resume, + + .driver = { + .name = "generic_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init generic_panel_drv_init(void) +{ + return omap_dss_register_driver(&generic_driver); +} + +static void __exit generic_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&generic_driver); +} + +module_init(generic_panel_drv_init); +module_exit(generic_panel_drv_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-n800.c b/drivers/video/omap2/displays/panel-n800.c new file mode 100644 index 000000000000..91d3e37d6d4c --- /dev/null +++ b/drivers/video/omap2/displays/panel-n800.c @@ -0,0 +1,435 @@ + +/*#define DEBUG*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/backlight.h> +#include <linux/fb.h> + +#include <mach/display.h> +#include <mach/dma.h> + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 + +#define MIPID_VER_LPH8923 3 +#define MIPID_VER_LS041Y3 4 + +#define MIPID_ESD_CHECK_PERIOD msecs_to_jiffies(5000) + +#ifdef DEBUG +#define DBG(format, ...) printk(KERN_DEBUG "PN800: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +struct pn800_device { + struct backlight_device *bl_dev; + int enabled; + int model; + int revision; + u8 display_id[3]; + unsigned int saved_bklight_level; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct spi_device *spi; + struct mutex mutex; + struct omap_panel panel; + struct omap_display *display; +}; + + +static void pn800_transfer(struct pn800_device *md, int cmd, + const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[4]; + u16 w; + int r; + + BUG_ON(md->spi == NULL); + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = &w; + x->len = 1; + spi_message_add_tail(x, &m); + + if (rlen > 1) { + /* Arrange for the extra clock before the first + * data bit. + */ + x->bits_per_word = 9; + x->len = 2; + + x++; + x->rx_buf = &rbuf[1]; + x->len = rlen - 1; + spi_message_add_tail(x, &m); + } + } + + r = spi_sync(md->spi, &m); + if (r < 0) + dev_dbg(&md->spi->dev, "spi_sync %d\n", r); + + if (rlen) + rbuf[0] = w & 0xff; +} + +static inline void pn800_cmd(struct pn800_device *md, int cmd) +{ + pn800_transfer(md, cmd, NULL, 0, NULL, 0); +} + +static inline void pn800_write(struct pn800_device *md, + int reg, const u8 *buf, int len) +{ + pn800_transfer(md, reg, buf, len, NULL, 0); +} + +static inline void pn800_read(struct pn800_device *md, + int reg, u8 *buf, int len) +{ + pn800_transfer(md, reg, NULL, 0, buf, len); +} + +static void set_data_lines(struct pn800_device *md, int data_lines) +{ + u16 par; + + switch (data_lines) { + case 16: + par = 0x150; + break; + case 18: + par = 0x160; + break; + case 24: + par = 0x170; + break; + } + pn800_write(md, 0x3a, (u8 *)&par, 2); +} + +static void send_init_string(struct pn800_device *md) +{ + u16 initpar[] = { 0x0102, 0x0100, 0x0100 }; + int data_lines; + + pn800_write(md, 0xc2, (u8 *)initpar, sizeof(initpar)); + + data_lines = (int)md->display->hw_config.panel_data; // XXX + + set_data_lines(md, data_lines); +} + +static void hw_guard_start(struct pn800_device *md, int guard_msec) +{ + md->hw_guard_wait = msecs_to_jiffies(guard_msec); + md->hw_guard_end = jiffies + md->hw_guard_wait; +} + +static void hw_guard_wait(struct pn800_device *md) +{ + unsigned long wait = md->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= md->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static void set_sleep_mode(struct pn800_device *md, int on) +{ + int cmd, sleep_time = 50; + + if (on) + cmd = MIPID_CMD_SLEEP_IN; + else + cmd = MIPID_CMD_SLEEP_OUT; + hw_guard_wait(md); + pn800_cmd(md, cmd); + hw_guard_start(md, 120); + /* + * When we enable the panel, it seems we _have_ to sleep + * 120 ms before sending the init string. When disabling the + * panel we'll sleep for the duration of 2 frames, so that the + * controller can still provide the PCLK,HS,VS signals. */ + if (!on) + sleep_time = 120; + msleep(sleep_time); +} + +static void set_display_state(struct pn800_device *md, int enabled) +{ + int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; + + pn800_cmd(md, cmd); +} + +static int panel_enabled(struct pn800_device *md) +{ + u32 disp_status; + int enabled; + + pn800_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + dev_dbg(&md->spi->dev, + "LCD panel %s enabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + DBG("status %#08x\n", disp_status); + return enabled; +} + +static int panel_detect(struct pn800_device *md) +{ + pn800_read(md, MIPID_CMD_READ_DISP_ID, md->display_id, 3); + dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n", + md->display_id[0], md->display_id[1], md->display_id[2]); + + switch (md->display_id[0]) { + case 0x45: + md->model = MIPID_VER_LPH8923; + md->panel.name = "lph8923"; + break; + case 0x83: + md->model = MIPID_VER_LS041Y3; + md->panel.name = "ls041y3"; + //md->esd_check = ls041y3_esd_check; + break; + default: + md->panel.name = "unknown"; + dev_err(&md->spi->dev, "invalid display ID\n"); + return -ENODEV; + } + + md->revision = md->display_id[1]; + pr_info("omapfb: %s rev %02x LCD detected\n", + md->panel.name, md->revision); + + return 0; +} + + + +static int pn800_panel_enable(struct omap_display *display) +{ + int r; + struct pn800_device *md = + (struct pn800_device *)display->panel->priv; + + DBG("pn800_panel_enable\n"); + + mutex_lock(&md->mutex); + + if (display->hw_config.panel_enable) + display->hw_config.panel_enable(display); + + msleep(50); // wait for power up + + r = panel_detect(md); + if (r) { + mutex_unlock(&md->mutex); + return r; + } + + md->enabled = panel_enabled(md); + + if (md->enabled) { + DBG("panel already enabled\n"); + ; /*pn800_esd_start_check(md);*/ + } else { + ; /*md->saved_bklight_level = pn800_get_bklight_level(panel);*/ + } + + + if (md->enabled) { + mutex_unlock(&md->mutex); + return 0; + } + + set_sleep_mode(md, 0); + md->enabled = 1; + send_init_string(md); + set_display_state(md, 1); + //mipid_set_bklight_level(panel, md->saved_bklight_level); + //mipid_esd_start_check(md); + + mutex_unlock(&md->mutex); + return 0; +} + +static void pn800_panel_disable(struct omap_display *display) +{ + struct pn800_device *md = + (struct pn800_device *)display->panel->priv; + + DBG("pn800_panel_disable\n"); + + mutex_lock(&md->mutex); + + if (!md->enabled) { + mutex_unlock(&md->mutex); + return; + } + /*md->saved_bklight_level = pn800_get_bklight_level(panel);*/ + /*pn800_set_bklight_level(panel, 0);*/ + + set_display_state(md, 0); + set_sleep_mode(md, 1); + md->enabled = 0; + + + if (display->hw_config.panel_disable) + display->hw_config.panel_disable(display); + + mutex_unlock(&md->mutex); +} + +static int pn800_panel_init(struct omap_display *display) +{ + struct pn800_device *md = + (struct pn800_device *)display->panel->priv; + + DBG("pn800_panel_init\n"); + + mutex_init(&md->mutex); + md->display = display; + + return 0; +} + +static int pn800_run_test(struct omap_display *display, int test_num) +{ + return 0; +} + +static struct omap_panel pn800_panel = { + .owner = THIS_MODULE, + .name = "panel-pn800", + .init = pn800_panel_init, + /*.remove = pn800_cleanup,*/ + .enable = pn800_panel_enable, + .disable = pn800_panel_disable, + //.set_mode = pn800_set_mode, + .run_test = pn800_run_test, + + .timings = { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 21940, + .hsw = 50, + .hfp = 20, + .hbp = 15, + + .vsw = 2, + .vfp = 1, + .vbp = 3, + }, + .config = OMAP_DSS_LCD_TFT, +}; + +static int pn800_spi_probe(struct spi_device *spi) +{ + struct pn800_device *md; + + DBG("pn800_spi_probe\n"); + + md = kzalloc(sizeof(*md), GFP_KERNEL); + if (md == NULL) { + dev_err(&spi->dev, "out of memory\n"); + return -ENOMEM; + } + + spi->mode = SPI_MODE_0; + md->spi = spi; + dev_set_drvdata(&spi->dev, md); + md->panel = pn800_panel; + pn800_panel.priv = md; + + omap_dss_register_panel(&pn800_panel); + + return 0; +} + +static int pn800_spi_remove(struct spi_device *spi) +{ + struct pn800_device *md = dev_get_drvdata(&spi->dev); + + DBG("pn800_spi_remove\n"); + + omap_dss_unregister_panel(&pn800_panel); + + /*pn800_disable(&md->panel);*/ + kfree(md); + + return 0; +} + +static struct spi_driver pn800_spi_driver = { + .driver = { + .name = "panel-n800", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = pn800_spi_probe, + .remove = __devexit_p(pn800_spi_remove), +}; + +static int __init pn800_init(void) +{ + DBG("pn800_init\n"); + return spi_register_driver(&pn800_spi_driver); +} + +static void __exit pn800_exit(void) +{ + DBG("pn800_exit\n"); + spi_unregister_driver(&pn800_spi_driver); +} + +module_init(pn800_init); +module_exit(pn800_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("N800 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-samsung-lte430wq-f0c.c b/drivers/video/omap2/displays/panel-samsung-lte430wq-f0c.c new file mode 100644 index 000000000000..eafe581b36ff --- /dev/null +++ b/drivers/video/omap2/displays/panel-samsung-lte430wq-f0c.c @@ -0,0 +1,113 @@ +/* + * LCD panel driver for Samsung LTE430WQ-F0C + * + * Author: Steve Sakoman <steve@sakoman.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> + +#include <mach/display.h> + +static struct omap_video_timings samsung_lte_timings = { + .x_res = 480, + .y_res = 272, + + .pixel_clock = 9200, + + .hsw = 41, + .hfp = 8, + .hbp = 45-41, + + .vsw = 10, + .vfp = 4, + .vbp = 12-10, +}; + +static int samsung_lte_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS; + dssdev->panel.timings = samsung_lte_timings; + + return 0; +} + +static void samsung_lte_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int samsung_lte_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + /* wait couple of vsyncs until enabling the LCD */ + msleep(50); + + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + + return r; +} + +static void samsung_lte_panel_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + /* wait at least 5 vsyncs after disabling the LCD */ + + msleep(100); +} + +static int samsung_lte_panel_suspend(struct omap_dss_device *dssdev) +{ + samsung_lte_panel_disable(dssdev); + return 0; +} + +static int samsung_lte_panel_resume(struct omap_dss_device *dssdev) +{ + return samsung_lte_panel_enable(dssdev); +} + +static struct omap_dss_driver samsung_lte_driver = { + .probe = samsung_lte_panel_probe, + .remove = samsung_lte_panel_remove, + + .enable = samsung_lte_panel_enable, + .disable = samsung_lte_panel_disable, + .suspend = samsung_lte_panel_suspend, + .resume = samsung_lte_panel_resume, + + .driver = { + .name = "samsung_lte_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init samsung_lte_panel_drv_init(void) +{ + return omap_dss_register_driver(&samsung_lte_driver); +} + +static void __exit samsung_lte_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&samsung_lte_driver); +} + +module_init(samsung_lte_panel_drv_init); +module_exit(samsung_lte_panel_drv_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c new file mode 100644 index 000000000000..2b6d8899441a --- /dev/null +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -0,0 +1,153 @@ +/* + * LCD panel driver for Sharp LS037V7DW01 + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> + +#include <mach/display.h> + +struct sharp_data { + /* XXX This regulator should actually be in SDP board file, not here, + * as it doesn't actually power the LCD, but something else that + * affects the output to LCD (I think. Somebody clarify). It doesn't do + * harm here, as SDP is the only board using this currently */ + struct regulator *vdvi_reg; +}; + +static struct omap_video_timings sharp_ls_timings = { + .x_res = 480, + .y_res = 640, + + .pixel_clock = 19200, + + .hsw = 2, + .hfp = 1, + .hbp = 28, + + .vsw = 1, + .vfp = 1, + .vbp = 1, +}; + +static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd; + + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS; + dssdev->panel.acb = 0x28; + dssdev->panel.timings = sharp_ls_timings; + + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) + return -ENOMEM; + + dev_set_drvdata(&dssdev->dev, sd); + + sd->vdvi_reg = regulator_get(&dssdev->dev, "vdvi"); + if (IS_ERR(sd->vdvi_reg)) { + kfree(sd); + printk(KERN_INFO "failed to get VDVI regulator\n"); + return PTR_ERR(sd->vdvi_reg); + } + + return 0; +} + +static void sharp_ls_panel_remove(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + + regulator_put(sd->vdvi_reg); + + kfree(sd); +} + +static int sharp_ls_panel_enable(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + int r = 0; + + /* wait couple of vsyncs until enabling the LCD */ + msleep(50); + + regulator_enable(sd->vdvi_reg); + + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + + return r; +} + +static void sharp_ls_panel_disable(struct omap_dss_device *dssdev) +{ + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + regulator_disable(sd->vdvi_reg); + + /* wait at least 5 vsyncs after disabling the LCD */ + + msleep(100); +} + +static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev) +{ + sharp_ls_panel_disable(dssdev); + return 0; +} + +static int sharp_ls_panel_resume(struct omap_dss_device *dssdev) +{ + return sharp_ls_panel_enable(dssdev); +} + +static struct omap_dss_driver sharp_ls_driver = { + .probe = sharp_ls_panel_probe, + .remove = sharp_ls_panel_remove, + + .enable = sharp_ls_panel_enable, + .disable = sharp_ls_panel_disable, + .suspend = sharp_ls_panel_suspend, + .resume = sharp_ls_panel_resume, + + .driver = { + .name = "sharp_ls_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init sharp_ls_panel_drv_init(void) +{ + return omap_dss_register_driver(&sharp_ls_driver); +} + +static void __exit sharp_ls_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&sharp_ls_driver); +} + +module_init(sharp_ls_panel_drv_init); +module_exit(sharp_ls_panel_drv_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c new file mode 100644 index 000000000000..54b27316d638 --- /dev/null +++ b/drivers/video/omap2/displays/panel-taal.c @@ -0,0 +1,905 @@ +/* + * Taal DSI command mode panel + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*#define DEBUG*/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/backlight.h> +#include <linux/fb.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/completion.h> + +#include <mach/display.h> + +/* DSI Virtual channel. Hardcoded for now. */ +#define TCH 0 + +#define DCS_READ_NUM_ERRORS 0x05 +#define DCS_READ_POWER_MODE 0x0a +#define DCS_READ_MADCTL 0x0b +#define DCS_READ_PIXEL_FORMAT 0x0c +#define DCS_SLEEP_IN 0x10 +#define DCS_SLEEP_OUT 0x11 +#define DCS_DISPLAY_OFF 0x28 +#define DCS_DISPLAY_ON 0x29 +#define DCS_COLUMN_ADDR 0x2a +#define DCS_PAGE_ADDR 0x2b +#define DCS_MEMORY_WRITE 0x2c +#define DCS_TEAR_OFF 0x34 +#define DCS_TEAR_ON 0x35 +#define DCS_MEM_ACC_CTRL 0x36 +#define DCS_PIXEL_FORMAT 0x3a +#define DCS_BRIGHTNESS 0x51 +#define DCS_CTRL_DISPLAY 0x53 +#define DCS_WRITE_CABC 0x55 +#define DCS_READ_CABC 0x56 +#define DCS_GET_ID1 0xda +#define DCS_GET_ID2 0xdb +#define DCS_GET_ID3 0xdc + +#ifdef DEBUG +#define DBG(format, ...) printk(KERN_DEBUG "Taal: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +struct taal_data { + struct backlight_device *bldev; + + unsigned long hw_guard_end; /* next value of jiffies when we can + * issue the next sleep in/out command + */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct omap_dss_device *dssdev; + + bool enabled; + u8 rotate; + bool mirror; + + bool te_enabled; + bool use_ext_te; + struct completion te_completion; + + bool use_dsi_bl; + + bool cabc_broken; + unsigned cabc_mode; + bool intro_printed; +}; + +static void hw_guard_start(struct taal_data *td, int guard_msec) +{ + td->hw_guard_wait = msecs_to_jiffies(guard_msec); + td->hw_guard_end = jiffies + td->hw_guard_wait; +} + +static void hw_guard_wait(struct taal_data *td) +{ + unsigned long wait = td->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= td->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static int taal_dcs_read_1(u8 dcs_cmd, u8 *data) +{ + int r; + u8 buf[1]; + + r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1); + + if (r < 0) { + printk(KERN_ERR "Taal read error\n"); + return r; + } + + *data = buf[0]; + + return 0; +} + +static int taal_dcs_write_0(u8 dcs_cmd) +{ + return dsi_vc_dcs_write(TCH, &dcs_cmd, 1); +} + +static int taal_dcs_write_1(u8 dcs_cmd, u8 param) +{ + u8 buf[2]; + buf[0] = dcs_cmd; + buf[1] = param; + return dsi_vc_dcs_write(TCH, buf, 2); +} + +static int taal_sleep_in(struct taal_data *td) +{ + u8 cmd; + int r; + + hw_guard_wait(td); + + cmd = DCS_SLEEP_IN; + r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1); + if (r) + return r; + + hw_guard_start(td, 120); + + msleep(5); + + return 0; +} + +static int taal_sleep_out(struct taal_data *td) +{ + int r; + + hw_guard_wait(td); + + r = taal_dcs_write_0(DCS_SLEEP_OUT); + if (r) + return r; + + hw_guard_start(td, 120); + + msleep(5); + + return 0; +} + +static int taal_get_id(u8 *id1, u8 *id2, u8 *id3) +{ + int r; + + r = taal_dcs_read_1(DCS_GET_ID1, id1); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID2, id2); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID3, id3); + if (r) + return r; + + return 0; +} + +static int taal_set_addr_mode(u8 rotate, bool mirror) +{ + int r; + u8 mode; + int b5, b6, b7; + + r = taal_dcs_read_1(DCS_READ_MADCTL, &mode); + if (r) + return r; + + switch (rotate) { + default: + case 0: + b7 = 0; + b6 = 0; + b5 = 0; + break; + case 1: + b7 = 0; + b6 = 1; + b5 = 1; + break; + case 2: + b7 = 1; + b6 = 1; + b5 = 0; + break; + case 3: + b7 = 1; + b6 = 0; + b5 = 1; + break; + } + + if (mirror) + b6 = !b6; + + mode &= ~((1<<7) | (1<<6) | (1<<5)); + mode |= (b7 << 7) | (b6 << 6) | (b5 << 5); + + return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode); +} + +static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h) +{ + int r; + u16 x1 = x; + u16 x2 = x + w - 1; + u16 y1 = y; + u16 y2 = y + h - 1; + + u8 buf[5]; + buf[0] = DCS_COLUMN_ADDR; + buf[1] = (x1 >> 8) & 0xff; + buf[2] = (x1 >> 0) & 0xff; + buf[3] = (x2 >> 8) & 0xff; + buf[4] = (x2 >> 0) & 0xff; + + r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); + if (r) + return r; + + buf[0] = DCS_PAGE_ADDR; + buf[1] = (y1 >> 8) & 0xff; + buf[2] = (y1 >> 0) & 0xff; + buf[3] = (y2 >> 8) & 0xff; + buf[4] = (y2 >> 0) & 0xff; + + r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); + if (r) + return r; + + dsi_vc_send_bta_sync(TCH); + + return r; +} + +static int taal_bl_update_status(struct backlight_device *dev) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + int level; + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + DBG("update brightness to %d\n", level); + + if (td->use_dsi_bl) { + if (td->enabled) { + dsi_bus_lock(); + r = taal_dcs_write_1(DCS_BRIGHTNESS, level); + dsi_bus_unlock(); + if (r) + return r; + } + } else { + if (!dssdev->set_backlight) + return -EINVAL; + + r = dssdev->set_backlight(dssdev, level); + if (r) + return r; + } + + return 0; +} + +static int taal_bl_get_intensity(struct backlight_device *dev) +{ + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + return dev->props.brightness; + + return 0; +} + +static struct backlight_ops taal_bl_ops = { + .get_brightness = taal_bl_get_intensity, + .update_status = taal_bl_update_status, +}; + +static void taal_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static void taal_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + if (td->rotate == 0 || td->rotate == 2) { + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; + } else { + *yres = dssdev->panel.timings.x_res; + *xres = dssdev->panel.timings.y_res; + } +} + +static irqreturn_t taal_te_isr(int irq, void *data) +{ + struct omap_dss_device *dssdev = data; + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + complete_all(&td->te_completion); + + return IRQ_HANDLED; +} + +static ssize_t taal_num_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + u8 errors; + int r; + + if (td->enabled) { + dsi_bus_lock(); + r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors); + dsi_bus_unlock(); + } else { + r = -ENODEV; + } + + if (r) + return r; + + return snprintf(buf, PAGE_SIZE, "%d\n", errors); +} + +static ssize_t taal_hw_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + u8 id1, id2, id3; + int r; + + if (td->enabled) { + dsi_bus_lock(); + r = taal_get_id(&id1, &id2, &id3); + dsi_bus_unlock(); + } else { + r = -ENODEV; + } + + if (r) + return r; + + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); +} + +static const char *cabc_modes[] = { + "off", /* used also always when CABC is not supported */ + "ui", + "still-image", + "moving-image", +}; + +static ssize_t show_cabc_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + const char *mode_str; + int mode; + int len; + + mode = td->cabc_mode; + + mode_str = "unknown"; + if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) + mode_str = cabc_modes[mode]; + len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); + + return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; +} + +static ssize_t store_cabc_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { + if (sysfs_streq(cabc_modes[i], buf)) + break; + } + + if (i == ARRAY_SIZE(cabc_modes)) + return -EINVAL; + + if (td->enabled) { + dsi_bus_lock(); + if (!td->cabc_broken) + taal_dcs_write_1(DCS_WRITE_CABC, i); + dsi_bus_unlock(); + } + + td->cabc_mode = i; + + return count; +} + +static ssize_t show_cabc_available_modes(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int len; + int i; + + for (i = 0, len = 0; + len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) + len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", + i ? " " : "", cabc_modes[i], + i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); + + return len < PAGE_SIZE ? len : PAGE_SIZE - 1; +} + +static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL); +static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL); +static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, + show_cabc_mode, store_cabc_mode); +static DEVICE_ATTR(cabc_available_modes, S_IRUGO, + show_cabc_available_modes, NULL); + +static struct attribute *taal_attrs[] = { + &dev_attr_num_dsi_errors.attr, + &dev_attr_hw_revision.attr, + &dev_attr_cabc_mode.attr, + &dev_attr_cabc_available_modes.attr, + NULL, +}; + +static struct attribute_group taal_attr_group = { + .attrs = taal_attrs, +}; + +static int taal_probe(struct omap_dss_device *dssdev) +{ + struct taal_data *td; + struct backlight_device *bldev; + int r; + + const struct omap_video_timings taal_panel_timings = { + .x_res = 864, + .y_res = 480, + }; + + DBG("probe\n"); + + dssdev->panel.config = OMAP_DSS_LCD_TFT; + dssdev->panel.timings = taal_panel_timings; + dssdev->ctrl.pixel_size = 24; + + td = kzalloc(sizeof(*td), GFP_KERNEL); + if (!td) { + r = -ENOMEM; + goto err0; + } + + dev_set_drvdata(&dssdev->dev, td); + + dssdev->get_timings = taal_get_timings; + dssdev->get_resolution = taal_get_resolution; + + /* if no platform set_backlight() defined, presume DSI backlight + * control */ + if (!dssdev->set_backlight) + td->use_dsi_bl = true; + + bldev = backlight_device_register("panel-taal", &dssdev->dev, dssdev, + &taal_bl_ops); + + if (IS_ERR(bldev)) { + r = PTR_ERR(bldev); + goto err1; + } + + td->bldev = bldev; + + bldev->props.fb_blank = FB_BLANK_UNBLANK; + bldev->props.power = FB_BLANK_UNBLANK; + if (td->use_dsi_bl) { + bldev->props.max_brightness = 255; + bldev->props.brightness = 255; + } else { + bldev->props.max_brightness = 127; + bldev->props.brightness = 127; + } + + taal_bl_update_status(bldev); + + if (dssdev->phy.dsi.ext_te) { + int gpio = dssdev->phy.dsi.ext_te_gpio; + r = gpio_request(gpio, "taal irq"); + if (r) + goto err2; + + gpio_direction_input(gpio); + + r = request_irq(gpio_to_irq(gpio), taal_te_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING, + "taal vsync", dssdev); + + if (r) { + gpio_free(gpio); + goto err2; + } + + init_completion(&td->te_completion); + + td->use_ext_te = true; + } + + r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group); + if (r) { + dev_err(&dssdev->dev, "failed to create sysfs files\n"); + goto err3; + } + + return 0; +err3: + if (td->use_ext_te) { + int gpio = dssdev->phy.dsi.ext_te_gpio; + free_irq(gpio_to_irq(gpio), dssdev); + gpio_free(gpio); + } +err2: + backlight_device_unregister(bldev); +err1: + kfree(td); +err0: + return r; +} + +static void taal_remove(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bldev; + + DBG("remove\n"); + + sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group); + + if (td->use_ext_te) { + int gpio = dssdev->phy.dsi.ext_te_gpio; + free_irq(gpio_to_irq(gpio), dssdev); + gpio_free(gpio); + } + + bldev = td->bldev; + bldev->props.power = FB_BLANK_POWERDOWN; + taal_bl_update_status(bldev); + backlight_device_unregister(bldev); + + kfree(td); +} + +static int taal_enable(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + u8 id1, id2, id3; + int r; + + DBG("enable\n"); + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + return r; + } + + /* it seems we have to wait a bit until taal is ready */ + msleep(5); + +#if 1 + r = taal_sleep_out(td); + if (r) + return r; + + r = taal_get_id(&id1, &id2, &id3); + if (r) + return r; + + /* on early revisions CABC is broken */ + if (id2 == 0x00 || id2 == 0xff || id2 == 0x81) + td->cabc_broken = true; + taal_dcs_write_1(DCS_BRIGHTNESS, 0xff); + taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */ + + taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ + + taal_set_addr_mode(td->rotate, td->mirror); + if (!td->cabc_broken) + taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode); + + taal_dcs_write_0(DCS_DISPLAY_ON); + + td->enabled = 1; + + if (!td->intro_printed) { + dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n", + id1, id2, id3); + if (td->cabc_broken) + dev_info(&dssdev->dev, + "old Taal version, CABC disabled\n"); + td->intro_printed = true; + } +#endif + return 0; +} + +static void taal_disable(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + DBG("disable\n"); + + taal_dcs_write_0(DCS_DISPLAY_OFF); + taal_sleep_in(td); + + /* wait a bit so that the message goes through */ + msleep(10); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + td->enabled = 0; +} + +static int taal_suspend(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bldev = td->bldev; + + bldev->props.power = FB_BLANK_POWERDOWN; + taal_bl_update_status(bldev); + + return 0; +} + +static int taal_resume(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bldev = td->bldev; + + bldev->props.power = FB_BLANK_UNBLANK; + taal_bl_update_status(bldev); + + return 0; +} + +static void taal_setup_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + taal_set_update_window(x, y, w, h); +} + +static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + + td->te_enabled = enable; + if (enable) + r = taal_dcs_write_1(DCS_TEAR_ON, 0); + else + r = taal_dcs_write_0(DCS_TEAR_OFF); + return r; +} + +static int taal_wait_te(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + long wait = msecs_to_jiffies(500); + + if (!td->use_ext_te || !td->te_enabled) + return 0; + + INIT_COMPLETION(td->te_completion); + wait = wait_for_completion_timeout(&td->te_completion, wait); + if (wait == 0) { + printk(KERN_ERR "Taal: timeout waiting TE\n"); + return -ETIME; + } + + return 0; +} + +static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + + DBG("taal_rotate %d\n", rotate); + + if (td->enabled) { + r = taal_set_addr_mode(rotate, td->mirror); + + if (r) + return r; + } + + td->rotate = rotate; + + return 0; +} + +static u8 taal_get_rotate(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + return td->rotate; +} + +static int taal_mirror(struct omap_dss_device *dssdev, bool enable) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + + DBG("taal_mirror %d\n", enable); + + if (td->enabled) { + r = taal_set_addr_mode(td->rotate, enable); + + if (r) + return r; + } + + td->mirror = enable; + + return 0; +} + +static bool taal_get_mirror(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + return td->mirror; +} + +static int taal_run_test(struct omap_dss_device *dssdev, int test_num) +{ + u8 id1, id2, id3; + int r; + + r = taal_dcs_read_1(DCS_GET_ID1, &id1); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID2, &id2); + if (r) + return r; + r = taal_dcs_read_1(DCS_GET_ID3, &id3); + if (r) + return r; + + return 0; +} + +static int taal_memory_read(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h) +{ + int r; + int first = 1; + int plen; + unsigned buf_used = 0; + + if (size < w * h * 3) + return -ENOMEM; + + size = min(w * h * 3, + dssdev->panel.timings.x_res * + dssdev->panel.timings.y_res * 3); + + /* plen 1 or 2 goes into short packet. until checksum error is fixed, + * use short packets. plen 32 works, but bigger packets seem to + * cause an error. + */ + if (size % 2) + plen = 1; + else + plen = 2; + + taal_setup_update(dssdev, x, y, w, h); + + r = dsi_vc_set_max_rx_packet_size(TCH, plen); + if (r) + return r; + + while (buf_used < size) { + u8 dcs_cmd = first ? 0x2e : 0x3e; + first = 0; + + r = dsi_vc_dcs_read(TCH, dcs_cmd, + buf + buf_used, size - buf_used); + + if (r < 0) { + printk(KERN_ERR "Taal read error\n"); + goto err; + } + + buf_used += r; + + if (r < plen) { + printk(KERN_INFO "short read\n"); + break; + } + } + + r = buf_used; + +err: + dsi_vc_set_max_rx_packet_size(TCH, 1); + + return r; +} + +static struct omap_dss_driver taal_driver = { + .probe = taal_probe, + .remove = taal_remove, + + .enable = taal_enable, + .disable = taal_disable, + .suspend = taal_suspend, + .resume = taal_resume, + + .setup_update = taal_setup_update, + .enable_te = taal_enable_te, + .wait_for_te = taal_wait_te, + .set_rotate = taal_rotate, + .get_rotate = taal_get_rotate, + .set_mirror = taal_mirror, + .get_mirror = taal_get_mirror, + .run_test = taal_run_test, + .memory_read = taal_memory_read, + + .driver = { + .name = "panel-taal", + .owner = THIS_MODULE, + }, +}; + +static int __init taal_init(void) +{ + DBG("\n taal_init\n"); + + omap_dss_register_driver(&taal_driver); + printk(KERN_INFO "\n omap_dss_register_driver DONE "); + return 0; +} + +static void __exit taal_exit(void) +{ + DBG("exit\n"); + + omap_dss_unregister_driver(&taal_driver); +} + +module_init(taal_init); +module_exit(taal_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("Taal Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig new file mode 100644 index 000000000000..b9e9410c20cf --- /dev/null +++ b/drivers/video/omap2/dss/Kconfig @@ -0,0 +1,96 @@ +menuconfig OMAP2_DSS + tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" + depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 + default y + help + OMAP2/3/4 Display Subsystem support. + +if OMAP2_DSS + +config OMAP2_DSS_VRAM_SIZE + int "VRAM size (MB)" + range 0 32 + default 4 + help + The amount of SDRAM to reserve at boot time for video RAM use. + This VRAM will be used by omapfb and other drivers that need + large continuous RAM area for video use. + + You can also set this with "vram=<bytes>" kernel argument, or + in the board file. + +config OMAP2_DSS_DEBUG_SUPPORT + bool "Debug support" + default y + help + This enables debug messages. You need to enable printing + with 'debug' module parameter. + +config OMAP2_DSS_RFBI + bool "RFBI support" + default n + help + MIPI DBI, or RFBI (Remote Framebuffer Interface), support. + +config OMAP2_DSS_VENC + bool "VENC support" + default y + help + OMAP Video Encoder support. + +config OMAP2_DSS_HDMI + bool "HDMI support" + default n + help + OMAP HDMI panel support. + +config OMAP2_DSS_SDI + bool "SDI support" + depends on ARCH_OMAP3 + default n + help + SDI (Serial Display Interface) support. + +config OMAP2_DSS_DSI + bool "DSI support" + depends on ARCH_OMAP3 || ARCH_OMAP4 + default n + help + MIPI DSI support. + +config OMAP2_DSS_USE_DSI_PLL + bool "Use DSI PLL for PCLK (EXPERIMENTAL)" + default n + depends on OMAP2_DSS_DSI + help + Use DSI PLL to generate pixel clock. Currently only for DPI output. + DSI PLL can be used to generate higher and more precise pixel clocks. + +config OMAP2_DSS_FAKE_VSYNC + bool "Fake VSYNC irq from manual update displays" + default n + help + If this is selected, DSI will generate a fake DISPC VSYNC interrupt + when DSI has sent a frame. This is only needed with DSI or RFBI + displays using manual mode, and you want VSYNC to, for example, + time animation. + +config OMAP2_DSS_MIN_FCK_PER_PCK + int "Minimum FCK/PCK ratio (for scaling)" + range 0 32 + default 0 + help + This can be used to adjust the minimum FCK/PCK ratio. + + With this you can make sure that DISPC FCK is at least + n x PCK. Video plane scaling requires higher FCK than + normally. + + If this is set to 0, there's no extra constraint on the + DISPC FCK. However, the FCK will at minimum be + 2xPCK (if active matrix) or 3xPCK (if passive matrix). + + Max FCK is 173MHz, so this doesn't work if your PCK + is very high. + +endif diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile new file mode 100644 index 000000000000..521b754da6fb --- /dev/null +++ b/drivers/video/omap2/dss/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_OMAP2_DSS) += omapdss.o +omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o +omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o +omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o +omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o +omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o +omapdss-$(CONFIG_OMAP2_DSS_HDMI) += hdmi.o diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c new file mode 100644 index 000000000000..aad8ec4ad137 --- /dev/null +++ b/drivers/video/omap2/dss/core.c @@ -0,0 +1,934 @@ +/* + * linux/drivers/video/omap2/dss/core.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "CORE" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/device.h> + +#include <mach/display.h> +#include <mach/clock.h> + +#include "dss.h" + +static struct { + struct platform_device *pdev; + int ctx_id; + + struct clk *dss_ick; + struct clk *dss1_fck; + struct clk *dss2_fck; + struct clk *dss_54m_fck; + struct clk *dss_96m_fck; + unsigned num_clks_enabled; +} core; + +static void dss_clk_enable_all_no_ctx(void); +static void dss_clk_disable_all_no_ctx(void); +static void dss_clk_enable_no_ctx(enum dss_clock clks); +static void dss_clk_disable_no_ctx(enum dss_clock clks); + +static char *def_disp_name; +module_param_named(def_disp, def_disp_name, charp, 0); +MODULE_PARM_DESC(def_disp_name, "default display name"); + +#ifdef DEBUG +unsigned int dss_debug = 1; +module_param_named(debug, dss_debug, bool, 0644); +#endif + +/* CONTEXT */ +static int dss_get_ctx_id(void) +{ + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + int r; + + if (!pdata->get_last_off_on_transaction_id) + return 0; + r = pdata->get_last_off_on_transaction_id(&core.pdev->dev); + if (r < 0) { + dev_err(&core.pdev->dev, "getting transaction ID failed, " + "will force context restore\n"); + r = -1; + } + return r; +} + +int dss_need_ctx_restore(void) +{ + int id = dss_get_ctx_id(); + + if (id < 0 || id != core.ctx_id) { + DSSDBG("ctx id %d -> id %d\n", + core.ctx_id, id); + core.ctx_id = id; + return 1; + } else { + return 0; + } +} + +static void save_all_ctx(void) +{ + DSSDBG("save context\n"); + + dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); + + dss_save_context(); + dispc_save_context(); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_save_context(); +#endif + + dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +static void restore_all_ctx(void) +{ + DSSDBG("restore context\n"); + + dss_clk_enable_all_no_ctx(); + + dss_restore_context(); + dispc_restore_context(); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_restore_context(); +#endif + + dss_clk_disable_all_no_ctx(); +} + +/* CLOCKS */ +void dss_dump_clocks(struct seq_file *s) +{ + int i; + struct clk *clocks[5] = { + core.dss_ick, + core.dss1_fck, + core.dss2_fck, + core.dss_54m_fck, + core.dss_96m_fck + }; + + seq_printf(s, "- dss -\n"); + + seq_printf(s, "internal clk count\t%u\n", core.num_clks_enabled); + + for (i = 0; i < 5; i++) { + if (!clocks[i]) + continue; + seq_printf(s, "%-15s\t%lu\t%d\n", + clocks[i]->name, + clk_get_rate(clocks[i]), + clocks[i]->usecount); + } +} + +static int dss_get_clock(struct clk **clock, const char *clk_name) +{ + struct clk *clk; + + clk = clk_get(&core.pdev->dev, clk_name); + + if (IS_ERR(clk)) { + DSSERR("can't get clock %s", clk_name); + return PTR_ERR(clk); + } + + *clock = clk; + + DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk)); + + return 0; +} + +static int dss_get_clocks(void) +{ + int r; + + core.dss_ick = NULL; + core.dss1_fck = NULL; + core.dss2_fck = NULL; + core.dss_54m_fck = NULL; + core.dss_96m_fck = NULL; + + r = dss_get_clock(&core.dss_ick, "ick"); + if (r) + goto err; + + r = dss_get_clock(&core.dss1_fck, "dss1_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss2_fck, "dss2_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss_54m_fck, "tv_fck"); + if (r) + goto err; + + r = dss_get_clock(&core.dss_96m_fck, "video_fck"); + if (r) + goto err; + + return 0; + +err: + if (core.dss_ick) + clk_put(core.dss_ick); + if (core.dss1_fck) + clk_put(core.dss1_fck); + if (core.dss2_fck) + clk_put(core.dss2_fck); + if (core.dss_54m_fck) + clk_put(core.dss_54m_fck); + if (core.dss_96m_fck) + clk_put(core.dss_96m_fck); + + return r; +} + +static void dss_put_clocks(void) +{ + if (core.dss_96m_fck) + clk_put(core.dss_96m_fck); + clk_put(core.dss_54m_fck); + clk_put(core.dss1_fck); + clk_put(core.dss2_fck); + clk_put(core.dss_ick); +} + +unsigned long dss_clk_get_rate(enum dss_clock clk) +{ + switch (clk) { + case DSS_CLK_ICK: + return clk_get_rate(core.dss_ick); + case DSS_CLK_FCK1: + return clk_get_rate(core.dss1_fck); + case DSS_CLK_FCK2: + return clk_get_rate(core.dss2_fck); + case DSS_CLK_54M: + return clk_get_rate(core.dss_54m_fck); + case DSS_CLK_96M: + return clk_get_rate(core.dss_96m_fck); + } + + BUG(); + return 0; +} + +static unsigned count_clk_bits(enum dss_clock clks) +{ + unsigned num_clks = 0; + + if (clks & DSS_CLK_ICK) + ++num_clks; + if (clks & DSS_CLK_FCK1) + ++num_clks; + if (clks & DSS_CLK_FCK2) + ++num_clks; + if (clks & DSS_CLK_54M) + ++num_clks; + if (clks & DSS_CLK_96M) + ++num_clks; + + return num_clks; +} + +static void dss_clk_enable_no_ctx(enum dss_clock clks) +{ + unsigned num_clks = count_clk_bits(clks); + + if (clks & DSS_CLK_ICK) + clk_enable(core.dss_ick); + if (clks & DSS_CLK_FCK1) + clk_enable(core.dss1_fck); + if (clks & DSS_CLK_FCK2) + clk_enable(core.dss2_fck); + if (clks & DSS_CLK_54M) + clk_enable(core.dss_54m_fck); + if (clks & DSS_CLK_96M) + clk_enable(core.dss_96m_fck); + + core.num_clks_enabled += num_clks; +} + +void dss_clk_enable(enum dss_clock clks) +{ + dss_clk_enable_no_ctx(clks); + + if (cpu_is_omap34xx() && dss_need_ctx_restore()) + restore_all_ctx(); + else if (cpu_is_omap44xx() && dss_need_ctx_restore()) + restore_all_ctx(); +} + +static void dss_clk_disable_no_ctx(enum dss_clock clks) +{ + unsigned num_clks = count_clk_bits(clks); + + if (clks & DSS_CLK_ICK) + clk_disable(core.dss_ick); + if (clks & DSS_CLK_FCK1) + clk_disable(core.dss1_fck); + if (clks & DSS_CLK_FCK2) + clk_disable(core.dss2_fck); + if (clks & DSS_CLK_54M) + clk_disable(core.dss_54m_fck); + if (clks & DSS_CLK_96M) + clk_disable(core.dss_96m_fck); + + core.num_clks_enabled -= num_clks; +} + +void dss_clk_disable(enum dss_clock clks) +{ + if (cpu_is_omap34xx()) { + unsigned num_clks = count_clk_bits(clks); + + BUG_ON(core.num_clks_enabled < num_clks); + + if (core.num_clks_enabled == num_clks) + save_all_ctx(); + } + + dss_clk_disable_no_ctx(clks); +} + +static void dss_clk_enable_all_no_ctx(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx() || cpu_is_omap44xx()) + clks |= DSS_CLK_96M; + dss_clk_enable_no_ctx(clks); +} + +static void dss_clk_disable_all_no_ctx(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx() || cpu_is_omap44xx()) + clks |= DSS_CLK_96M; + dss_clk_disable_no_ctx(clks); +} + +static void dss_clk_disable_all(void) +{ + enum dss_clock clks; + + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; + if (cpu_is_omap34xx() || cpu_is_omap44xx()) + clks |= DSS_CLK_96M; + dss_clk_disable(clks); +} + +/* DEBUGFS */ +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) +static void dss_debug_dump_clocks(struct seq_file *s) +{ + dss_dump_clocks(s); + dispc_dump_clocks(s); +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_dump_clocks(s); +#endif +} + +static int dss_debug_show(struct seq_file *s, void *unused) +{ + void (*func)(struct seq_file *) = s->private; + func(s); + return 0; +} + +static int dss_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, dss_debug_show, inode->i_private); +} + +static const struct file_operations dss_debug_fops = { + .open = dss_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *dss_debugfs_dir; + +static int dss_initialize_debugfs(void) +{ + dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); + if (IS_ERR(dss_debugfs_dir)) { + int err = PTR_ERR(dss_debugfs_dir); + dss_debugfs_dir = NULL; + return err; + } + + debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, + &dss_debug_dump_clocks, &dss_debug_fops); + + debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, + &dss_dump_regs, &dss_debug_fops); + debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir, + &dispc_dump_regs, &dss_debug_fops); +#ifdef CONFIG_OMAP2_DSS_RFBI + debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir, + &rfbi_dump_regs, &dss_debug_fops); +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir, + &dsi_dump_regs, &dss_debug_fops); +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, + &venc_dump_regs, &dss_debug_fops); +#endif + return 0; +} + +static void dss_uninitialize_debugfs(void) +{ + if (dss_debugfs_dir) + debugfs_remove_recursive(dss_debugfs_dir); +} +#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ + +/* PLATFORM DEVICE */ +static int omap_dss_probe(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int skip_init = 0; + int r; + int i; + + core.pdev = pdev; + + dss_init_overlay_managers(pdev); + dss_init_overlays(pdev); + + /* + * FIX-ME: Replace with correct clk node when clk + * framework is available + */ + if (!cpu_is_omap44xx()) { + r = dss_get_clocks(); + if (r) + goto fail0; + } + + dss_clk_enable_all_no_ctx(); + + core.ctx_id = dss_get_ctx_id(); + DSSDBG("initial ctx id %u\n", core.ctx_id); + +#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT + /* DISPC_CONTROL */ + if (omap_readl(0x48050440) & 1) /* LCD enabled? */ + skip_init = 1; +#endif + + r = dss_init(skip_init); + if (r) { + DSSERR("Failed to initialize DSS\n"); + goto fail0; + } + +#ifdef CONFIG_OMAP2_DSS_RFBI + r = rfbi_init(); + if (r) { + DSSERR("Failed to initialize rfbi\n"); + goto fail0; + } +#endif + + r = dpi_init(); + if (r) { + DSSERR("Failed to initialize dpi\n"); + goto fail0; + } + + r = dispc_init(); + if (r) { + DSSERR("Failed to initialize dispc\n"); + goto fail0; + } +#ifdef CONFIG_OMAP2_DSS_VENC + r = venc_init(pdev); + if (r) { + DSSERR("Failed to initialize venc\n"); + goto fail0; + } +#endif + if (cpu_is_omap34xx()) { +#ifdef CONFIG_OMAP2_DSS_SDI + r = sdi_init(skip_init); + if (r) { + DSSERR("Failed to initialize SDI\n"); + goto fail0; + } +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_init(pdev); + if (r) { + DSSERR("Failed to initialize DSI\n"); + goto fail0; + } +#endif + } +#ifdef CONFIG_OMAP2_DSS_HDMI + r = hdmi_init(pdev); + if (r) { + DSSERR("Failed to initialize hdmi\n"); + goto fail0; + } +#endif +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) + r = dss_initialize_debugfs(); + if (r) + goto fail0; +#endif + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + r = omap_dss_register_device(dssdev); + if (r) + DSSERR("device reg failed %d\n", i); + + if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) + pdata->default_device = dssdev; + } + + dss_clk_disable_all(); + + return 0; + + /* XXX fail correctly */ +fail0: + return r; +} + +static int omap_dss_remove(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i; + int c; + +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) + dss_uninitialize_debugfs(); +#endif + +#ifdef CONFIG_OMAP2_DSS_VENC + venc_exit(); +#endif +#ifdef CONFIG_OMAP2_DSS_HDMI + hdmi_exit(); +#endif + dispc_exit(); + dpi_exit(); +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_exit(); +#endif + if (cpu_is_omap34xx()) { +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_exit(); +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_exit(); +#endif + } + + dss_exit(); + + /* these should be removed at some point */ + c = core.dss_ick->usecount; + if (c > 0) { + DSSERR("warning: dss_ick usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss_ick); + } + + c = core.dss1_fck->usecount; + if (c > 0) { + DSSERR("warning: dss1_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss1_fck); + } + + c = core.dss2_fck->usecount; + if (c > 0) { + DSSERR("warning: dss2_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss2_fck); + } + + c = core.dss_54m_fck->usecount; + if (c > 0) { + DSSERR("warning: dss_54m_fck usecount %d, disabling\n", c); + while (c-- > 0) + clk_disable(core.dss_54m_fck); + } + + if (core.dss_96m_fck) { + c = core.dss_96m_fck->usecount; + if (c > 0) { + DSSERR("warning: dss_96m_fck usecount %d, disabling\n", + c); + while (c-- > 0) + clk_disable(core.dss_96m_fck); + } + } + + dss_put_clocks(); + + dss_uninit_overlays(pdev); + dss_uninit_overlay_managers(pdev); + + for (i = 0; i < pdata->num_devices; ++i) + omap_dss_unregister_device(pdata->devices[i]); + + return 0; +} + +static void omap_dss_shutdown(struct platform_device *pdev) +{ + DSSDBG("shutdown\n"); +} + +static int omap_dss_suspend(struct platform_device *pdev, pm_message_t state) +{ + DSSDBG("suspend %d\n", state.event); + + return dss_suspend_all_devices(); +} + +static int omap_dss_resume(struct platform_device *pdev) +{ + DSSDBG("resume\n"); + + return dss_resume_all_devices(); +} + +static struct platform_driver omap_dss_driver = { + .probe = omap_dss_probe, + .remove = omap_dss_remove, + .shutdown = omap_dss_shutdown, + .suspend = omap_dss_suspend, + .resume = omap_dss_resume, + .driver = { + .name = "omapdss", + .owner = THIS_MODULE, + }, +}; + +/* BUS */ +static int dss_bus_match(struct device *dev, struct device_driver *driver) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + + DSSDBG("bus_match. dev %s/%s, drv %s\n", + dev_name(dev), dssdev->driver_name, driver->name); + + return strcmp(dssdev->driver_name, driver->name) == 0; +} + +static ssize_t device_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", + dssdev->name ? + dssdev->name : ""); +} + +static struct device_attribute default_dev_attrs[] = { + __ATTR(name, S_IRUGO, device_name_show, NULL), + __ATTR_NULL, +}; + +static ssize_t driver_name_show(struct device_driver *drv, char *buf) +{ + struct omap_dss_driver *dssdrv = to_dss_driver(drv); + return snprintf(buf, PAGE_SIZE, "%s\n", + dssdrv->driver.name ? + dssdrv->driver.name : ""); +} +static struct driver_attribute default_drv_attrs[] = { + __ATTR(name, S_IRUGO, driver_name_show, NULL), + __ATTR_NULL, +}; + +static struct bus_type dss_bus_type = { + .name = "omapdss", + .match = dss_bus_match, + .dev_attrs = default_dev_attrs, + .drv_attrs = default_drv_attrs, +}; + +static void dss_bus_release(struct device *dev) +{ + DSSDBG("bus_release\n"); +} + +static struct device dss_bus = { + .release = dss_bus_release, +}; + +struct bus_type *dss_get_bus(void) +{ + return &dss_bus_type; +} + +/* DRIVER */ +static int dss_driver_probe(struct device *dev) +{ + int r; + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + bool force; + + DSSDBG("driver_probe: dev %s/%s, drv %s\n", + dev_name(dev), dssdev->driver_name, + dssdrv->driver.name); + + dss_init_device(core.pdev, dssdev); + + /* skip this if the device is behind a ctrl */ + if (!dssdev->panel.ctrl) { + force = pdata->default_device == dssdev; + dss_recheck_connections(dssdev, force); + } + + r = dssdrv->probe(dssdev); + + if (r) { + DSSERR("driver probe failed: %d\n", r); + return r; + } + + DSSDBG("probe done for device %s\n", dev_name(dev)); + + dssdev->driver = dssdrv; + + return 0; +} + +static int dss_driver_remove(struct device *dev) +{ + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); + struct omap_dss_device *dssdev = to_dss_device(dev); + + DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev), + dssdev->driver_name); + + dssdrv->remove(dssdev); + + dss_uninit_device(core.pdev, dssdev); + + dssdev->driver = NULL; + + return 0; +} + +int omap_dss_register_driver(struct omap_dss_driver *dssdriver) +{ + dssdriver->driver.bus = &dss_bus_type; + dssdriver->driver.probe = dss_driver_probe; + dssdriver->driver.remove = dss_driver_remove; + return driver_register(&dssdriver->driver); +} +EXPORT_SYMBOL(omap_dss_register_driver); + +void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver) +{ + driver_unregister(&dssdriver->driver); +} +EXPORT_SYMBOL(omap_dss_unregister_driver); + +/* DEVICE */ +static void reset_device(struct device *dev, int check) +{ + u8 *dev_p = (u8 *)dev; + u8 *dev_end = dev_p + sizeof(*dev); + void *saved_pdata; + + saved_pdata = dev->platform_data; + if (check) { + /* + * Check if there is any other setting than platform_data + * in struct device; warn that these will be reset by our + * init. + */ + dev->platform_data = NULL; + while (dev_p < dev_end) { + if (*dev_p) { + WARN("%s: struct device fields will be " + "discarded\n", + __func__); + break; + } + dev_p++; + } + } + memset(dev, 0, sizeof(*dev)); + dev->platform_data = saved_pdata; +} + + +static void omap_dss_dev_release(struct device *dev) +{ + reset_device(dev, 0); +} + +int omap_dss_register_device(struct omap_dss_device *dssdev) +{ + static int dev_num; + static int panel_num; + int r; + + WARN_ON(!dssdev->driver_name); + + reset_device(&dssdev->dev, 1); + dssdev->dev.bus = &dss_bus_type; + dssdev->dev.parent = &dss_bus; + dssdev->dev.release = omap_dss_dev_release; + dev_set_name(&dssdev->dev, "display%d", dev_num++); + r = device_register(&dssdev->dev); + if (r) + return r; + + if (dssdev->ctrl.panel) { + struct omap_dss_device *panel = dssdev->ctrl.panel; + + panel->panel.ctrl = dssdev; + + reset_device(&panel->dev, 1); + panel->dev.bus = &dss_bus_type; + panel->dev.parent = &dssdev->dev; + panel->dev.release = omap_dss_dev_release; + dev_set_name(&panel->dev, "panel%d", panel_num++); + r = device_register(&panel->dev); + if (r) + return r; + } + + return 0; +} + +void omap_dss_unregister_device(struct omap_dss_device *dssdev) +{ + device_unregister(&dssdev->dev); + + if (dssdev->ctrl.panel) { + struct omap_dss_device *panel = dssdev->ctrl.panel; + device_unregister(&panel->dev); + } +} + +/* BUS */ +static int omap_dss_bus_register(void) +{ + int r; + + r = bus_register(&dss_bus_type); + if (r) { + DSSERR("bus register failed\n"); + return r; + } + + dev_set_name(&dss_bus, "omapdss"); + r = device_register(&dss_bus); + if (r) { + DSSERR("bus driver register failed\n"); + bus_unregister(&dss_bus_type); + return r; + } + + return 0; +} + +/* INIT */ + +#ifdef CONFIG_OMAP2_DSS_MODULE +static void omap_dss_bus_unregister(void) +{ + device_unregister(&dss_bus); + + bus_unregister(&dss_bus_type); +} + +static int __init omap_dss_init(void) +{ + int r; + + r = omap_dss_bus_register(); + if (r) + return r; + + r = platform_driver_register(&omap_dss_driver); + if (r) { + omap_dss_bus_unregister(); + return r; + } + + return 0; +} + +static void __exit omap_dss_exit(void) +{ + platform_driver_unregister(&omap_dss_driver); + + omap_dss_bus_unregister(); +} + +module_init(omap_dss_init); +module_exit(omap_dss_exit); +#else +static int __init omap_dss_init(void) +{ + return omap_dss_bus_register(); +} + +static int __init omap_dss_init2(void) +{ + return platform_driver_register(&omap_dss_driver); +} + +core_initcall(omap_dss_init); +device_initcall(omap_dss_init2); +#endif + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("OMAP2/3 Display Subsystem"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c new file mode 100644 index 000000000000..3db9ec6cf6f4 --- /dev/null +++ b/drivers/video/omap2/dss/dispc.c @@ -0,0 +1,4163 @@ +/* + * linux/drivers/video/omap2/dss/dispc.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DISPC" + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/seq_file.h> +#include <linux/delay.h> +#include <linux/workqueue.h> + +#include <mach/sram.h> +#include <mach/board.h> +#include <mach/clock.h> + +#include <mach/display.h> + +#include "dss.h" +#include "../../../media/video/tiler/tiler.h" + +#ifndef CONFIG_ARCH_OMAP4 + /* DSS */ + #define DSS_BASE 0x48050000 + /* DISPLAY CONTROLLER */ + #define DISPC_BASE 0x48050400 +#else + /* DSS */ + #define DSS_BASE 0x48040000 + /* DISPLAY CONTROLLER */ + #define DISPC_BASE 0x48041000 +#endif + +#define DISPC_SZ_REGS SZ_1K + +struct dispc_reg { u16 idx; }; + +#define DISPC_REG(idx) ((const struct dispc_reg) { idx }) + +/* DISPC common */ +#define DISPC_REVISION DISPC_REG(0x0000) +#define DISPC_SYSCONFIG DISPC_REG(0x0010) +#define DISPC_SYSSTATUS DISPC_REG(0x0014) +#define DISPC_IRQSTATUS DISPC_REG(0x0018) +#define DISPC_IRQENABLE DISPC_REG(0x001C) +#define DISPC_CONTROL DISPC_REG(0x0040) + /* DISPC_CONTROL1 in OMAP4 */ +#define DISPC_CONFIG DISPC_REG(0x0044) + /* DISPC_CONFIG1 in OMAP4 */ +#define DISPC_CAPABLE DISPC_REG(0x0048) +#define DISPC_DEFAULT_COLOR0 DISPC_REG(0x004C) +#define DISPC_DEFAULT_COLOR1 DISPC_REG(0x0050) +#define DISPC_TRANS_COLOR0 DISPC_REG(0x0054) +#define DISPC_TRANS_COLOR1 DISPC_REG(0x0058) +#define DISPC_LINE_STATUS DISPC_REG(0x005C) +#define DISPC_LINE_NUMBER DISPC_REG(0x0060) +#define DISPC_TIMING_H DISPC_REG(0x0064) + /* DISPC_TIMING_H1 in OMAP4 */ +#define DISPC_TIMING_V DISPC_REG(0x0068) + /* DISPC_TIMING_V1 in OMAP4 */ +#define DISPC_POL_FREQ DISPC_REG(0x006C) + /* DISPC_POL_FREQ1 in OMAP4 */ + +#ifndef CONFIG_ARCH_OMAP4 +#define DISPC_DIVISOR DISPC_REG(0x0070) +#else +#define DISPC_DIVISOR DISPC_REG(0x0804) +#define DISPC_DIVISOR1 DISPC_REG(0x0070) +#endif + +#define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) +#define DISPC_SIZE_DIG DISPC_REG(0x0078) + /* DISPC_SIZE_TV in OMAP4 */ +#define DISPC_SIZE_LCD DISPC_REG(0x007C) + /* DISPC_SIZE_LCD1 in OMAP4 */ + +#ifdef CONFIG_ARCH_OMAP4 +#define DISPC_GLOBAL_BUFFER DISPC_REG(0x0800) +#endif + +/* DISPC GFX plane */ +#define DISPC_GFX_BA0 DISPC_REG(0x0080) +#define DISPC_GFX_BA1 DISPC_REG(0x0084) +#define DISPC_GFX_POSITION DISPC_REG(0x0088) +#define DISPC_GFX_SIZE DISPC_REG(0x008C) +#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0) +#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4) + /* DISPC_GFX_BUF_THRESHOLD in OMAP4 */ +#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) + /* DISPC_GFX_BUF_SIZE_STATUS in OMAP4 */ +#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC) +#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0) +#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) +#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) + +#define DISPC_DATA_CYCLE1 DISPC_REG(0x01D4) + /* DISPC_DATA1_CYCLE1 in OMAP4 */ +#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8) + /* DISPC_DATA1_CYCLE2 in OMAP4 */ +#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC) + /* DISPC_DATA1_CYCLE3 in OMAP4 */ + +#define DISPC_CPR_COEF_R DISPC_REG(0x0220) + /* DISPC_CPR1_COEFF_R in OMAP4 */ +#define DISPC_CPR_COEF_G DISPC_REG(0x0224) + /* DISPC_CPR1_COEFF_G in OMAP4 */ +#define DISPC_CPR_COEF_B DISPC_REG(0x0228) + /* DISPC_CPR1_COEFF_B in OMAP4 */ + +#define DISPC_GFX_PRELOAD DISPC_REG(0x022C) + +/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */ +#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx) + +#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000) +#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004) + +#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008) +#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C) +#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010) +#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014) +#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018) +#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C) +#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020) +#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024) +#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028) +#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C) +#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8) +/* coef index i = {0, 1, 2, 3, 4} */ +#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4) +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_V(n, i) DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4) + +#define DISPC_VID_PRELOAD(n) DISPC_REG(0x230 + (n)*0x04) + +#ifdef CONFIG_ARCH_OMAP4 + +#define DISPC_CONTROL2 DISPC_REG(0x0238) + +/******** registers related to VID3 and WB pipelines ****/ +/* DISPC Video plane, n = 0 for VID3, n = 1 for WB _VID_V3_WB_ */ +#define DISPC_VID_V3_WB_REG(n, idx) DISPC_REG(0x0300 + (n)*0x200 + idx) + +#define DISPC_VID_V3_WB_ACCU0(n) DISPC_VID_V3_WB_REG(n, 0x0000) +#define DISPC_VID_V3_WB_ACCU1(n) DISPC_VID_V3_WB_REG(n, 0x0004) + +#define DISPC_VID_V3_WB_BA0(n) DISPC_VID_V3_WB_REG(n, 0x0008) +#define DISPC_VID_V3_WB_BA1(n) DISPC_VID_V3_WB_REG(n, 0x000C) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_H(n, i) DISPC_REG(0x0310+(n)*0x200+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_HV(n, i) DISPC_REG(0x0314+(n)*0x200+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_V(n, i) DISPC_REG(0x0350+(n)*0x200+(i)*0x4) + +#define DISPC_VID_V3_WB_ATTRIBUTES(n) DISPC_VID_V3_WB_REG(n, 0x0070) + +/* coef index i = {0, 1, 2, 3, 4} */ +#define DISPC_VID_V3_WB_CONV_COEF(n, i) DISPC_REG(0x0374 + (n)*0x200 + (i)*0x4) + +#define DISPC_VID_V3_WB_BUF_SIZE_STATUS(n) DISPC_VID_V3_WB_REG(n, 0x0088) +#define DISPC_VID_V3_WB_BUF_THRESHOLD(n) DISPC_VID_V3_WB_REG(n, 0x008C) +#define DISPC_VID_V3_WB_FIR(n) DISPC_VID_V3_WB_REG(n, 0x0090) +#define DISPC_VID_V3_WB_PICTURE_SIZE(n) DISPC_VID_V3_WB_REG(n, 0x0094) +#define DISPC_VID_V3_WB_PIXEL_INC(n) DISPC_VID_V3_WB_REG(n, 0x0098) + +#define DISPC_VID_VID3_POSITION DISPC_REG(0x039C) +#define DISPC_VID_VID3_PRELOAD DISPC_REG(0x03A0) + +#define DISPC_VID_V3_WB_ROW_INC(n) DISPC_VID_V3_WB_REG(n, 0x00A4) +#define DISPC_VID_V3_WB_SIZE(n) DISPC_VID_V3_WB_REG(n, 0x00A8) + +#define DISPC_VID_V3_WB_FIR2(n) DISPC_REG(0x0724 + (n)*0x6C) + /* n=0: VID3, n=1: WB*/ + +#define DISPC_VID_V3_WB_ACCU2_0(n) DISPC_REG(0x0728 + (n)*0x6C) + /* n=0: VID3, n=1: WB*/ +#define DISPC_VID_V3_WB_ACCU2_1(n) DISPC_REG(0x072C + (n)*0x6C) + /* n=0: VID3, n=1: WB*/ + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} n=0: VID3, n=1: WB */ +#define DISPC_VID_V3_WB_FIR_COEF_H2(n, i) DISPC_REG(0x0730+(n)*0x6C+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_HV2(n, i) DISPC_REG(0x0734+(n)*0x6C+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_V2(n, i) DISPC_REG(0x0770+(n)*0x6C+(i)*0x4) + + +/*********End Vid3 and WB Registers ***************/ + +/********** OMAP4 new global registers **/ +#define DISPC_DEFAULT_COLOR2 DISPC_REG(0x03AC) +#define DISPC_TRANS_COLOR2 DISPC_REG(0x03B0) +#define DISPC_CPR2_COEF_B DISPC_REG(0x03B4) +#define DISPC_CPR2_COEF_G DISPC_REG(0x03B8) +#define DISPC_CPR2_COEF_R DISPC_REG(0x03BC) +#define DISPC_DATA2_CYCLE1 DISPC_REG(0x03C0) +#define DISPC_DATA2_CYCLE2 DISPC_REG(0x03C4) +#define DISPC_DATA2_CYCLE3 DISPC_REG(0x03C8) +#define DISPC_SIZE_LCD2 DISPC_REG(0x03CC) +#define DISPC_TIMING_H2 DISPC_REG(0x0400) +#define DISPC_TIMING_V2 DISPC_REG(0x0404) +#define DISPC_POL_FREQ2 DISPC_REG(0x0408) +#define DISPC_DIVISOR2 DISPC_REG(0x040C) + + + +/* DISPC Video plane, + n = 0 for VID1 + n = 1 for VID2 + and n = 2 for VID3, + n = 3 for WB*/ + +#define DISPC_VID_OMAP4_REG(n, idx) DISPC_REG(0x0600 + (n)*0x04 + idx) + +#define DISPC_VID_BA_UV0(n) DISPC_VID_OMAP4_REG((n)*2, 0x0000) +#define DISPC_VID_BA_UV1(n) DISPC_VID_OMAP4_REG((n)*2, 0x0004) + +#define DISPC_CONFIG2 DISPC_REG(0x0620) + +#define DISPC_VID_ATTRIBUTES2(n) DISPC_VID_OMAP4_REG(n, 0x0024) + /* n = {0,1,2,3} */ +#define DISPC_GAMMA_TABLE(n) DISPC_VID_OMAP4_REG(n, 0x0030) + /* n = {0,1,2,3} */ + +/* VID1/VID2 specific new registers */ +#define DISPC_VID_FIR2(n) DISPC_REG(0x063C + (n)*0x6C) + /* n=0: VID1, n=1: VID2*/ + +#define DISPC_VID_ACCU2_0(n) DISPC_REG(0x0640 + (n)*0x6C) + /* n=0: VID1, n=1: VID2*/ +#define DISPC_VID_ACCU2_1(n) DISPC_REG(0x0644 + (n)*0x6C) + /* n=0: VID1, n=1: VID2*/ + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} n=0: VID1, n=1: VID2 */ +#define DISPC_VID_FIR_COEF_H2(n, i) DISPC_REG(0x0648 + (n)*0x6C + (i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_HV2(n, i) DISPC_REG(0x064C + (n)*0x6C + (i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_V2(n, i) DISPC_REG(0x0688 + (n)*0x6C + (i)*0x4) + +/*end of VID1/VID2 specific new registers*/ + + +#endif + +/*#Enable sync after LCD comes up! SPC_IRQ_SYNC_LOST | \ +*/ +#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ + DISPC_IRQ_OCP_ERR | \ + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ + DISPC_IRQ_SYNC_LOST_DIGIT) + +#define DISPC_MAX_NR_ISRS 8 + +struct omap_dispc_isr_data { + omap_dispc_isr_t isr; + void *arg; + u32 mask; +}; + +#define REG_GET(idx, start, end) \ + FLD_GET(dispc_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) + +static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID_ATTRIBUTES(0), + DISPC_VID_ATTRIBUTES(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_ATTRIBUTES(0)/* VID 3 pipeline */ +#endif + +}; + +static struct { + void __iomem *base; + + struct clk *dpll4_m4_ck; + + unsigned long cache_req_pck; + unsigned long cache_prate; + struct dispc_clock_info cache_cinfo; + + u32 fifo_size[3]; + + spinlock_t irq_lock; + u32 irq_error_mask; + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; + u32 error_irqs; + struct work_struct error_work; + + u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; +} dispc; + +static void _omap_dispc_set_irqs(void); + +static inline void dispc_write_reg(const struct dispc_reg idx, u32 val) +{ + __raw_writel(val, dispc.base + idx.idx); +} + +static inline u32 dispc_read_reg(const struct dispc_reg idx) +{ + return __raw_readl(dispc.base + idx.idx); +} + +static inline u8 calc_tiler_orientation(u8 rotation, u8 mir) +{ + static u8 orientation; + switch (rotation) { + case 0: + orientation = (mir ? 0x2 : 0x0); + break; + case 1: + orientation = (mir ? 0x7 : 0x6); + break; + case 2: + orientation = (mir ? 0x1 : 0x3); + break; + case 3: + orientation = (mir ? 0x4 : 0x5); + break; + } + return orientation; +} + +#define SR(reg) \ + dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg) +#define RR(reg) \ + dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)]) + +void dispc_save_context(void) +{ + if (cpu_is_omap24xx()) + return; + + SR(SYSCONFIG); + SR(IRQENABLE); + SR(CONTROL); + SR(CONFIG); + SR(DEFAULT_COLOR0); + SR(DEFAULT_COLOR1); + SR(TRANS_COLOR0); + SR(TRANS_COLOR1); + SR(LINE_NUMBER); + SR(TIMING_H); + SR(TIMING_V); + SR(POL_FREQ); + SR(DIVISOR); + SR(GLOBAL_ALPHA); + SR(SIZE_DIG); + SR(SIZE_LCD); + + SR(GFX_BA0); + SR(GFX_BA1); + SR(GFX_POSITION); + SR(GFX_SIZE); + SR(GFX_ATTRIBUTES); + SR(GFX_FIFO_THRESHOLD); + SR(GFX_ROW_INC); + SR(GFX_PIXEL_INC); + SR(GFX_WINDOW_SKIP); + SR(GFX_TABLE_BA); + + SR(DATA_CYCLE1); + SR(DATA_CYCLE2); + SR(DATA_CYCLE3); + + SR(CPR_COEF_R); + SR(CPR_COEF_G); + SR(CPR_COEF_B); + + SR(GFX_PRELOAD); + +#ifdef CONFIG_ARCH_OMAP4 +/* TODO: find out what needs to be saved */ +#endif + + /* VID1 */ + SR(VID_BA0(0)); + SR(VID_BA1(0)); + SR(VID_POSITION(0)); + SR(VID_SIZE(0)); + SR(VID_ATTRIBUTES(0)); + SR(VID_FIFO_THRESHOLD(0)); + SR(VID_ROW_INC(0)); + SR(VID_PIXEL_INC(0)); + SR(VID_FIR(0)); + SR(VID_PICTURE_SIZE(0)); + SR(VID_ACCU0(0)); + SR(VID_ACCU1(0)); + + SR(VID_FIR_COEF_H(0, 0)); + SR(VID_FIR_COEF_H(0, 1)); + SR(VID_FIR_COEF_H(0, 2)); + SR(VID_FIR_COEF_H(0, 3)); + SR(VID_FIR_COEF_H(0, 4)); + SR(VID_FIR_COEF_H(0, 5)); + SR(VID_FIR_COEF_H(0, 6)); + SR(VID_FIR_COEF_H(0, 7)); + + SR(VID_FIR_COEF_HV(0, 0)); + SR(VID_FIR_COEF_HV(0, 1)); + SR(VID_FIR_COEF_HV(0, 2)); + SR(VID_FIR_COEF_HV(0, 3)); + SR(VID_FIR_COEF_HV(0, 4)); + SR(VID_FIR_COEF_HV(0, 5)); + SR(VID_FIR_COEF_HV(0, 6)); + SR(VID_FIR_COEF_HV(0, 7)); + + SR(VID_CONV_COEF(0, 0)); + SR(VID_CONV_COEF(0, 1)); + SR(VID_CONV_COEF(0, 2)); + SR(VID_CONV_COEF(0, 3)); + SR(VID_CONV_COEF(0, 4)); + + SR(VID_FIR_COEF_V(0, 0)); + SR(VID_FIR_COEF_V(0, 1)); + SR(VID_FIR_COEF_V(0, 2)); + SR(VID_FIR_COEF_V(0, 3)); + SR(VID_FIR_COEF_V(0, 4)); + SR(VID_FIR_COEF_V(0, 5)); + SR(VID_FIR_COEF_V(0, 6)); + SR(VID_FIR_COEF_V(0, 7)); + + SR(VID_PRELOAD(0)); + + /* VID2 */ + SR(VID_BA0(1)); + SR(VID_BA1(1)); + SR(VID_POSITION(1)); + SR(VID_SIZE(1)); + SR(VID_ATTRIBUTES(1)); + SR(VID_FIFO_THRESHOLD(1)); + SR(VID_ROW_INC(1)); + SR(VID_PIXEL_INC(1)); + SR(VID_FIR(1)); + SR(VID_PICTURE_SIZE(1)); + SR(VID_ACCU0(1)); + SR(VID_ACCU1(1)); + + SR(VID_FIR_COEF_H(1, 0)); + SR(VID_FIR_COEF_H(1, 1)); + SR(VID_FIR_COEF_H(1, 2)); + SR(VID_FIR_COEF_H(1, 3)); + SR(VID_FIR_COEF_H(1, 4)); + SR(VID_FIR_COEF_H(1, 5)); + SR(VID_FIR_COEF_H(1, 6)); + SR(VID_FIR_COEF_H(1, 7)); + + SR(VID_FIR_COEF_HV(1, 0)); + SR(VID_FIR_COEF_HV(1, 1)); + SR(VID_FIR_COEF_HV(1, 2)); + SR(VID_FIR_COEF_HV(1, 3)); + SR(VID_FIR_COEF_HV(1, 4)); + SR(VID_FIR_COEF_HV(1, 5)); + SR(VID_FIR_COEF_HV(1, 6)); + SR(VID_FIR_COEF_HV(1, 7)); + + SR(VID_CONV_COEF(1, 0)); + SR(VID_CONV_COEF(1, 1)); + SR(VID_CONV_COEF(1, 2)); + SR(VID_CONV_COEF(1, 3)); + SR(VID_CONV_COEF(1, 4)); + + SR(VID_FIR_COEF_V(1, 0)); + SR(VID_FIR_COEF_V(1, 1)); + SR(VID_FIR_COEF_V(1, 2)); + SR(VID_FIR_COEF_V(1, 3)); + SR(VID_FIR_COEF_V(1, 4)); + SR(VID_FIR_COEF_V(1, 5)); + SR(VID_FIR_COEF_V(1, 6)); + SR(VID_FIR_COEF_V(1, 7)); + + SR(VID_PRELOAD(1)); +} + +void dispc_restore_context(void) +{ + RR(SYSCONFIG); + RR(IRQENABLE); + /*RR(CONTROL);*/ + RR(CONFIG); + RR(DEFAULT_COLOR0); + RR(DEFAULT_COLOR1); + RR(TRANS_COLOR0); + RR(TRANS_COLOR1); + RR(LINE_NUMBER); + RR(TIMING_H); + RR(TIMING_V); + RR(POL_FREQ); + RR(DIVISOR); + RR(GLOBAL_ALPHA); + RR(SIZE_DIG); + RR(SIZE_LCD); + +#ifdef CONFIG_ARCH_OMAP4 + /* TODO: find out what needs to be saved/restored */ +#endif + + RR(GFX_BA0); + RR(GFX_BA1); + RR(GFX_POSITION); + RR(GFX_SIZE); + RR(GFX_ATTRIBUTES); + RR(GFX_FIFO_THRESHOLD); + RR(GFX_ROW_INC); + RR(GFX_PIXEL_INC); + RR(GFX_WINDOW_SKIP); + RR(GFX_TABLE_BA); + + RR(DATA_CYCLE1); + RR(DATA_CYCLE2); + RR(DATA_CYCLE3); + + RR(CPR_COEF_R); + RR(CPR_COEF_G); + RR(CPR_COEF_B); + + RR(GFX_PRELOAD); + + /* VID1 */ + RR(VID_BA0(0)); + RR(VID_BA1(0)); + RR(VID_POSITION(0)); + RR(VID_SIZE(0)); + RR(VID_ATTRIBUTES(0)); + RR(VID_FIFO_THRESHOLD(0)); + RR(VID_ROW_INC(0)); + RR(VID_PIXEL_INC(0)); + RR(VID_FIR(0)); + RR(VID_PICTURE_SIZE(0)); + RR(VID_ACCU0(0)); + RR(VID_ACCU1(0)); + + RR(VID_FIR_COEF_H(0, 0)); + RR(VID_FIR_COEF_H(0, 1)); + RR(VID_FIR_COEF_H(0, 2)); + RR(VID_FIR_COEF_H(0, 3)); + RR(VID_FIR_COEF_H(0, 4)); + RR(VID_FIR_COEF_H(0, 5)); + RR(VID_FIR_COEF_H(0, 6)); + RR(VID_FIR_COEF_H(0, 7)); + + RR(VID_FIR_COEF_HV(0, 0)); + RR(VID_FIR_COEF_HV(0, 1)); + RR(VID_FIR_COEF_HV(0, 2)); + RR(VID_FIR_COEF_HV(0, 3)); + RR(VID_FIR_COEF_HV(0, 4)); + RR(VID_FIR_COEF_HV(0, 5)); + RR(VID_FIR_COEF_HV(0, 6)); + RR(VID_FIR_COEF_HV(0, 7)); + + RR(VID_CONV_COEF(0, 0)); + RR(VID_CONV_COEF(0, 1)); + RR(VID_CONV_COEF(0, 2)); + RR(VID_CONV_COEF(0, 3)); + RR(VID_CONV_COEF(0, 4)); + + RR(VID_FIR_COEF_V(0, 0)); + RR(VID_FIR_COEF_V(0, 1)); + RR(VID_FIR_COEF_V(0, 2)); + RR(VID_FIR_COEF_V(0, 3)); + RR(VID_FIR_COEF_V(0, 4)); + RR(VID_FIR_COEF_V(0, 5)); + RR(VID_FIR_COEF_V(0, 6)); + RR(VID_FIR_COEF_V(0, 7)); + + RR(VID_PRELOAD(0)); + + /* VID2 */ + RR(VID_BA0(1)); + RR(VID_BA1(1)); + RR(VID_POSITION(1)); + RR(VID_SIZE(1)); + RR(VID_ATTRIBUTES(1)); + RR(VID_FIFO_THRESHOLD(1)); + RR(VID_ROW_INC(1)); + RR(VID_PIXEL_INC(1)); + RR(VID_FIR(1)); + RR(VID_PICTURE_SIZE(1)); + RR(VID_ACCU0(1)); + RR(VID_ACCU1(1)); + + RR(VID_FIR_COEF_H(1, 0)); + RR(VID_FIR_COEF_H(1, 1)); + RR(VID_FIR_COEF_H(1, 2)); + RR(VID_FIR_COEF_H(1, 3)); + RR(VID_FIR_COEF_H(1, 4)); + RR(VID_FIR_COEF_H(1, 5)); + RR(VID_FIR_COEF_H(1, 6)); + RR(VID_FIR_COEF_H(1, 7)); + + RR(VID_FIR_COEF_HV(1, 0)); + RR(VID_FIR_COEF_HV(1, 1)); + RR(VID_FIR_COEF_HV(1, 2)); + RR(VID_FIR_COEF_HV(1, 3)); + RR(VID_FIR_COEF_HV(1, 4)); + RR(VID_FIR_COEF_HV(1, 5)); + RR(VID_FIR_COEF_HV(1, 6)); + RR(VID_FIR_COEF_HV(1, 7)); + + RR(VID_CONV_COEF(1, 0)); + RR(VID_CONV_COEF(1, 1)); + RR(VID_CONV_COEF(1, 2)); + RR(VID_CONV_COEF(1, 3)); + RR(VID_CONV_COEF(1, 4)); + + RR(VID_FIR_COEF_V(1, 0)); + RR(VID_FIR_COEF_V(1, 1)); + RR(VID_FIR_COEF_V(1, 2)); + RR(VID_FIR_COEF_V(1, 3)); + RR(VID_FIR_COEF_V(1, 4)); + RR(VID_FIR_COEF_V(1, 5)); + RR(VID_FIR_COEF_V(1, 6)); + RR(VID_FIR_COEF_V(1, 7)); + + RR(VID_PRELOAD(1)); + + /* enable last, because LCD & DIGIT enable are here */ + RR(CONTROL); +} + +#undef SR +#undef RR + +static inline void enable_clocks(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +bool dispc_go_busy(enum omap_channel channel) +{ + int bit; +#ifdef CONFIG_ARCH_OMAP4 + if (channel != OMAP_DSS_CHANNEL_DIGIT) +#else + if (channel == OMAP_DSS_CHANNEL_LCD) +#endif + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + +#ifdef CONFIG_ARCH_OMAP4 + if (channel == OMAP_DSS_CHANNEL_LCD2) + return REG_GET(DISPC_CONTROL2, bit, bit) == 1; + else +#endif + return REG_GET(DISPC_CONTROL, bit, bit) == 1; +} + +void dispc_go(enum omap_channel channel) +{ + int bit; + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (channel != OMAP_DSS_CHANNEL_DIGIT) +#else + if (channel == OMAP_DSS_CHANNEL_LCD) +#endif + bit = 0; /* LCDENABLE */ + else + bit = 1; /* DIGITALENABLE */ + +#ifdef CONFIG_ARCH_OMAP4 + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, 1, 0, 0); + else + REG_FLD_MOD(DISPC_CONTROL, 1, 0, 0); + +#endif + if (REG_GET(DISPC_CONTROL, bit, bit) == 0) + goto end; + +#ifdef CONFIG_ARCH_OMAP4 + if (channel != OMAP_DSS_CHANNEL_DIGIT) +#else + if (channel == OMAP_DSS_CHANNEL_LCD) +#endif + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + +#ifndef CONFIG_ARCH_OMAP4 + if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { + DSSERR("GO bit not down for channel %d\n", channel); + goto end; + } + + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT"); + + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); +#else + if (channel == OMAP_DSS_CHANNEL_LCD2) { + if (REG_GET(DISPC_CONTROL2, bit, bit) == 1) { + DSSERR("GO bit not down for channel %d\n", channel); + goto end; + } + DSSDBG("GO LCD2\n"); + REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit); + } else { + if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { + DSSERR("GO bit not down for channel %d\n", channel); + goto end; + } + + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? + "LCD" : "DIGIT"); + + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); + } +#endif + +end: + enable_clocks(0); +} + +static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + +#ifdef CONFIG_ARCH_OMAP4 + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) +#endif + dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); +#ifdef CONFIG_ARCH_OMAP4 + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_H(0, reg), value); +#endif + +} + +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + +#ifdef CONFIG_ARCH_OMAP4 + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) +#endif + dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); +#ifdef CONFIG_ARCH_OMAP4 + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_HV(0, reg), value); +#endif +} + +static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + +#ifdef CONFIG_ARCH_OMAP4 + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) +#endif + + dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); +#ifdef CONFIG_ARCH_OMAP4 + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_V(0, reg), value); +#endif +} + +#ifdef CONFIG_ARCH_OMAP4 +static void _dispc_write_firh2_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_H2(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_H2(0, reg), value); + +} + +static void _dispc_write_firhv2_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_HV2(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_HV2(0, reg), value); +} + +static void _dispc_write_firv2_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_V2(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_V2(0, reg), value); +} +#endif + + +static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, + int vscaleup, int five_taps) +{ + /* Coefficients for horizontal up-sampling */ + static const u32 coef_hup[8] = { + 0x00800000, + 0x0D7CF800, + 0x1E70F5FF, + 0x335FF5FE, + 0xF74949F7, + 0xF55F33FB, + 0xF5701EFE, + 0xF87C0DFF, + }; + + /* Coefficients for horizontal down-sampling */ + static const u32 coef_hdown[8] = { + 0x24382400, + 0x28371FFE, + 0x2C361BFB, + 0x303516F9, + 0x11343311, + 0x1635300C, + 0x1B362C08, + 0x1F372804, + }; + + /* Coefficients for horizontal and vertical up-sampling */ + static const u32 coef_hvup[2][8] = { + { + 0x00800000, + 0x037B02FF, + 0x0C6F05FE, + 0x205907FB, + 0x00404000, + 0x075920FE, + 0x056F0CFF, + 0x027B0300, + }, + { + 0x00800000, + 0x0D7CF8FF, + 0x1E70F5FE, + 0x335FF5FB, + 0xF7404000, + 0xF55F33FE, + 0xF5701EFF, + 0xF87C0D00, + }, + }; + + /* Coefficients for horizontal and vertical down-sampling */ + static const u32 coef_hvdown[2][8] = { + { + 0x24382400, + 0x28391F04, + 0x2D381B08, + 0x3237170C, + 0x123737F7, + 0x173732F9, + 0x1B382DFB, + 0x1F3928FE, + }, + { + 0x24382400, + 0x28371F04, + 0x2C361B08, + 0x3035160C, + 0x113433F7, + 0x163530F9, + 0x1B362CFB, + 0x1F3728FE, + }, + }; + + /* Coefficients for vertical up-sampling */ + static const u32 coef_vup[8] = { + 0x00000000, + 0x0000FF00, + 0x0000FEFF, + 0x0000FBFE, + 0x000000F7, + 0x0000FEFB, + 0x0000FFFE, + 0x000000FF, + }; + + + /* Coefficients for vertical down-sampling */ + static const u32 coef_vdown[8] = { + 0x00000000, + 0x000004FE, + 0x000008FB, + 0x00000CF9, + 0x0000F711, + 0x0000F90C, + 0x0000FB08, + 0x0000FE04, + }; + + const u32 *h_coef; + const u32 *hv_coef; + const u32 *hv_coef_mod; + const u32 *v_coef; + int i; + + if (hscaleup) + h_coef = coef_hup; + else + h_coef = coef_hdown; + + if (vscaleup) { + hv_coef = coef_hvup[five_taps]; + v_coef = coef_vup; + + if (hscaleup) + hv_coef_mod = NULL; + else + hv_coef_mod = coef_hvdown[five_taps]; + } else { + hv_coef = coef_hvdown[five_taps]; + v_coef = coef_vdown; + + if (hscaleup) + hv_coef_mod = coef_hvup[five_taps]; + else + hv_coef_mod = NULL; + } + + for (i = 0; i < 8; i++) { + u32 h, hv; + + h = h_coef[i]; + + hv = hv_coef[i]; + + if (hv_coef_mod) { + hv &= 0xffffff00; + hv |= (hv_coef_mod[i] & 0xff); + } + + _dispc_write_firh_reg(plane, i, h); + _dispc_write_firhv_reg(plane, i, hv); + } + + if (!five_taps) + return; + + for (i = 0; i < 8; i++) { + u32 v; + v = v_coef[i]; + _dispc_write_firv_reg(plane, i, v); + } +} + +static void _dispc_setup_color_conv_coef(void) +{ + const struct color_conv_coef { + int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; + int full_range; + } ctbl_bt601_5 = { + 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, + }; + + const struct color_conv_coef *ct; + +#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) + + ct = &ctbl_bt601_5; + + dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb)); + + dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); + +#ifdef CONFIG_ARCH_OMAP4 + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 4), + CVAL(0, ct->bcb)); + + REG_FLD_MOD(DISPC_VID_V3_WB_ATTRIBUTES(0), ct->full_range, 11, 11); +#endif + +#undef CVAL + + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11); +} + + +static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, + DISPC_VID_BA0(0), + DISPC_VID_BA0(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_BA0(0) /* VID 3 pipeline*/ +#endif + }; + + dispc_write_reg(ba0_reg[plane], paddr); +} + +static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, + DISPC_VID_BA1(0), + DISPC_VID_BA1(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_BA1(0) /* VID 3 pipeline*/ +#endif + + }; + + dispc_write_reg(ba1_reg[plane], paddr); +} + +#ifdef CONFIG_ARCH_OMAP4 +static void _dispc_set_plane_ba_uv0(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba_uv0_reg[] = { DISPC_VID_BA_UV0(0), + DISPC_VID_BA_UV0(1), + DISPC_VID_BA_UV0(2) /* VID 3 pipeline*/ + }; + + u32 val = 0; + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(ba_uv0_reg[plane - 1], paddr); + /* plane - 1 => no UV_BA for GFX*/ + +} + +static void _dispc_set_plane_ba_uv1(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba_uv1_reg[] = { DISPC_VID_BA_UV1(0), + DISPC_VID_BA_UV1(1), + DISPC_VID_BA_UV1(2) + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(ba_uv1_reg[plane - 1], paddr); + /* plane - 1 => no UV_BA for GFX*/ +} + +#endif + +static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +{ + const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, + DISPC_VID_POSITION(0), + DISPC_VID_POSITION(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_VID3_POSITION /* VID 3 pipeline*/ +#endif + + }; + + u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); + dispc_write_reg(pos_reg[plane], val); +} + +static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) +{ + const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, + DISPC_VID_PICTURE_SIZE(0), + DISPC_VID_PICTURE_SIZE(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_PICTURE_SIZE(0) /* VID 3 pipeline*/ +#endif + + }; + u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(siz_reg[plane], val); +} + +static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) +{ + u32 val; + const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), + DISPC_VID_SIZE(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_SIZE(0) /* VID 3 pipeline*/ +#endif + + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + dispc_write_reg(vsi_reg[plane-1], val); +} + +static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) +{ +#ifndef CONFIG_ARCH_OMAP4 /* all pipelines have alpha coeff in OMAP4 */ + BUG_ON(plane == OMAP_DSS_VIDEO1); +#endif + if (plane == OMAP_DSS_GFX) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); + else if (plane == OMAP_DSS_VIDEO2) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16); +#ifdef CONFIG_ARCH_OMAP4 + else if (plane == OMAP_DSS_VIDEO1) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 15, 8); + else if (plane == OMAP_DSS_VIDEO3) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 31, 24); +#endif + +} + +static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) +{ + const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC, + DISPC_VID_PIXEL_INC(0), + DISPC_VID_PIXEL_INC(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_PIXEL_INC(0) /* VID 3 pipeline*/ +#endif + + }; + + dispc_write_reg(ri_reg[plane], inc); +} + +static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) +{ + const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, + DISPC_VID_ROW_INC(0), + DISPC_VID_ROW_INC(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_ROW_INC(0) /* VID 3 pipeline*/ +#endif + + }; + + dispc_write_reg(ri_reg[plane], inc); +} + +static void _dispc_set_color_mode(enum omap_plane plane, + enum omap_color_mode color_mode) +{ + u32 m = 0; + +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_GFX == plane) { +#endif + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + m = 0x0; break; + case OMAP_DSS_COLOR_CLUT2: + m = 0x1; break; + case OMAP_DSS_COLOR_CLUT4: + m = 0x2; break; + case OMAP_DSS_COLOR_CLUT8: + m = 0x3; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + + default: + BUG(); break; + } +#ifdef CONFIG_ARCH_OMAP4 + } else { + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + m = 0x0; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x1; break; + case OMAP_DSS_COLOR_RGBA12: + m = 0x2; break; + case OMAP_DSS_COLOR_XRGB12: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_ARGB16_1555: + m = 0x7; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xA; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xB; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xC; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xD; break; + case OMAP_DSS_COLOR_RGBX24_32_ALGN: + m = 0xE; break; + case OMAP_DSS_COLOR_XRGB15: + m = 0xF; break; + default: + BUG(); break; + } + } +#endif + REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); +} + +static void _dispc_set_channel_out(enum omap_plane plane, + enum omap_channel channel) +{ + int shift; + u32 val; + + switch (plane) { + case OMAP_DSS_GFX: + shift = 8; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: +#ifdef CONFIG_ARCH_OMAP4 + case OMAP_DSS_VIDEO3: /* VID 3 pipeline*/ +#endif + shift = 16; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, channel, shift, shift); + dispc_write_reg(dispc_reg_att[plane], val); +} + +void dispc_set_burst_size(enum omap_plane plane, + enum omap_burst_size burst_size) +{ + int shift; + u32 val; + + enable_clocks(1); + + switch (plane) { + case OMAP_DSS_GFX: + shift = 6; + break; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: +#ifdef CONFIG_ARCH_OMAP4 + case OMAP_DSS_VIDEO3: /* VID 3 pipeline*/ +#endif + shift = 14; + break; + default: + BUG(); + return; + } + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, burst_size, shift+1, shift); + dispc_write_reg(dispc_reg_att[plane], val); + + enable_clocks(0); +} + +#ifdef CONFIG_ARCH_OMAP4 +void dispc_set_zorder(enum omap_plane plane, + enum omap_overlay_zorder zorder) +{ + u32 val; + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, zorder, 27, 26); + dispc_write_reg(dispc_reg_att[plane], val); + +} +void dispc_enable_zorder(enum omap_plane plane, bool enable) +{ + u32 val; + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, enable, 25, 25); + dispc_write_reg(dispc_reg_att[plane], val); + +} +/* this routine is a collection of some fine tuned settings from SiVal test, needs to be revisited on SDC */ +void dispc_enable_preload(enum omap_plane plane, bool enable) +{ + u32 val; + int x, y; + + /* enable preload */ + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, enable, 19, 19); + dispc_write_reg(dispc_reg_att[plane], val); + + /* DMA preload values */ + dispc_write_reg(DISPC_VID_PRELOAD(0), 0x100); + + /* clk divisor for DISPC_CORE_CLK */ + x = 2; + y = 1; + val = FLD_VAL(x, 23, 16) | FLD_VAL(y, 0, 0); + dispc_write_reg(DISPC_DIVISOR, val); +} +void dispc_set_idle_mode(void) +{ + u32 l; + + l = dispc_read_reg(DISPC_SYSCONFIG); + l = FLD_MOD(l, 1, 13, 12); /* MIDLEMODE: smart standby */ + l = FLD_MOD(l, 1, 4, 3); /* SIDLEMODE: smart idle */ + l = FLD_MOD(l, 0, 2, 2); /* ENWAKEUP */ + l = FLD_MOD(l, 0, 0, 0); /* AUTOIDLE */ + dispc_write_reg(DISPC_SYSCONFIG, l); + +} +void dispc_enable_gamma_table(bool enable) +{ + REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9); +} + + +#endif + +static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) +{ + u32 val; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, enable, 9, 9); + dispc_write_reg(dispc_reg_att[plane], val); +} + +void dispc_enable_replication(enum omap_plane plane, bool enable) +{ + int bit; + + if (plane == OMAP_DSS_GFX) + bit = 5; + else + bit = 10; + + enable_clocks(1); + REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit); + enable_clocks(0); +} + +void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) + dispc_write_reg(DISPC_SIZE_LCD2, val); + else +#endif + dispc_write_reg(DISPC_SIZE_LCD, val); + enable_clocks(0); +} + +void dispc_set_digit_size(u16 width, u16 height) +{ + u32 val; + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + enable_clocks(1); + dispc_write_reg(DISPC_SIZE_DIG, val); + enable_clocks(0); +} + + +static void dispc_read_plane_fifo_sizes(void) +{ + const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, + DISPC_VID_FIFO_SIZE_STATUS(0), + DISPC_VID_FIFO_SIZE_STATUS(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_BUF_SIZE_STATUS(0) + /* VID 3 pipeline*/ +#endif + + }; + u32 size; + int plane; + + enable_clocks(1); + + for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { + if (cpu_is_omap24xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0); + else if (cpu_is_omap34xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0); + else if (cpu_is_omap44xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 15, 0); + else + BUG(); + dispc.fifo_size[plane] = size; + } + + enable_clocks(0); +} + +u32 dispc_get_plane_fifo_size(enum omap_plane plane) +{ + return dispc.fifo_size[plane]; +} + +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) +{ + const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, + DISPC_VID_FIFO_THRESHOLD(0), + DISPC_VID_FIFO_THRESHOLD(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_BUF_THRESHOLD(0) + /* VID 3 pipeline*/ +#endif + + }; + enable_clocks(1); + + DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", + plane, + REG_GET(ftrs_reg[plane], 11, 0), + REG_GET(ftrs_reg[plane], 27, 16), + low, high); + + if (cpu_is_omap24xx()) + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0)); + else if (cpu_is_omap34xx()) + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); + else /* cpu is omap44xx */ + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); + + enable_clocks(0); +} + +void dispc_enable_fifomerge(bool enable) +{ + enable_clocks(1); + + DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); + + enable_clocks(0); +} + +static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) +{ + u32 val; + const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), + DISPC_VID_FIR(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_FIR(0) + /* VID 3 pipeline*/ +#endif + + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + if (cpu_is_omap24xx()) + val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0); + else + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + dispc_write_reg(fir_reg[plane-1], val); +} + +static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), + DISPC_VID_ACCU0(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_ACCU0(0) + /* VID 3 pipeline*/ +#endif + + }; + + BUG_ON(plane == OMAP_DSS_GFX); + if (cpu_is_omap44xx()) + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + else + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac0_reg[plane-1], val); +} + +static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), + DISPC_VID_ACCU1(1) +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_VID_V3_WB_ACCU1(0) /* VID 3 pipeline*/ +#endif + + }; + + BUG_ON(plane == OMAP_DSS_GFX); + if (cpu_is_omap44xx()) + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + else + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac1_reg[plane-1], val); +} + +#ifdef CONFIG_ARCH_OMAP4 +static void _dispc_set_fir2(enum omap_plane plane, int hinc, int vinc) +{ + u32 val; + const struct dispc_reg fir_reg[] = { DISPC_VID_FIR2(0), + DISPC_VID_FIR2(1), + DISPC_VID_V3_WB_FIR2(0) + /* VID 3 pipeline*/ + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + + dispc_write_reg(fir_reg[plane-1], val); +} + +static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU2_0(0), + DISPC_VID_ACCU2_0(1), + DISPC_VID_V3_WB_ACCU2_0(0) + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + dispc_write_reg(ac0_reg[plane-1], val); +} + +static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU2_1(0), + DISPC_VID_ACCU2_1(1), + DISPC_VID_V3_WB_ACCU2_1(0) + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + dispc_write_reg(ac1_reg[plane-1], val); +} + +#endif + +static void _dispc_set_scaling(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode) +{ + int fir_hinc; + int fir_vinc; + int hscaleup, vscaleup; + int accu0 = 0; + int accu1 = 0; + u32 l; + + BUG_ON(plane == OMAP_DSS_GFX); + + hscaleup = orig_width <= out_width; + vscaleup = orig_height <= out_height; + + _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps); + + if (!orig_width || orig_width == out_width) + fir_hinc = 0; + else + fir_hinc = 1024 * orig_width / out_width; + + if (!orig_height || orig_height == out_height) + fir_vinc = 0; + else + fir_vinc = 1024 * orig_height / out_height; + + _dispc_set_fir(plane, fir_hinc, fir_vinc); + + l = dispc_read_reg(dispc_reg_att[plane]); + l &= ~((0x0f << 5) | (0x1 << 21)); + + l |= fir_hinc ? (1 << 5) : 0; + l |= fir_vinc ? (1 << 6) : 0; + + l |= hscaleup ? 0 : (1 << 7); + l |= vscaleup ? 0 : (1 << 8); + + l |= five_taps ? (1 << 21) : 0; + + dispc_write_reg(dispc_reg_att[plane], l); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + if (ilace && !fieldmode) { + accu1 = 0; + accu0 = (fir_vinc / 2) & 0x3ff; + if (accu0 >= 1024/2) { + accu1 = 1024/2; + accu0 -= accu1; + } + } + + _dispc_set_vid_accu0(plane, 0, accu0); + _dispc_set_vid_accu1(plane, 0, accu1); +} + +static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, + bool mirroring, enum omap_color_mode color_mode) +{ +#ifndef CONFIG_ARCH_OMAP4 + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) { + int vidrot = 0; + + if (mirroring) { + switch (rotation) { + case 0: + vidrot = 2; + break; + case 1: + vidrot = 1; + break; + case 2: + vidrot = 0; + break; + case 3: + vidrot = 3; + break; + } + } else { + switch (rotation) { + case 0: + vidrot = 0; + break; + case 1: + vidrot = 1; + break; + case 2: + vidrot = 2; + break; + case 3: + vidrot = 3; + break; + } + } + + REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); + + if (rotation == 1 || rotation == 3) + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18); + else + REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18); + } else { + REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12); + REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18); + } +#else + if (plane != OMAP_DSS_GFX) { + if (color_mode == OMAP_DSS_COLOR_NV12) { + /* DOUBLESTRIDE : 0 for 90-, 270-; 1 for 0- and 180- */ + if (rotation == 1 || rotation == 3) + REG_FLD_MOD(dispc_reg_att[plane], 0x0, 22, 22); + else + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 22, 22); + } + } + + /* Set the rotation value for pipeline */ +/* REG_FLD_MOD(dispc_reg_att[plane], rotation, 13, 12); */ +#endif +} + +static s32 pixinc(int pixels, u8 ps) +{ + if (pixels == 1) + return 1; + else if (pixels > 1) + return 1 + (pixels - 1) * ps; + else if (pixels < 0) + return 1 - (-pixels + 1) * ps; + else + BUG(); +} + +static void calc_tiler_row_rotation(u8 rotation, + u16 width, u16 height, + enum omap_color_mode color_mode, + s32 *row_inc) +{ + u8 ps = 1; + DSSDBG("calc_tiler_rot(%d): %dx%d\n", rotation, width, height); + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 2; + break; + + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + ps = 4; + break; + + case OMAP_DSS_COLOR_NV12: + ps = 1; + break; + + default: + BUG(); + return; + } + + switch (rotation) { + case 0: + case 2: + if (1 == ps) + *row_inc = 16384 + 1 - (width); + else + *row_inc = 32768 + 1 - (width * ps); + break; + + case 1: + case 3: + if (4 == ps) + *row_inc = 16384 + 1 - (width * ps); + else + *row_inc = 8192 + 1 - (width * ps); + break; + + default: + BUG(); + return; + } + + printk(KERN_INFO + " colormode: %d, rotation: %d, ps: %d, width: %d," + " height: %d, row_inc:%d\n", + color_mode, rotation, ps, width, height, *row_inc); + + return; +} + +static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, + u16 screen_width, + u16 width, u16 height, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, + unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc) +{ + u8 ps; + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + ps = 2; + break; + + case OMAP_DSS_COLOR_RGB24P: + ps = 3; + break; + + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 4; + break; + + default: + BUG(); + return; + } + + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, + width, height); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + switch (rotation + mirror * 4) { + case 0: + case 2: + /* + * If the pixel format is YUV or UYVY divide the width + * of the image by 2 for 0 and 180 degree rotation. + */ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + width = width >> 1; + case 1: + case 3: + *offset1 = 0; + if (field_offset) + *offset0 = field_offset * screen_width * ps; + else + *offset0 = 0; + + *row_inc = pixinc(1 + (screen_width - width) + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + case 4: + case 6: + /* If the pixel format is YUV or UYVY divide the width + * of the image by 2 for 0 degree and 180 degree + */ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + width = width >> 1; + case 5: + case 7: + *offset1 = 0; + if (field_offset) + *offset0 = field_offset * screen_width * ps; + else + *offset0 = 0; + *row_inc = pixinc(1 - (screen_width + width) - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + default: + BUG(); + } +} + +static void calc_dma_rotation_offset(u8 rotation, bool mirror, + u16 screen_width, + u16 width, u16 height, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, + unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc) +{ + u8 ps; + u16 fbw, fbh; + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + ps = 2; + break; + + case OMAP_DSS_COLOR_RGB24P: + ps = 3; + break; + + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + ps = 4; + break; + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 2; + break; + default: + BUG(); + return; + } + + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, + width, height); + + /* width & height are overlay sizes, convert to fb sizes */ + + if (rotation == 0 || rotation == 2) { + fbw = width; + fbh = height; + } else { + fbw = height; + fbh = width; + } + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + switch (rotation + mirror * 4) { + case 0: + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 + (screen_width - fbw) + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + case 1: + *offset1 = screen_width * (fbh - 1) * ps; + if (field_offset) + *offset0 = *offset1 + field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * (fbh - 1) + 1 + + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(-screen_width, ps); + break; + case 2: + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-1 - + (screen_width - fbw) - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(-1, ps); + break; + case 3: + *offset1 = (fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-screen_width * (fbh - 1) - 1 - + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(screen_width, ps); + break; + + /* mirroring */ + case 0 + 4: + *offset1 = (fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * 2 - 1 + + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(-1, ps); + break; + + case 1 + 4: + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(-screen_width * (fbh - 1) + 1 + + (fieldmode ? 1 : 0), + ps); + *pix_inc = pixinc(screen_width, ps); + break; + + case 2 + 4: + *offset1 = screen_width * (fbh - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 - screen_width * 2 - + (fieldmode ? screen_width : 0), + ps); + *pix_inc = pixinc(1, ps); + break; + + case 3 + 4: + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; + if (field_offset) + *offset0 = *offset1 - field_offset * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(screen_width * (fbh - 1) - 1 - + (fieldmode ? 1 : 0), + ps); + *pix_inc = pixinc(-screen_width, ps); + break; + + default: + BUG(); + } +} + +static unsigned long calc_fclk_five_taps(enum omap_channel channel, + u16 width, u16 height, u16 out_width, u16 out_height, + enum omap_color_mode color_mode) +{ + u32 fclk = 0; + /* FIXME venc pclk? */ + u64 tmp, pclk = dispc_pclk_rate(channel); + + if (height > out_height) { + /* FIXME get real display PPL */ + unsigned int ppl = 800; + + tmp = pclk * height * out_width; + do_div(tmp, 2 * out_height * ppl); + fclk = tmp; + + if (height > 2 * out_height && ppl != out_width) { + tmp = pclk * (height - 2 * out_height) * out_width; + do_div(tmp, 2 * out_height * (ppl - out_width)); + fclk = max(fclk, (u32) tmp); + } + } + + if (width > out_width) { + tmp = pclk * width; + do_div(tmp, out_width); + fclk = max(fclk, (u32) tmp); + + if (color_mode == OMAP_DSS_COLOR_RGB24U) + fclk <<= 1; + } + + return fclk; +} + +static unsigned long calc_fclk(enum omap_channel channel, u16 width, + u16 height, u16 out_width, u16 out_height) +{ + unsigned int hf, vf; + + /* + * FIXME how to determine the 'A' factor + * for the no downscaling case ? + */ + + if (width > 3 * out_width) + hf = 4; + else if (width > 2 * out_width) + hf = 3; + else if (width > out_width) + hf = 2; + else + hf = 1; + + if (height > out_height) + vf = 2; + else + vf = 1; + + /* FIXME venc pclk? */ + return dispc_pclk_rate(channel) * vf * hf; +} + +void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) +{ + enable_clocks(1); + _dispc_set_channel_out(plane, channel_out); + enable_clocks(0); +} + + + +static int _dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, int mirror, + u8 global_alpha, enum omap_channel channel +#ifdef CONFIG_ARCH_OMAP4 + , u32 puv_addr +#endif + ) +{ + const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; + bool five_taps = 0; + bool fieldmode = 0; + int cconv = 0; + unsigned offset0, offset1; + s32 row_inc; + s32 pix_inc; + u16 frame_height = height; + unsigned int field_offset = 0; + + u8 orientation = 0; + struct dmmViewOrientT orient; + + if (paddr == 0) + return -EINVAL; + + if (ilace && height == out_height) + fieldmode = 1; + + if (ilace) { + if (fieldmode) + height /= 2; + pos_y /= 2; + out_height /= 2; + + DSSDBG("adjusting for ilace: height %d, pos_y %d, " + "out_height %d\n", + height, pos_y, out_height); + } + + if (plane == OMAP_DSS_GFX) { + if (width != out_width || height != out_height) + return -EINVAL; + + switch (color_mode) { + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + break; + + default: + return -EINVAL; + } + } else { + /* video plane */ + + unsigned long fclk = 0; + + if (out_width < width / maxdownscale || + out_width > width * 8) + return -EINVAL; + + if (out_height < height / maxdownscale || + out_height > height * 8) + return -EINVAL; + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: +#ifndef CONFIG_ARCH_OMAP4 + case OMAP_DSS_COLOR_RGBX32: +#endif + break; + + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: +#ifdef CONFIG_ARCH_OMAP4 + case OMAP_DSS_COLOR_RGBA12: + case OMAP_DSS_COLOR_XRGB12: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_RGBX24_32_ALGN: + case OMAP_DSS_COLOR_XRGB15: +#else + if (plane == OMAP_DSS_VIDEO1) + return -EINVAL; +#endif + break; + +#ifdef CONFIG_ARCH_OMAP4 + case OMAP_DSS_COLOR_NV12: +#endif + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + cconv = 1; + break; + + default: + return -EINVAL; + } + + /* Must use 5-tap filter? */ + five_taps = height > out_height * 2; + + if (!five_taps) { + fclk = calc_fclk(channel, width, height, + out_width, out_height); + + /* Try 5-tap filter if 3-tap fclk is too high */ + if (cpu_is_omap34xx() && height > out_height && + fclk > dispc_fclk_rate()) + five_taps = true; + } + + if (width > (2048 >> five_taps)) + return -EINVAL; + + if (five_taps) + fclk = calc_fclk_five_taps(channel, width, height, + out_width, out_height, color_mode); + + DSSDBG("required fclk rate = %lu Hz\n", fclk); + DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); + + if (fclk > dispc_fclk_rate()) + return -EINVAL; + } + + if (ilace && !fieldmode) { + /* + * when downscaling the bottom field may have to start several + * source lines below the top field. Unfortunately ACCUI + * registers will only hold the fractional part of the offset + * so the integer part must be added to the base address of the + * bottom field. + */ + if (!height || height == out_height) + field_offset = 0; + else + field_offset = height / out_height / 2; + } + + /* Fields are independent but interleaved in memory. */ + if (fieldmode) + field_offset = 1; +#ifdef CONFIG_ARCH_OMAP4 /*TODO: OMAP4: check ?! */ + pix_inc = 0x1; + offset0 = 0x0; + offset1 = 0x0; + /* check if tiler address; else set row_inc = 1*/ + if ((paddr >= 0x60000000) && (paddr <= 0x7fffffff)) { + calc_tiler_row_rotation(rotation, width, frame_height, + color_mode, &row_inc); + + orientation = calc_tiler_orientation(rotation, (u8)mirror); +#if 1 + paddr &= ~(0x7 << 29); + paddr |= (orientation << 29); + if (puv_addr) { + puv_addr &= ~(0x7 << 29); + puv_addr |= (orientation << 29); + } +#else + orient.dmm90Rotate = ((orientation & 0x04) ? 1 : 0); + orient.dmmYInvert = ((orientation & 0x02) ? 1 : 0); + orient.dmmXInvert = ((orientation & 0x01) ? 1 : 0); + + paddr = tiler_get_tiler_address(paddr, orient, width, + frame_height, 0, 0); + + if (puv_addr) + puv_addr = tiler_get_tiler_address(puv_addr, + orient, width, frame_height, + 1, 0); +#endif + + + printk(KERN_INFO + "rotated addresses: 0x%0x, 0x%0x\n", + paddr, puv_addr); + /* set BURSTTYPE if rotation is non-zero */ + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 29, 29); + + } else + row_inc = 0x1; + +#else + if (rotation_type == OMAP_DSS_ROT_DMA) + calc_dma_rotation_offset(rotation, mirror, + screen_width, width, frame_height, color_mode, + fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc); + else + calc_vrfb_rotation_offset(rotation, mirror, + screen_width, width, frame_height, color_mode, + fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc); +#endif + DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", + offset0, offset1, row_inc, pix_inc); + + _dispc_set_color_mode(plane, color_mode); + + _dispc_set_plane_ba0(plane, paddr + offset0); + _dispc_set_plane_ba1(plane, paddr + offset1); +#ifdef CONFIG_ARCH_OMAP4 + /* TODO: check the offset calculations? */ + if (OMAP_DSS_COLOR_NV12 == color_mode) { + _dispc_set_plane_ba_uv0(plane, puv_addr + offset0); + _dispc_set_plane_ba_uv1(plane, puv_addr + offset1); + } +#endif + _dispc_set_row_inc(plane, row_inc); + _dispc_set_pix_inc(plane, pix_inc); + + DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height, + out_width, out_height); + + _dispc_set_plane_pos(plane, pos_x, pos_y); + + _dispc_set_pic_size(plane, width, height); + + if (plane != OMAP_DSS_GFX) { + _dispc_set_scaling(plane, width, height, + out_width, out_height, + ilace, five_taps, fieldmode); + _dispc_set_vid_size(plane, out_width, out_height); + _dispc_set_vid_color_conv(plane, cconv); + } + + _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode); + +#ifndef CONFIG_ARCH_OMAP4 + if (plane != OMAP_DSS_VIDEO1) +#endif + _dispc_setup_global_alpha(plane, global_alpha); + + pix_inc = dispc_read_reg(dispc_reg_att[plane]); + DSSDBG("vid[%d] attributes = %x\n", plane, pix_inc); + + return 0; +} + +static void _dispc_enable_plane(enum omap_plane plane, bool enable) +{ + REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); +} + +static void dispc_disable_isr(void *data, u32 mask) +{ + struct completion *compl = data; + complete(compl); +} + +static void _enable_lcd_out(enum omap_channel channel, bool enable) +{ +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) + REG_FLD_MOD(DISPC_CONTROL2, enable ? 1 : 0, 0, 0); + else +#endif + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); +} + +void dispc_enable_lcd_out(enum omap_channel channel, bool enable) +{ + struct completion frame_done_completion; + bool is_on; + int r; + int irq; + + enable_clocks(1); + + /* When we disable LCD output, we need to wait until frame is done. + * Otherwise the DSS is still working, and turning off the clocks + * prevents DSS from going to OFF mode */ +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) { + is_on = REG_GET(DISPC_CONTROL2, 0, 0); + irq = DISPC_IRQ_FRAMEDONE2; + } else +#endif + { + is_on = REG_GET(DISPC_CONTROL, 0, 0); + irq = DISPC_IRQ_FRAMEDONE; + } + + if (!enable && is_on) { + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, + &frame_done_completion, + irq); + + if (r) + DSSERR("failed to register FRAMEDONE isr\n"); + } + + _enable_lcd_out(channel, enable); + + if (!enable && is_on) { + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for FRAME DONE\n"); + + r = omap_dispc_unregister_isr(dispc_disable_isr, + &frame_done_completion, + irq); + + if (r) + DSSERR("failed to unregister FRAMEDONE isr\n"); + } + + enable_clocks(0); +} + +static void _enable_digit_out(bool enable) +{ + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1); +} + +void dispc_enable_digit_out(bool enable) +{ + struct completion frame_done_completion; + int r; + + enable_clocks(1); + + if (REG_GET(DISPC_CONTROL, 1, 1) == enable) { + enable_clocks(0); + return; + } + + if (enable) { + unsigned long flags; + /* When we enable digit output, we'll get an extra digit + * sync lost interrupt, that we need to ignore */ + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); + } + + /* When we disable digit output, we need to wait until fields are done. + * Otherwise the DSS is still working, and turning off the clocks + * prevents DSS from going to OFF mode. And when enabling, we need to + * wait for the extra sync losts */ + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion, + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + if (r) + DSSERR("failed to register EVSYNC isr\n"); + + _enable_digit_out(enable); + + /* XXX I understand from TRM that we should only wait for the + * current field to complete. But it seems we have to wait + * for both fields */ + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for EVSYNC\n"); + + if (!wait_for_completion_timeout(&frame_done_completion, + msecs_to_jiffies(100))) + DSSERR("timeout waiting for EVSYNC\n"); + + r = omap_dispc_unregister_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); + if (r) + DSSERR("failed to unregister EVSYNC isr\n"); + + if (enable) { + unsigned long flags; + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); + } + + enable_clocks(0); +} + +void dispc_lcd_enable_signal_polarity(bool act_high) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); + enable_clocks(0); +} + +void dispc_lcd_enable_signal(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); + enable_clocks(0); +} + +void dispc_pck_free_enable(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); + enable_clocks(0); +} + +void dispc_enable_fifohandcheck(bool enable) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); + enable_clocks(0); +} + + +void dispc_set_lcd_display_type(enum omap_channel channel, + enum omap_lcd_display_type type) +{ + int mode; + + switch (type) { + case OMAP_DSS_LCD_DISPLAY_STN: + mode = 0; + break; + + case OMAP_DSS_LCD_DISPLAY_TFT: + mode = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) + REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3); + else +#endif + REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); + enable_clocks(0); +} + +void dispc_set_loadmode(enum omap_dss_load_mode mode) +{ + enable_clocks(1); + REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); + enable_clocks(0); +} + + +void dispc_set_default_color(enum omap_channel channel, u32 color) +{ + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, + DISPC_DEFAULT_COLOR1 +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_DEFAULT_COLOR2 +#endif + }; + + enable_clocks(1); + dispc_write_reg(def_reg[channel], color); + enable_clocks(0); +} + +u32 dispc_get_default_color(enum omap_channel channel) +{ + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, + DISPC_DEFAULT_COLOR1 +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_DEFAULT_COLOR2 +#endif +}; + + u32 l; + +#ifndef CONFIG_ARCH_OMAP4 + BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && + channel != OMAP_DSS_CHANNEL_LCD); +#endif + + enable_clocks(1); + l = dispc_read_reg(def_reg[channel]); + enable_clocks(0); + + return l; +} + +void dispc_set_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key) +{ + const struct dispc_reg tr_reg[] = { + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_TRANS_COLOR2 +#endif + + }; + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (ch == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11); + else +#endif + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, type, 13, 13); + + dispc_write_reg(tr_reg[ch], trans_key); + enable_clocks(0); +} + +void dispc_get_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type *type, + u32 *trans_key) +{ + const struct dispc_reg tr_reg[] = { + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 +#ifdef CONFIG_ARCH_OMAP4 + , DISPC_TRANS_COLOR2 +#endif + + }; + + enable_clocks(1); + if (type) { +#ifdef CONFIG_ARCH_OMAP4 + if (ch == OMAP_DSS_CHANNEL_LCD2) + *type = REG_GET(DISPC_CONFIG2, 11, 11); + else +#endif + if (ch == OMAP_DSS_CHANNEL_LCD) + *type = REG_GET(DISPC_CONFIG, 11, 11); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + *type = REG_GET(DISPC_CONFIG, 13, 13); + else + BUG(); + } + + if (trans_key) + *trans_key = dispc_read_reg(tr_reg[ch]); + enable_clocks(0); +} + +void dispc_enable_trans_key(enum omap_channel ch, bool enable) +{ + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (ch == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10); + else +#endif + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); + enable_clocks(0); +} +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) +{ + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); + else /* OMAP_DSS_CHANNEL_DIGIT */ + REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); + enable_clocks(0); +} +bool dispc_alpha_blending_enabled(enum omap_channel ch) +{ + bool enabled; +#ifdef CONFIG_ARCH_OMAP4 + BUG_ON(ch == OMAP_DSS_CHANNEL_LCD2); +#endif + enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD) + enabled = REG_GET(DISPC_CONFIG, 18, 18); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + enabled = REG_GET(DISPC_CONFIG, 18, 18); + else + BUG(); + enable_clocks(0); + + return enabled; + +} + + +bool dispc_trans_key_enabled(enum omap_channel ch) +{ + bool enabled; + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (ch == OMAP_DSS_CHANNEL_LCD2) + enabled = REG_GET(DISPC_CONFIG2, 10, 10); + else +#endif + if (ch == OMAP_DSS_CHANNEL_LCD) + enabled = REG_GET(DISPC_CONFIG, 10, 10); + else if (ch == OMAP_DSS_CHANNEL_DIGIT) + enabled = REG_GET(DISPC_CONFIG, 12, 12); + else + BUG(); + enable_clocks(0); + + return enabled; +} + + +void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines) +{ + int code; + + switch (data_lines) { + case 12: + code = 0; + break; + case 16: + code = 1; + break; + case 18: + code = 2; + break; + case 24: + code = 3; + break; + default: + BUG(); + return; + } + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8); + else +#endif + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); + enable_clocks(0); +} + +void dispc_set_parallel_interface_mode(enum omap_channel channel, + enum omap_parallel_interface_mode mode) +{ + u32 l; + int stallmode; + int gpout0 = 1; + int gpout1; + + switch (mode) { + case OMAP_DSS_PARALLELMODE_BYPASS: + stallmode = 0; + gpout1 = 1; + break; + + case OMAP_DSS_PARALLELMODE_RFBI: + stallmode = 1; + gpout1 = 0; + break; + + case OMAP_DSS_PARALLELMODE_DSI: + stallmode = 1; + gpout1 = 1; + break; + + default: + BUG(); + return; + } + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) { + l = dispc_read_reg(DISPC_CONTROL2); + + printk(KERN_INFO "OMAP DISPCONTROL read (stallmode)%d\n", + FLD_GET(l, 11, 11)); + + l = FLD_MOD(l, stallmode, 11, 11); + + dispc_write_reg(DISPC_CONTROL2, l); + + } else +#endif + { + l = dispc_read_reg(DISPC_CONTROL); + + printk(KERN_INFO "OMAP DISPCONTROL read (stallmode)%d\n", + FLD_GET(l, 11, 11)); + printk(KERN_INFO "OMAP DISPCONTROL read (gpout)%d\n", + FLD_GET(l, 15, 15)); + printk(KERN_INFO "OMAP DISPCONTROL read (stallmode)%d\n", + FLD_GET(l, 16, 16)); + + + l = FLD_MOD(l, stallmode, 11, 11); + l = FLD_MOD(l, gpout0, 15, 15); + l = FLD_MOD(l, gpout1, 16, 16); + + dispc_write_reg(DISPC_CONTROL, l); + } + enable_clocks(0); +} + +static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, + int vsw, int vfp, int vbp) +{ + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { + if (hsw < 1 || hsw > 64 || + hfp < 1 || hfp > 256 || + hbp < 1 || hbp > 256 || + vsw < 1 || vsw > 64 || + vfp < 0 || vfp > 255 || + vbp < 0 || vbp > 255) + return false; + } else { + if (hsw < 1 || hsw > 256 || + hfp < 1 || hfp > 4096 || + hbp < 1 || hbp > 4096 || + vsw < 1 || vsw > 256 || + vfp < 0 || vfp > 4095 || + vbp < 0 || vbp > 4095) + return false; + } + + return true; +} + +bool dispc_lcd_timings_ok(struct omap_video_timings *timings) +{ + + return _dispc_lcd_timings_ok(timings->hsw, timings->hfp, + timings->hbp, timings->vsw, + timings->vfp, timings->vbp); +} + +static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw, + int hfp, int hbp, int vsw, int vfp, int vbp) +{ + u32 timing_h, timing_v; + + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { + timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) | + FLD_VAL(hbp-1, 27, 20); + + timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) | + FLD_VAL(vbp, 27, 20); + } else { + + timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) | + FLD_VAL(hbp-1, 31, 20); + + timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) | + FLD_VAL(vbp, 31, 20); + } + enable_clocks(1); + +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) { + dispc_write_reg(DISPC_TIMING_H2, timing_h); + dispc_write_reg(DISPC_TIMING_V2, timing_v); + } else +#endif + { + dispc_write_reg(DISPC_TIMING_H, timing_h); + dispc_write_reg(DISPC_TIMING_V, timing_v); + } + enable_clocks(0); +} + + +void dispc_set_lcd_timings(enum omap_channel channel, + struct omap_video_timings *timings) +{ + unsigned xtot, ytot; + unsigned long ht, vt; + + if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp, + timings->hbp, timings->vsw, + timings->vfp, timings->vbp)) + BUG(); + + _dispc_set_lcd_timings(channel, timings->hsw, timings->hfp, + timings->hbp, timings->vsw, timings->vfp, + timings->vbp); + + dispc_set_lcd_size(channel, timings->x_res, timings->y_res); + + xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; + ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; + + ht = (timings->pixel_clock * 1000) / xtot; + vt = (timings->pixel_clock * 1000) / xtot / ytot; + + DSSDBG("channel %u xres %u yres %u\n", channel, timings->x_res, + timings->y_res); + DSSDBG("pck %u\n", timings->pixel_clock); + DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", + timings->hsw, timings->hfp, timings->hbp, + timings->vsw, timings->vfp, timings->vbp); + + DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); +} + +void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div, + u16 pck_div) +{ + BUG_ON(lck_div < 1); + BUG_ON(pck_div < 2); + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) + dispc_write_reg(DISPC_DIVISOR2, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); + else + dispc_write_reg(DISPC_DIVISOR1, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); +#else + dispc_write_reg(DISPC_DIVISOR, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); +#endif + enable_clocks(0); +} + +static void dispc_get_lcd_divisor(enum omap_channel channel, + int *lck_div, int *pck_div) +{ + u32 l; +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) + l = dispc_read_reg(DISPC_DIVISOR2); + else + l = dispc_read_reg(DISPC_DIVISOR1); +#else + l = dispc_read_reg(DISPC_DIVISOR); +#endif + *lck_div = FLD_GET(l, 23, 16); + *pck_div = FLD_GET(l, 7, 0); +} + +/* TODO: Check with Senthil on handling of clocks */ +unsigned long dispc_fclk_rate(void) +{ + unsigned long r = 0; + + if (dss_get_dispc_clk_source() == 0) + r = dss_clk_get_rate(DSS_CLK_FCK1); + else +#ifdef CONFIG_OMAP2_DSS_DSI + r = dsi_get_dsi1_pll_rate(); +#else + BUG(); +#endif + return r; +} + +unsigned long dispc_lclk_rate(enum omap_channel channel) +{ + int lcd; + unsigned long r; + u32 l; +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) + l = dispc_read_reg(DISPC_DIVISOR2); + else + l = dispc_read_reg(DISPC_DIVISOR1); +#else + l = dispc_read_reg(DISPC_DIVISOR); +#endif + lcd = FLD_GET(l, 23, 16); + + r = dispc_fclk_rate(); + + return r / lcd; +} + +unsigned long dispc_pclk_rate(enum omap_channel channel) +{ + int lcd, pcd; + unsigned long r; + u32 l; +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2 == channel) + l = dispc_read_reg(DISPC_DIVISOR2); + else + l = dispc_read_reg(DISPC_DIVISOR1); +#else + l = dispc_read_reg(DISPC_DIVISOR); +#endif + lcd = FLD_GET(l, 23, 16); + pcd = FLD_GET(l, 7, 0); + + r = dispc_fclk_rate(); + + return r / lcd / pcd; +} + +void dispc_dump_clocks(struct seq_file *s) +{ + int lcd, pcd; + + enable_clocks(1); + + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd); + + seq_printf(s, "- dispc -\n"); + + seq_printf(s, "dispc fclk source = %s\n", + dss_get_dispc_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi1_pll_fclk"); + + seq_printf(s, "pixel clk = %lu / %d / %d = %lu\n", + dispc_fclk_rate(), + lcd, pcd, + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD)); + +#ifdef CONFIG_ARCH_OMAP4 + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd); + + seq_printf(s, "- dispc -\n"); + + seq_printf(s, "dispc fclk source = %s\n", + dss_get_dispc_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi1_pll_fclk"); + + seq_printf(s, "pixel clk = %lu / %d / %d = %lu\n", + dispc_fclk_rate(), + lcd, pcd, + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2)); + +#endif + + enable_clocks(0); +} + +void dispc_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DISPC_REVISION); + DUMPREG(DISPC_SYSCONFIG); + DUMPREG(DISPC_SYSSTATUS); + DUMPREG(DISPC_IRQSTATUS); + DUMPREG(DISPC_IRQENABLE); + DUMPREG(DISPC_CONTROL); + DUMPREG(DISPC_CONFIG); + DUMPREG(DISPC_CAPABLE); + DUMPREG(DISPC_DEFAULT_COLOR0); + DUMPREG(DISPC_DEFAULT_COLOR1); + DUMPREG(DISPC_TRANS_COLOR0); + DUMPREG(DISPC_TRANS_COLOR1); + DUMPREG(DISPC_LINE_STATUS); + DUMPREG(DISPC_LINE_NUMBER); + DUMPREG(DISPC_TIMING_H); + DUMPREG(DISPC_TIMING_V); + DUMPREG(DISPC_POL_FREQ); +#ifdef CONFIG_ARCH_OMAP4 + DUMPREG(DISPC_DIVISOR1); +#else + DUMPREG(DISPC_DIVISOR); +#endif + DUMPREG(DISPC_GLOBAL_ALPHA); + DUMPREG(DISPC_SIZE_DIG); + DUMPREG(DISPC_SIZE_LCD); + + DUMPREG(DISPC_GFX_BA0); + DUMPREG(DISPC_GFX_BA1); + DUMPREG(DISPC_GFX_POSITION); + DUMPREG(DISPC_GFX_SIZE); + DUMPREG(DISPC_GFX_ATTRIBUTES); + DUMPREG(DISPC_GFX_FIFO_THRESHOLD); + DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS); + DUMPREG(DISPC_GFX_ROW_INC); + DUMPREG(DISPC_GFX_PIXEL_INC); + DUMPREG(DISPC_GFX_WINDOW_SKIP); + DUMPREG(DISPC_GFX_TABLE_BA); + + DUMPREG(DISPC_DATA_CYCLE1); + DUMPREG(DISPC_DATA_CYCLE2); + DUMPREG(DISPC_DATA_CYCLE3); + + DUMPREG(DISPC_CPR_COEF_R); + DUMPREG(DISPC_CPR_COEF_G); + DUMPREG(DISPC_CPR_COEF_B); + + DUMPREG(DISPC_GFX_PRELOAD); + + DUMPREG(DISPC_VID_BA0(0)); + DUMPREG(DISPC_VID_BA1(0)); + DUMPREG(DISPC_VID_POSITION(0)); + DUMPREG(DISPC_VID_SIZE(0)); + DUMPREG(DISPC_VID_ATTRIBUTES(0)); + DUMPREG(DISPC_VID_FIFO_THRESHOLD(0)); + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0)); + DUMPREG(DISPC_VID_ROW_INC(0)); + DUMPREG(DISPC_VID_PIXEL_INC(0)); + DUMPREG(DISPC_VID_FIR(0)); + DUMPREG(DISPC_VID_PICTURE_SIZE(0)); + DUMPREG(DISPC_VID_ACCU0(0)); + DUMPREG(DISPC_VID_ACCU1(0)); + + DUMPREG(DISPC_VID_BA0(1)); + DUMPREG(DISPC_VID_BA1(1)); + DUMPREG(DISPC_VID_POSITION(1)); + DUMPREG(DISPC_VID_SIZE(1)); + DUMPREG(DISPC_VID_ATTRIBUTES(1)); + DUMPREG(DISPC_VID_FIFO_THRESHOLD(1)); + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1)); + DUMPREG(DISPC_VID_ROW_INC(1)); + DUMPREG(DISPC_VID_PIXEL_INC(1)); + DUMPREG(DISPC_VID_FIR(1)); + DUMPREG(DISPC_VID_PICTURE_SIZE(1)); + DUMPREG(DISPC_VID_ACCU0(1)); + DUMPREG(DISPC_VID_ACCU1(1)); + + DUMPREG(DISPC_VID_FIR_COEF_H(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_H(0, 7)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7)); + DUMPREG(DISPC_VID_CONV_COEF(0, 0)); + DUMPREG(DISPC_VID_CONV_COEF(0, 1)); + DUMPREG(DISPC_VID_CONV_COEF(0, 2)); + DUMPREG(DISPC_VID_CONV_COEF(0, 3)); + DUMPREG(DISPC_VID_CONV_COEF(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 0)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 1)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 2)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 3)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 5)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 6)); + DUMPREG(DISPC_VID_FIR_COEF_V(0, 7)); + + DUMPREG(DISPC_VID_FIR_COEF_H(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_H(1, 7)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7)); + DUMPREG(DISPC_VID_CONV_COEF(1, 0)); + DUMPREG(DISPC_VID_CONV_COEF(1, 1)); + DUMPREG(DISPC_VID_CONV_COEF(1, 2)); + DUMPREG(DISPC_VID_CONV_COEF(1, 3)); + DUMPREG(DISPC_VID_CONV_COEF(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 0)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 1)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 2)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 3)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 4)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 5)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 6)); + DUMPREG(DISPC_VID_FIR_COEF_V(1, 7)); + + DUMPREG(DISPC_VID_PRELOAD(0)); + DUMPREG(DISPC_VID_PRELOAD(1)); +/* TODO: OMAP4: add new registers here */ + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, + bool rf, bool ieo, bool ipc, bool ihs, + bool ivs, u8 acbi, u8 acb) +{ + u32 l = 0; + + DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n", + onoff, rf, ieo, ipc, ihs, ivs, acbi, acb); + + l |= FLD_VAL(onoff, 17, 17); + l |= FLD_VAL(rf, 16, 16); + l |= FLD_VAL(ieo, 15, 15); + l |= FLD_VAL(ipc, 14, 14); + l |= FLD_VAL(ihs, 13, 13); + l |= FLD_VAL(ivs, 12, 12); + l |= FLD_VAL(acbi, 11, 8); + l |= FLD_VAL(acb, 7, 0); + + enable_clocks(1); +#ifdef CONFIG_ARCH_OMAP4 + if (OMAP_DSS_CHANNEL_LCD2) + dispc_write_reg(DISPC_POL_FREQ2, l); + else +#endif + dispc_write_reg(DISPC_POL_FREQ, l); + enable_clocks(0); +} + +void dispc_set_pol_freq(enum omap_channel ch, enum omap_panel_config config, + u8 acbi, u8 acb) +{ + _dispc_set_pol_freq(ch, (config & OMAP_DSS_LCD_ONOFF) != 0, + (config & OMAP_DSS_LCD_RF) != 0, + (config & OMAP_DSS_LCD_IEO) != 0, + (config & OMAP_DSS_LCD_IPC) != 0, + (config & OMAP_DSS_LCD_IHS) != 0, + (config & OMAP_DSS_LCD_IVS) != 0, + acbi, acb); +} + +void find_lck_pck_divs(bool is_tft, unsigned long req_pck, unsigned long fck, + u16 *lck_div, u16 *pck_div) +{ + u16 pcd_min = is_tft ? 2 : 3; + unsigned long best_pck; + u16 best_ld, cur_ld; + u16 best_pd, cur_pd; + + best_pck = 0; + best_ld = 0; + best_pd = 0; + + for (cur_ld = 1; cur_ld <= 255; ++cur_ld) { + unsigned long lck = fck / cur_ld; + + for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) { + unsigned long pck = lck / cur_pd; + long old_delta = abs(best_pck - req_pck); + long new_delta = abs(pck - req_pck); + + if (best_pck == 0 || new_delta < old_delta) { + best_pck = pck; + best_ld = cur_ld; + best_pd = cur_pd; + + if (pck == req_pck) + goto found; + } + + if (pck < req_pck) + break; + } + + if (lck / pcd_min < req_pck) + break; + } + +found: + *lck_div = best_ld; + *pck_div = best_pd; +} + +int dispc_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dispc_clock_info *cinfo) +{ + unsigned long prate; + struct dispc_clock_info cur, best; + int match = 0; + int min_fck_per_pck; + unsigned long fck_rate = dss_clk_get_rate(DSS_CLK_FCK1); + + if (cpu_is_omap34xx()) + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + else + prate = 0; + + if (req_pck == dispc.cache_req_pck && + ((cpu_is_omap34xx() && prate == dispc.cache_prate) || + dispc.cache_cinfo.fck == fck_rate)) { + DSSDBG("dispc clock info found from cache.\n"); + *cinfo = dispc.cache_cinfo; + return 0; + } + + min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; + + if (min_fck_per_pck && + req_pck * min_fck_per_pck > DISPC_MAX_FCK) { + DSSERR("Requested pixel clock not possible with the current " + "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " + "the constraint off.\n"); + min_fck_per_pck = 0; + } + +retry: + memset(&cur, 0, sizeof(cur)); + memset(&best, 0, sizeof(best)); + + if (cpu_is_omap24xx()) { + /* XXX can we change the clock on omap2? */ + cur.fck = dss_clk_get_rate(DSS_CLK_FCK1); + cur.fck_div = 1; + + match = 1; + + find_lck_pck_divs(is_tft, req_pck, cur.fck, + &cur.lck_div, &cur.pck_div); + + cur.lck = cur.fck / cur.lck_div; + cur.pck = cur.lck / cur.pck_div; + + best = cur; + + goto found; + } else if (cpu_is_omap34xx()) { + for (cur.fck_div = 16; cur.fck_div > 0; --cur.fck_div) { + cur.fck = prate / cur.fck_div * 2; + + if (cur.fck > DISPC_MAX_FCK) + continue; + + if (min_fck_per_pck && + cur.fck < req_pck * min_fck_per_pck) + continue; + + match = 1; + + find_lck_pck_divs(is_tft, req_pck, cur.fck, + &cur.lck_div, &cur.pck_div); + + cur.lck = cur.fck / cur.lck_div; + cur.pck = cur.lck / cur.pck_div; + + if (abs(cur.pck - req_pck) < abs(best.pck - req_pck)) { + best = cur; + + if (cur.pck == req_pck) + goto found; + } + } + } else if (cpu_is_omap44xx()) { + for (cur.fck_div = 16; cur.fck_div > 0; --cur.fck_div) { + cur.fck = prate / cur.fck_div * 2; + + if (cur.fck > DISPC_MAX_FCK) + continue; + + if (min_fck_per_pck && + cur.fck < req_pck * min_fck_per_pck) + continue; + + match = 1; + + find_lck_pck_divs(is_tft, req_pck, cur.fck, + &cur.lck_div, &cur.pck_div); + + cur.lck = cur.fck / cur.lck_div; + cur.pck = cur.lck / cur.pck_div; + + if (abs(cur.pck - req_pck) < abs(best.pck - req_pck)) { + best = cur; + + if (cur.pck == req_pck) + goto found; + } + } + } else { + BUG(); + } + +found: + if (!match) { + if (min_fck_per_pck) { + DSSERR("Could not find suitable clock settings.\n" + "Turning FCK/PCK constraint off and" + "trying again.\n"); + min_fck_per_pck = 0; + goto retry; + } + + DSSERR("Could not find suitable clock settings.\n"); + + return -EINVAL; + } + + if (cinfo) + *cinfo = best; + + dispc.cache_req_pck = req_pck; + dispc.cache_prate = prate; + dispc.cache_cinfo = best; + + return 0; +} + +int dispc_set_clock_div(struct dispc_clock_info *cinfo) +{ + unsigned long prate; + int r; + + if (cpu_is_omap34xx()) { + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + DSSDBG("dpll4_m4 = %ld\n", prate); + } + + DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div); + DSSDBG("lck = %ld (%d)\n", cinfo->lck, cinfo->lck_div); + DSSDBG("pck = %ld (%d)\n", cinfo->pck, cinfo->pck_div); + + if (cpu_is_omap34xx()) { + r = clk_set_rate(dispc.dpll4_m4_ck, prate / cinfo->fck_div); + if (r) + return r; + } + + /* TODO: update here for LCD2 support */ + dispc_set_lcd_divisor(OMAP_DSS_CHANNEL_LCD, cinfo->lck_div, + cinfo->pck_div); + + return 0; +} + +int dispc_get_clock_div(struct dispc_clock_info *cinfo) +{ + cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1); + + if (cpu_is_omap34xx()) { + unsigned long prate; + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); + cinfo->fck_div = prate / (cinfo->fck / 2); + } else { + cinfo->fck_div = 0; + } +#ifdef CONFIG_ARCH_OMAP4 + cinfo->lck_div = REG_GET(DISPC_DIVISOR1, 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR1, 7, 0); +#else + cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); +#endif + cinfo->lck = cinfo->fck / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; + + return 0; +} + +/* dispc.irq_lock has to be locked by the caller */ +static void _omap_dispc_set_irqs(void) +{ + u32 mask; + u32 old_mask; + int i; + struct omap_dispc_isr_data *isr_data; + + mask = dispc.irq_error_mask; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + + if (isr_data->isr == NULL) + continue; + + mask |= isr_data->mask; + } + + enable_clocks(1); + + old_mask = dispc_read_reg(DISPC_IRQENABLE); + /* clear the irqstatus for newly enabled irqs */ + dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask); + + dispc_write_reg(DISPC_IRQENABLE, mask); + + enable_clocks(0); +} + +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) +{ + int i; + int ret; + unsigned long flags; + struct omap_dispc_isr_data *isr_data; + + if (isr == NULL) + return -EINVAL; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + /* check for duplicate entry */ + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + if (isr_data->isr == isr && isr_data->arg == arg && + isr_data->mask == mask) { + ret = -EINVAL; + goto err; + } + } + + isr_data = NULL; + ret = -EBUSY; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + + if (isr_data->isr != NULL) + continue; + + isr_data->isr = isr; + isr_data->arg = arg; + isr_data->mask = mask; + ret = 0; + + break; + } + + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return 0; +err: + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_register_isr); + +int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) +{ + int i; + unsigned long flags; + int ret = -EINVAL; + struct omap_dispc_isr_data *isr_data; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = &dispc.registered_isr[i]; + if (isr_data->isr != isr || isr_data->arg != arg || + isr_data->mask != mask) + continue; + + /* found the correct isr */ + + isr_data->isr = NULL; + isr_data->arg = NULL; + isr_data->mask = 0; + + ret = 0; + break; + } + + if (ret == 0) + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + return ret; +} +EXPORT_SYMBOL(omap_dispc_unregister_isr); + +#ifdef DEBUG +static void print_irq_status(u32 status) +{ + if ((status & dispc.irq_error_mask) == 0) + return; + + printk(KERN_DEBUG "DISPC IRQ: 0x%x: ", status); + +#define PIS(x) \ + if (status & DISPC_IRQ_##x) \ + printk(#x " "); + PIS(GFX_FIFO_UNDERFLOW); + PIS(OCP_ERR); + PIS(VID1_FIFO_UNDERFLOW); + PIS(VID2_FIFO_UNDERFLOW); + PIS(SYNC_LOST); + PIS(SYNC_LOST_DIGIT); +#undef PIS + + printk("\n"); +} +#endif + +/* Called from dss.c. Note that we don't touch clocks here, + * but we presume they are on because we got an IRQ. However, + * an irq handler may turn the clocks off, so we may not have + * clock later in the function. */ +void dispc_irq_handler(void) +{ + int i; + u32 irqstatus; + u32 handledirqs = 0; + u32 unhandled_errors; + struct omap_dispc_isr_data *isr_data; + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; + + spin_lock(&dispc.irq_lock); + + irqstatus = dispc_read_reg(DISPC_IRQSTATUS); + +#ifdef DEBUG + if (dss_debug) + print_irq_status(irqstatus); +#endif + /* Ack the interrupt. Do it here before clocks are possibly turned + * off */ + dispc_write_reg(DISPC_IRQSTATUS, irqstatus); + + /* make a copy and unlock, so that isrs can unregister + * themselves */ + memcpy(registered_isr, dispc.registered_isr, + sizeof(registered_isr)); + + spin_unlock(&dispc.irq_lock); + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + isr_data = ®istered_isr[i]; + + if (!isr_data->isr) + continue; + + if (isr_data->mask & irqstatus) { + isr_data->isr(isr_data->arg, irqstatus); + handledirqs |= isr_data->mask; + } + } + + spin_lock(&dispc.irq_lock); + + unhandled_errors = irqstatus & ~handledirqs & dispc.irq_error_mask; + + if (unhandled_errors) { + dispc.error_irqs |= unhandled_errors; + + dispc.irq_error_mask &= ~unhandled_errors; + _omap_dispc_set_irqs(); + + schedule_work(&dispc.error_work); + } + + spin_unlock(&dispc.irq_lock); +} + +static void dispc_error_worker(struct work_struct *work) +{ + int i; + u32 errors; + unsigned long flags; + + spin_lock_irqsave(&dispc.irq_lock, flags); + errors = dispc.error_irqs; + dispc.error_irqs = 0; + spin_unlock_irqrestore(&dispc.irq_lock, flags); + + if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) { + DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 0) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) { + DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 1) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) { + DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 2) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } +#ifdef CONFIG_ARCH_OMAP4 + if (errors & DISPC_IRQ_VID3_FIFO_UNDERFLOW) { + DSSERR("VID3_FIFO_UNDERFLOW, disabling VID2\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 3) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } + + if (errors & DISPC_IRQ_SYNC_LOST_2) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST for LCD2, disabling LCD2\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); +#ifdef CONFIG_ARCH_OMAP4 + if (mgr->id == OMAP_DSS_CHANNEL_LCD2) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->disable(mgr->device); + break; + } +#endif + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->enable(manager->device); + } + } + +#endif + + if (errors & DISPC_IRQ_SYNC_LOST) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST, disabling LCD\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_LCD) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->disable(mgr->device); + break; + } + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->enable(manager->device); + } + } + + if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST_DIGIT, disabling TV\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->disable(mgr->device); + break; + } + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->enable(manager->device); + } + } + + if (errors & DISPC_IRQ_OCP_ERR) { + DSSERR("OCP_ERR\n"); + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC) + mgr->device->disable(mgr->device); + } + } + + spin_lock_irqsave(&dispc.irq_lock, flags); + dispc.irq_error_mask |= errors; + _omap_dispc_set_irqs(); + spin_unlock_irqrestore(&dispc.irq_lock, flags); +} + +int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout) +{ + void dispc_irq_wait_handler(void *data, u32 mask) + { + complete((struct completion *)data); + } + + int r; + DECLARE_COMPLETION_ONSTACK(completion); + + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, + irqmask); + + if (r) + return r; + + timeout = wait_for_completion_timeout(&completion, timeout); + + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); + + if (timeout == 0) + return -ETIMEDOUT; + + if (timeout == -ERESTARTSYS) + return -ERESTARTSYS; + + return 0; +} + +int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, + unsigned long timeout) +{ + void dispc_irq_wait_handler(void *data, u32 mask) + { + complete((struct completion *)data); + } + + int r; + DECLARE_COMPLETION_ONSTACK(completion); + + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, + irqmask); + + if (r) + return r; + + timeout = wait_for_completion_interruptible_timeout(&completion, + timeout); + + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); + + if (timeout == 0) + return -ETIMEDOUT; + + if (timeout == -ERESTARTSYS) + return -ERESTARTSYS; + + return 0; +} + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC +void dispc_fake_vsync_irq(void) +{ + u32 irqstatus = DISPC_IRQ_VSYNC; + int i; + + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { + struct omap_dispc_isr_data *isr_data; + isr_data = &dispc.registered_isr[i]; + + if (!isr_data->isr) + continue; + + if (isr_data->mask & irqstatus) + isr_data->isr(isr_data->arg, irqstatus); + } +} +#endif + +static void _omap_dispc_initialize_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&dispc.irq_lock, flags); + + memset(dispc.registered_isr, 0, sizeof(dispc.registered_isr)); + + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; + + /* there's SYNC_LOST_DIGIT waiting after enabling the DSS, + * so clear it */ + dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS)); + + _omap_dispc_set_irqs(); + + spin_unlock_irqrestore(&dispc.irq_lock, flags); +} + +void dispc_enable_sidle(void) +{ + REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */ +} + +void dispc_disable_sidle(void) +{ + REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ +} + +static void _omap_dispc_initial_config(void) +{ + u32 l; + + l = dispc_read_reg(DISPC_SYSCONFIG); + l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */ + l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */ + l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */ + l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */ + dispc_write_reg(DISPC_SYSCONFIG, l); + + /* FUNCGATED */ + REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); + + /* L3 firewall setting: enable access to OCM RAM */ + if (cpu_is_omap24xx()) + __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0)); + + _dispc_setup_color_conv_coef(); + + dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); + + dispc_read_plane_fifo_sizes(); +} + +int dispc_init(void) +{ + u32 rev; + + spin_lock_init(&dispc.irq_lock); + + INIT_WORK(&dispc.error_work, dispc_error_worker); + + dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS); + if (!dispc.base) { + DSSERR("can't ioremap DISPC\n"); + return -ENOMEM; + } + + if (cpu_is_omap34xx()) { + dispc.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); + if (IS_ERR(dispc.dpll4_m4_ck)) { + DSSERR("Failed to get dpll4_m4_ck\n"); + return -ENODEV; + } + } + + + + enable_clocks(1); + + _omap_dispc_initial_config(); + + _omap_dispc_initialize_irq(); + + dispc_save_context(); + + rev = dispc_read_reg(DISPC_REVISION); + printk(KERN_INFO "OMAP DISPC rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + enable_clocks(0); + + return 0; +} + +void dispc_exit(void) +{ + if (cpu_is_omap34xx()) + clk_put(dispc.dpll4_m4_ck); + iounmap(dispc.base); +} + +int dispc_enable_plane(enum omap_plane plane, bool enable) +{ + DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); + + enable_clocks(1); + _dispc_enable_plane(plane, enable); + enable_clocks(0); + + return 0; +} + +int dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, bool mirror, u8 global_alpha, + enum omap_channel channel +#ifdef CONFIG_ARCH_OMAP4 + , u32 puv_addr +#endif + ) +{ + int r = 0; + + DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " + "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n", + plane, paddr, screen_width, pos_x, pos_y, + width, height, + out_width, out_height, + ilace, color_mode, + rotation, mirror); + + enable_clocks(1); + + r = _dispc_setup_plane(plane, + paddr, screen_width, + pos_x, pos_y, + width, height, + out_width, out_height, + color_mode, ilace, + rotation_type, + rotation, mirror, + global_alpha, channel +#ifdef CONFIG_ARCH_OMAP4 + , puv_addr +#endif + ); + + enable_clocks(0); + + return r; +} diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c new file mode 100644 index 000000000000..a375da7db987 --- /dev/null +++ b/drivers/video/omap2/dss/display.c @@ -0,0 +1,681 @@ +/* + * linux/drivers/video/omap2/dss/display.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DISPLAY" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/list.h> +#include <linux/platform_device.h> + +#include <mach/display.h> +#include "dss.h" + +static LIST_HEAD(display_list); + +static ssize_t display_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED; + + return snprintf(buf, PAGE_SIZE, "%s\n", enabled? "true" : "false"); +} + +static ssize_t display_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + bool enabled, r; + + enabled = simple_strtoul(buf, NULL, 10); + + if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { + if (enabled) { + r = dssdev->enable(dssdev); + if (r) + return r; + } else { + dssdev->disable(dssdev); + } + } + + return size; +} + +static ssize_t display_upd_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO; + if (dssdev->get_update_mode) + mode = dssdev->get_update_mode(dssdev); + return snprintf(buf, PAGE_SIZE, "%d\n", mode); +} + +static ssize_t display_upd_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int val, r; + enum omap_dss_update_mode mode; + + val = simple_strtoul(buf, NULL, 10); + + switch (val) { + case OMAP_DSS_UPDATE_DISABLED: + case OMAP_DSS_UPDATE_AUTO: + case OMAP_DSS_UPDATE_MANUAL: + mode = (enum omap_dss_update_mode)val; + break; + default: + return -EINVAL; + } + + r = dssdev->set_update_mode(dssdev, mode); + if (r) + return r; + + return size; +} + +static ssize_t display_tear_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", + dssdev->get_te ? dssdev->get_te(dssdev) : 0); +} + +static ssize_t display_tear_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long te; + int r; + + if (!dssdev->enable_te || !dssdev->get_te) + return -ENOENT; + + te = simple_strtoul(buf, NULL, 0); + + r = dssdev->enable_te(dssdev, te); + if (r) + return r; + + return size; +} + +static ssize_t display_timings_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_video_timings t; + + if (!dssdev->get_timings) + return -ENOENT; + + dssdev->get_timings(dssdev, &t); + + return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", + t.pixel_clock, + t.x_res, t.hfp, t.hbp, t.hsw, + t.y_res, t.vfp, t.vbp, t.vsw); +} + +static ssize_t display_timings_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_video_timings t; + int r, found; + + if (!dssdev->set_timings || !dssdev->check_timings) + return -ENOENT; + + found = 0; +#ifdef CONFIG_OMAP2_DSS_VENC + if (strncmp("pal", buf, 3) == 0) { + t = omap_dss_pal_timings; + found = 1; + } else if (strncmp("ntsc", buf, 4) == 0) { + t = omap_dss_ntsc_timings; + found = 1; + } +#endif + if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", + &t.pixel_clock, + &t.x_res, &t.hfp, &t.hbp, &t.hsw, + &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) + return -EINVAL; + + r = dssdev->check_timings(dssdev, &t); + if (r) + return r; + + dssdev->set_timings(dssdev, &t); + + return size; +} + +static ssize_t display_rotate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int rotate; + if (!dssdev->get_rotate) + return -ENOENT; + rotate = dssdev->get_rotate(dssdev); + return snprintf(buf, PAGE_SIZE, "%u\n", rotate); +} + +static ssize_t display_rotate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long rot; + int r; + + if (!dssdev->set_rotate || !dssdev->get_rotate) + return -ENOENT; + + rot = simple_strtoul(buf, NULL, 0); + + r = dssdev->set_rotate(dssdev, rot); + if (r) + return r; + + return size; +} + +static ssize_t display_mirror_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int mirror; + if (!dssdev->get_mirror) + return -ENOENT; + mirror = dssdev->get_mirror(dssdev); + return snprintf(buf, PAGE_SIZE, "%u\n", mirror); +} + +static ssize_t display_mirror_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long mirror; + int r; + + if (!dssdev->set_mirror || !dssdev->get_mirror) + return -ENOENT; + + mirror = simple_strtoul(buf, NULL, 0); + + r = dssdev->set_mirror(dssdev, mirror); + if (r) + return r; + + return size; +} + +static ssize_t display_wss_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned int wss; + + if (!dssdev->get_wss) + return -ENOENT; + + wss = dssdev->get_wss(dssdev); + + return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); +} + +static ssize_t display_wss_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + unsigned long wss; + int r; + + if (!dssdev->get_wss || !dssdev->set_wss) + return -ENOENT; + + if (strict_strtoul(buf, 0, &wss)) + return -EINVAL; + + if (wss > 0xfffff) + return -EINVAL; + + r = dssdev->set_wss(dssdev, wss); + if (r) + return r; + + return size; +} + +static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, + display_enabled_show, display_enabled_store); +static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, + display_upd_mode_show, display_upd_mode_store); +static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, + display_tear_show, display_tear_store); +static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, + display_timings_show, display_timings_store); +static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, + display_rotate_show, display_rotate_store); +static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, + display_mirror_show, display_mirror_store); +static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, + display_wss_show, display_wss_store); + +static struct device_attribute *display_sysfs_attrs[] = { + &dev_attr_enabled, + &dev_attr_update_mode, + &dev_attr_tear_elim, + &dev_attr_timings, + &dev_attr_rotate, + &dev_attr_mirror, + &dev_attr_wss, + NULL +}; + +static void default_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; +} + +void default_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high) +{ + unsigned low, high, size; + unsigned burst_size_bytes; + +#if 0 + *burst_size = OMAP_DSS_BURST_16x32; /* OMAP4: same as 8x128*/ +#ifndef CONFIG_ARCH_OMAP4 + burst_size_bytes = 16 * 32 / 8; +#else + burst_size_bytes = 8 * 128 / 8; /* OMAP4: highest burst size is 8x128*/ +#endif +#else + *burst_size = OMAP_DSS_BURST_4x32; /* OMAP4: same as 2x128*/ + burst_size_bytes = 2 * 128 / 8; /* OMAP4: highest burst size is 8x128*/ + +#endif + *fifo_high = fifo_size - 1; + *fifo_low = fifo_size - burst_size_bytes; +} + +static int default_wait_vsync(struct omap_dss_device *dssdev) +{ + unsigned long timeout = msecs_to_jiffies(500); + u32 irq; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) + irq = DISPC_IRQ_EVSYNC_ODD; + else if (dssdev->type == OMAP_DISPLAY_TYPE_HDMI) + irq = DISPC_IRQ_EVSYNC_EVEN; + else + irq = DISPC_IRQ_VSYNC; + + return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); +} + +static int default_get_recommended_bpp(struct omap_dss_device *dssdev) +{ + if (dssdev->panel.recommended_bpp) + return dssdev->panel.recommended_bpp; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + if (dssdev->phy.dpi.data_lines == 24) + return 24; + else + return 16; + + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_DSI: + if (dssdev->ctrl.pixel_size == 24) + return 24; + else + return 16; + case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_SDI: + case OMAP_DISPLAY_TYPE_HDMI: + return 24; + default: + BUG(); + } +} + +/* Checks if replication logic should be used. Only use for active matrix, + * when overlay is in RGB12U or RGB16 mode, and LCD interface is + * 18bpp or 24bpp */ +bool dss_use_replication(struct omap_dss_device *dssdev, + enum omap_color_mode mode) +{ + int bpp; + + if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) + return false; + + if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && + (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) + return false; + + if (dssdev->type == OMAP_DISPLAY_TYPE_HDMI) + return false; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + bpp = dssdev->phy.dpi.data_lines; + break; + case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_SDI: + bpp = 24; + break; + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_DSI: + bpp = dssdev->ctrl.pixel_size; + break; + default: + BUG(); + } + + return bpp > 16; +} + +void dss_init_device(struct platform_device *pdev, + struct omap_dss_device *dssdev) +{ + struct device_attribute *attr; + int i; + int r; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: +#ifdef CONFIG_OMAP2_DSS_RFBI + case OMAP_DISPLAY_TYPE_DBI: +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + case OMAP_DISPLAY_TYPE_SDI: +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + case OMAP_DISPLAY_TYPE_VENC: +#endif +#ifdef CONFIG_OMAP2_DSS_HDMI + case OMAP_DISPLAY_TYPE_HDMI: +#endif + break; + default: + DSSERR("Support for display '%s' not compiled in.\n", + dssdev->name); + return; + } + + dssdev->get_resolution = default_get_resolution; + dssdev->get_recommended_bpp = default_get_recommended_bpp; + dssdev->wait_vsync = default_wait_vsync; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + r = dpi_init_display(dssdev); + break; +#ifdef CONFIG_OMAP2_DSS_RFBI + case OMAP_DISPLAY_TYPE_DBI: + r = rfbi_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + case OMAP_DISPLAY_TYPE_VENC: + r = venc_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + case OMAP_DISPLAY_TYPE_SDI: + r = sdi_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + r = dsi_init_display(dssdev); + break; +#endif +#ifdef CONFIG_OMAP2_DSS_HDMI + case OMAP_DISPLAY_TYPE_HDMI: + r = hdmi_init_display(dssdev); + break; +#endif + default: + BUG(); + } + + if (r) { + DSSERR("failed to init display %s\n", dssdev->name); + return; + } + + /* create device sysfs files */ + i = 0; + while ((attr = display_sysfs_attrs[i++]) != NULL) { + r = device_create_file(&dssdev->dev, attr); + if (r) + DSSERR("failed to create sysfs file\n"); + } + + /* create display? sysfs links */ + r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj, + dev_name(&dssdev->dev)); + if (r) + DSSERR("failed to create sysfs display link\n"); +} + +void dss_uninit_device(struct platform_device *pdev, + struct omap_dss_device *dssdev) +{ + struct device_attribute *attr; + int i = 0; + + sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev)); + + while ((attr = display_sysfs_attrs[i++]) != NULL) + device_remove_file(&dssdev->dev, attr); + + if (dssdev->manager) + dssdev->manager->unset_device(dssdev->manager); +} + +static int dss_suspend_device(struct device *dev, void *data) +{ + int r; + struct omap_dss_device *dssdev = to_dss_device(dev); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + dssdev->activate_after_resume = false; + return 0; + } + + if (!dssdev->suspend) { + DSSERR("display '%s' doesn't implement suspend\n", + dssdev->name); + return -ENOSYS; + } + + r = dssdev->suspend(dssdev); + if (r) + return r; + + dssdev->activate_after_resume = true; + + return 0; +} + +int dss_suspend_all_devices(void) +{ + int r; + struct bus_type *bus = dss_get_bus(); + + r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device); + if (r) { + /* resume all displays that were suspended */ + dss_resume_all_devices(); + return r; + } + + return 0; +} + +static int dss_resume_device(struct device *dev, void *data) +{ + int r; + struct omap_dss_device *dssdev = to_dss_device(dev); + + if (dssdev->activate_after_resume && dssdev->resume) { + r = dssdev->resume(dssdev); + if (r) + return r; + } + + dssdev->activate_after_resume = false; + + return 0; +} + +int dss_resume_all_devices(void) +{ + struct bus_type *bus = dss_get_bus(); + + return bus_for_each_dev(bus, NULL, NULL, dss_resume_device); +} + + +void omap_dss_get_device(struct omap_dss_device *dssdev) +{ + get_device(&dssdev->dev); +} +EXPORT_SYMBOL(omap_dss_get_device); + +void omap_dss_put_device(struct omap_dss_device *dssdev) +{ + put_device(&dssdev->dev); +} +EXPORT_SYMBOL(omap_dss_put_device); + +/* ref count of the found device is incremented. ref count + * of from-device is decremented. */ +struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) +{ + struct device *dev; + struct device *dev_start = NULL; + struct omap_dss_device *dssdev = NULL; + + int match(struct device *dev, void *data) + { + /* skip panels connected to controllers */ + if (to_dss_device(dev)->panel.ctrl) + return 0; + + return 1; + } + + if (from) + dev_start = &from->dev; + dev = bus_find_device(dss_get_bus(), dev_start, NULL, match); + if (dev) + dssdev = to_dss_device(dev); + if (from) + put_device(&from->dev); + + return dssdev; +} +EXPORT_SYMBOL(omap_dss_get_next_device); + +struct omap_dss_device *omap_dss_find_device(void *data, + int (*match)(struct omap_dss_device *dssdev, void *data)) +{ + struct omap_dss_device *dssdev = NULL; + + while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { + if (match(dssdev, data)) + return dssdev; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_find_device); + +int omap_dss_start_device(struct omap_dss_device *dssdev) +{ + int r; + + if (!dssdev->driver) { + DSSDBG("no driver\n"); + r = -ENODEV; + goto err0; + } + + if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) { + DSSDBG("no panel driver\n"); + r = -ENODEV; + goto err0; + } + + if (!try_module_get(dssdev->dev.driver->owner)) { + r = -ENODEV; + goto err0; + } + + if (dssdev->ctrl.panel) { + if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) { + r = -ENODEV; + goto err1; + } + } + + return 0; +err1: + module_put(dssdev->dev.driver->owner); +err0: + return r; +} +EXPORT_SYMBOL(omap_dss_start_device); + +void omap_dss_stop_device(struct omap_dss_device *dssdev) +{ + if (dssdev->ctrl.panel) + module_put(dssdev->ctrl.panel->dev.driver->owner); + + module_put(dssdev->dev.driver->owner); +} +EXPORT_SYMBOL(omap_dss_stop_device); + diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c new file mode 100644 index 000000000000..cf85741dd121 --- /dev/null +++ b/drivers/video/omap2/dss/dpi.c @@ -0,0 +1,407 @@ +/* + * linux/drivers/video/omap2/dss/dpi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DPI" + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/errno.h> + +#include <mach/board.h> +#include <mach/display.h> +#include <mach/cpu.h> + +#include "dss.h" + +static struct { + int update_enabled; +} dpi; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL +static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, + unsigned long *fck, int *lck_div, int *pck_div) +{ + struct dsi_clock_info cinfo; + int r; + + r = dsi_pll_calc_pck(is_tft, pck_req, &cinfo); + if (r) + return r; + + r = dsi_pll_program(&cinfo); + if (r) + return r; + + dss_select_clk_source(0, 1); + + /* TODO: update here for LCD 2 support */ + dispc_set_lcd_divisor(OMAP_DSS_CHANNEL_LCD, cinfo.lck_div, + cinfo.pck_div); + + *fck = cinfo.dsi1_pll_fclk; + *lck_div = cinfo.lck_div; + *pck_div = cinfo.pck_div; + + return 0; +} +#else +static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, + unsigned long *fck, int *lck_div, int *pck_div) +{ + struct dispc_clock_info cinfo; + int r = 0; + + return 0; + r = dispc_calc_clock_div(is_tft, pck_req, &cinfo); + if (r) + return r; + + r = dispc_set_clock_div(&cinfo); + if (r) + return r; + + *fck = cinfo.fck; + *lck_div = cinfo.lck_div; + *pck_div = cinfo.pck_div; + + return 0; +} +#endif + +static int dpi_set_mode(struct omap_dss_device *dssdev) +{ + struct omap_video_timings *t = &dssdev->panel.timings; + int lck_div, pck_div; + unsigned long fck; + unsigned long pck; + bool is_tft; + int r = 0; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + /* TODO: add LCD2 support here*/ + dispc_set_pol_freq(OMAP_DSS_CHANNEL_LCD, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); +#else + r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); +#endif + if (r) + goto err0; + +#ifndef CONFIG_ARCH_OMAP4 + pck = fck / lck_div / pck_div / 1000; +#else + pck = 0; +#endif + + if (pck != t->pixel_clock) { + DSSWARN("Could not find exact pixel clock. " + "Requested %d kHz, got %lu kHz\n", + t->pixel_clock, pck); + + t->pixel_clock = pck; + } + + /* TODO: OMAP4: add LCD 2 support here*/ + dispc_set_lcd_timings(OMAP_DSS_CHANNEL_LCD, t); + +err0: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + return r; +} + +static int dpi_basic_init(struct omap_dss_device *dssdev) +{ + bool is_tft; + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_PARALLELMODE_BYPASS); + + /*TODO: change here for LCD 2 support */ + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD, + is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD, + dssdev->phy.dpi.data_lines); + + return 0; +} + +static int dpi_display_enable(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("display already enabled\n"); + r = -EINVAL; + goto err1; + } + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + r = dpi_basic_init(dssdev); + if (r) + goto err2; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_clk_enable(DSS_CLK_FCK2); + r = dsi_pll_init(0, 1); + if (r) + goto err3; +#endif + r = dpi_set_mode(dssdev); + if (r) + goto err4; + + mdelay(2); + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 1); + + r = dssdev->driver->enable(dssdev); + if (r) + goto err5; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; + +err5: + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); +err4: +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dsi_pll_uninit(); +err3: + dss_clk_disable(DSS_CLK_FCK2); +#endif +err2: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev); + +static void dpi_display_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + return; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + dpi_display_resume(dssdev); + + dssdev->driver->disable(dssdev); + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + dss_select_clk_source(0, 0); + dsi_pll_uninit(); + dss_clk_disable(DSS_CLK_FCK2); +#endif + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); +} + +static int dpi_display_suspend(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EINVAL; + + DSSDBG("dpi_display_suspend\n"); + + if (dssdev->driver->suspend) + dssdev->driver->suspend(dssdev); + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int dpi_display_resume(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) + return -EINVAL; + + DSSDBG("dpi_display_resume\n"); + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 1); + + if (dssdev->driver->resume) + dssdev->driver->resume(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void dpi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("dpi_set_timings\n"); + dssdev->panel.timings = *timings; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + dpi_set_mode(dssdev); + dispc_go(OMAP_DSS_CHANNEL_LCD); + } +} + +static int dpi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + bool is_tft; + int r = 0; + int lck_div, pck_div; + unsigned long fck; + unsigned long pck; + + if (!dispc_lcd_timings_ok(timings)) + return -EINVAL; + + if (timings->pixel_clock == 0) + return -EINVAL; + + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; + +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL + { + struct dsi_clock_info cinfo; + r = dsi_pll_calc_pck(is_tft, timings->pixel_clock * 1000, + &cinfo); + + if (r) + return r; + + fck = cinfo.dsi1_pll_fclk; + lck_div = cinfo.lck_div; + pck_div = cinfo.pck_div; + } +#else + { + struct dispc_clock_info cinfo; + r = dispc_calc_clock_div(is_tft, timings->pixel_clock * 1000, + &cinfo); + + if (r) + return r; + + fck = cinfo.fck; + lck_div = cinfo.lck_div; + pck_div = cinfo.pck_div; + } +#endif + + pck = fck / lck_div / pck_div / 1000; + + timings->pixel_clock = pck; + + return 0; +} + +static void dpi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static int dpi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + if (mode == OMAP_DSS_UPDATE_MANUAL) + return -EINVAL; + + if (mode == OMAP_DSS_UPDATE_DISABLED) { + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + dpi.update_enabled = 0; + } else { + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 1); + dpi.update_enabled = 1; + } + + return 0; +} + +static enum omap_dss_update_mode dpi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO : + OMAP_DSS_UPDATE_DISABLED; +} + +int dpi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + dssdev->enable = dpi_display_enable; + dssdev->disable = dpi_display_disable; + dssdev->suspend = dpi_display_suspend; + dssdev->resume = dpi_display_resume; + dssdev->set_timings = dpi_set_timings; + dssdev->check_timings = dpi_check_timings; + dssdev->get_timings = dpi_get_timings; + dssdev->set_update_mode = dpi_display_set_update_mode; + dssdev->get_update_mode = dpi_display_get_update_mode; + + return 0; +} + +int dpi_init(void) +{ + return 0; +} + +void dpi_exit(void) +{ +} + diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c new file mode 100644 index 000000000000..011d0acd973d --- /dev/null +++ b/drivers/video/omap2/dss/dsi.c @@ -0,0 +1,3517 @@ +/* + * linux/drivers/video/omap2/dss/dsi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DSI" + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/seq_file.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/kthread.h> +#include <linux/wait.h> + +#include <mach/board.h> +#include <mach/display.h> +#include <mach/clock.h> + +#include "dss.h" + +/*#define VERBOSE_IRQ*/ + +#define DSI_BASE 0x4804FC00 + +struct dsi_reg { u16 idx; }; + +#define DSI_REG(idx) ((const struct dsi_reg) { idx }) + +#define DSI_SZ_REGS SZ_1K +/* DSI Protocol Engine */ + +#define DSI_REVISION DSI_REG(0x0000) +#define DSI_SYSCONFIG DSI_REG(0x0010) +#define DSI_SYSSTATUS DSI_REG(0x0014) +#define DSI_IRQSTATUS DSI_REG(0x0018) +#define DSI_IRQENABLE DSI_REG(0x001C) +#define DSI_CTRL DSI_REG(0x0040) +#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048) +#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C) +#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050) +#define DSI_CLK_CTRL DSI_REG(0x0054) +#define DSI_TIMING1 DSI_REG(0x0058) +#define DSI_TIMING2 DSI_REG(0x005C) +#define DSI_VM_TIMING1 DSI_REG(0x0060) +#define DSI_VM_TIMING2 DSI_REG(0x0064) +#define DSI_VM_TIMING3 DSI_REG(0x0068) +#define DSI_CLK_TIMING DSI_REG(0x006C) +#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070) +#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074) +#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078) +#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C) +#define DSI_VM_TIMING4 DSI_REG(0x0080) +#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084) +#define DSI_VM_TIMING5 DSI_REG(0x0088) +#define DSI_VM_TIMING6 DSI_REG(0x008C) +#define DSI_VM_TIMING7 DSI_REG(0x0090) +#define DSI_STOPCLK_TIMING DSI_REG(0x0094) +#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20)) +#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20)) +#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20)) +#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20)) +#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20)) + +/* DSIPHY_SCP */ + +#define DSI_DSIPHY_CFG0 DSI_REG(0x200 + 0x0000) +#define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004) +#define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008) +#define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014) + +/* DSI_PLL_CTRL_SCP */ + +#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000) +#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004) +#define DSI_PLL_GO DSI_REG(0x300 + 0x0008) +#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C) +#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010) + +#define REG_GET(idx, start, end) \ + FLD_GET(dsi_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end)) + +/* Global interrupts */ +#define DSI_IRQ_VC0 (1 << 0) +#define DSI_IRQ_VC1 (1 << 1) +#define DSI_IRQ_VC2 (1 << 2) +#define DSI_IRQ_VC3 (1 << 3) +#define DSI_IRQ_WAKEUP (1 << 4) +#define DSI_IRQ_RESYNC (1 << 5) +#define DSI_IRQ_PLL_LOCK (1 << 7) +#define DSI_IRQ_PLL_UNLOCK (1 << 8) +#define DSI_IRQ_PLL_RECALL (1 << 9) +#define DSI_IRQ_COMPLEXIO_ERR (1 << 10) +#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14) +#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15) +#define DSI_IRQ_TE_TRIGGER (1 << 16) +#define DSI_IRQ_ACK_TRIGGER (1 << 17) +#define DSI_IRQ_SYNC_LOST (1 << 18) +#define DSI_IRQ_LDO_POWER_GOOD (1 << 19) +#define DSI_IRQ_TA_TIMEOUT (1 << 20) +#define DSI_IRQ_ERROR_MASK \ + (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ + DSI_IRQ_TA_TIMEOUT) +#define DSI_IRQ_CHANNEL_MASK 0xf + +/* Virtual channel interrupts */ +#define DSI_VC_IRQ_CS (1 << 0) +#define DSI_VC_IRQ_ECC_CORR (1 << 1) +#define DSI_VC_IRQ_PACKET_SENT (1 << 2) +#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3) +#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4) +#define DSI_VC_IRQ_BTA (1 << 5) +#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6) +#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7) +#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8) +#define DSI_VC_IRQ_ERROR_MASK \ + (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \ + DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \ + DSI_VC_IRQ_FIFO_TX_UDF) + +/* ComplexIO interrupts */ +#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) +#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) +#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) +#define DSI_CIO_IRQ_ERRESC1 (1 << 5) +#define DSI_CIO_IRQ_ERRESC2 (1 << 6) +#define DSI_CIO_IRQ_ERRESC3 (1 << 7) +#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) +#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) +#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) +#define DSI_CIO_IRQ_STATEULPS1 (1 << 15) +#define DSI_CIO_IRQ_STATEULPS2 (1 << 16) +#define DSI_CIO_IRQ_STATEULPS3 (1 << 17) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) + +#define DSI_DT_DCS_SHORT_WRITE_0 0x05 +#define DSI_DT_DCS_SHORT_WRITE_1 0x15 +#define DSI_DT_DCS_READ 0x06 +#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37 +#define DSI_DT_NULL_PACKET 0x09 +#define DSI_DT_DCS_LONG_WRITE 0x39 + +#define DSI_DT_RX_ACK_WITH_ERR 0x02 +#define DSI_DT_RX_DCS_LONG_READ 0x1c +#define DSI_DT_RX_SHORT_READ_1 0x21 +#define DSI_DT_RX_SHORT_READ_2 0x22 + +#define FINT_MAX 2100000 +#define FINT_MIN 750000 +#define REGN_MAX (1 << 7) +#define REGM_MAX ((1 << 11) - 1) +#define REGM3_MAX (1 << 4) +#define REGM4_MAX (1 << 4) + +enum fifo_size { + DSI_FIFO_SIZE_0 = 0, + DSI_FIFO_SIZE_32 = 1, + DSI_FIFO_SIZE_64 = 2, + DSI_FIFO_SIZE_96 = 3, + DSI_FIFO_SIZE_128 = 4, +}; + +enum dsi_vc_mode { + DSI_VC_MODE_L4 = 0, + DSI_VC_MODE_VP, +}; + +struct dsi_update_region { + bool dirty; + u16 x, y, w, h; + struct omap_dss_device *device; +}; + +static struct +{ + void __iomem *base; + + unsigned long dsi1_pll_fclk; /* Hz */ + unsigned long dsi2_pll_fclk; /* Hz */ + unsigned long dsiphy; /* Hz */ + unsigned long ddr_clk; /* Hz */ + + struct regulator *vdds_dsi_reg; + + struct { + enum dsi_vc_mode mode; + struct omap_dss_device *dssdev; + enum fifo_size fifo_size; + int dest_per; /* destination peripheral 0-3 */ + } vc[4]; + + struct mutex lock; + struct mutex bus_lock; + + unsigned pll_locked; + + struct completion bta_completion; + + struct task_struct *thread; + wait_queue_head_t waitqueue; + + spinlock_t update_lock; + bool framedone_received; + struct dsi_update_region update_region; + struct dsi_update_region active_update_region; + struct completion update_completion; + + enum omap_dss_update_mode user_update_mode; + enum omap_dss_update_mode update_mode; + bool te_enabled; + bool use_ext_te; + + unsigned long cache_req_pck; + unsigned long cache_clk_freq; + struct dsi_clock_info cache_cinfo; + + u32 errors; + spinlock_t errors_lock; +#ifdef DEBUG + ktime_t perf_setup_time; + ktime_t perf_start_time; + ktime_t perf_start_time_auto; + int perf_measure_frames; +#endif + int debug_read; + int debug_write; +} dsi; + +#ifdef DEBUG +static unsigned int dsi_perf; +module_param_named(dsi_perf, dsi_perf, bool, 0644); +#endif + +static inline void dsi_write_reg(const struct dsi_reg idx, u32 val) +{ + __raw_writel(val, dsi.base + idx.idx); +} + +static inline u32 dsi_read_reg(const struct dsi_reg idx) +{ + return __raw_readl(dsi.base + idx.idx); +} + + +void dsi_save_context(void) +{ +} + +void dsi_restore_context(void) +{ +} + +void dsi_bus_lock(void) +{ + mutex_lock(&dsi.bus_lock); +} +EXPORT_SYMBOL(dsi_bus_lock); + +void dsi_bus_unlock(void) +{ + mutex_unlock(&dsi.bus_lock); +} +EXPORT_SYMBOL(dsi_bus_unlock); + +static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, + int value) +{ + int t = 100000; + + while (REG_GET(idx, bitnum, bitnum) != value) { + if (--t == 0) + return !value; + } + + return value; +} + +#ifdef DEBUG +static void dsi_perf_mark_setup(void) +{ + dsi.perf_setup_time = ktime_get(); +} + +static void dsi_perf_mark_start(void) +{ + dsi.perf_start_time = ktime_get(); +} + +static void dsi_perf_mark_start_auto(void) +{ + dsi.perf_measure_frames = 0; + dsi.perf_start_time_auto = ktime_get(); +} + +static void dsi_perf_show(const char *name) +{ + ktime_t t, setup_time, trans_time; + u32 total_bytes; + u32 setup_us, trans_us, total_us; + + if (!dsi_perf) + return; + + if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED) + return; + + t = ktime_get(); + + setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time); + setup_us = (u32)ktime_to_us(setup_time); + if (setup_us == 0) + setup_us = 1; + + trans_time = ktime_sub(t, dsi.perf_start_time); + trans_us = (u32)ktime_to_us(trans_time); + if (trans_us == 0) + trans_us = 1; + + total_us = setup_us + trans_us; + + total_bytes = dsi.active_update_region.w * + dsi.active_update_region.h * + dsi.active_update_region.device->ctrl.pixel_size / 8; + + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { + static u32 s_total_trans_us, s_total_setup_us; + static u32 s_min_trans_us = 0xffffffff, s_min_setup_us; + static u32 s_max_trans_us, s_max_setup_us; + const int numframes = 100; + ktime_t total_time_auto; + u32 total_time_auto_us; + + dsi.perf_measure_frames++; + + if (setup_us < s_min_setup_us) + s_min_setup_us = setup_us; + + if (setup_us > s_max_setup_us) + s_max_setup_us = setup_us; + + s_total_setup_us += setup_us; + + if (trans_us < s_min_trans_us) + s_min_trans_us = trans_us; + + if (trans_us > s_max_trans_us) + s_max_trans_us = trans_us; + + s_total_trans_us += trans_us; + + if (dsi.perf_measure_frames < numframes) + return; + + total_time_auto = ktime_sub(t, dsi.perf_start_time_auto); + total_time_auto_us = (u32)ktime_to_us(total_time_auto); + + printk(KERN_INFO "DSI(%s): %u fps, setup %u/%u/%u, " + "trans %u/%u/%u\n", + name, + 1000 * 1000 * numframes / total_time_auto_us, + s_min_setup_us, + s_max_setup_us, + s_total_setup_us / numframes, + s_min_trans_us, + s_max_trans_us, + s_total_trans_us / numframes); + + s_total_setup_us = 0; + s_min_setup_us = 0xffffffff; + s_max_setup_us = 0; + s_total_trans_us = 0; + s_min_trans_us = 0xffffffff; + s_max_trans_us = 0; + dsi_perf_mark_start_auto(); + } else { + printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " + "%u bytes, %u kbytes/sec\n", + name, + setup_us, + trans_us, + total_us, + 1000*1000 / total_us, + total_bytes, + total_bytes * 1000 / total_us); + } +} +#else +#define dsi_perf_mark_setup() +#define dsi_perf_mark_start() +#define dsi_perf_mark_start_auto() +#define dsi_perf_show(x) +#endif + +static void print_irq_status(u32 status) +{ +#ifndef VERBOSE_IRQ + if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0) + return; +#endif + printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status); + +#define PIS(x) \ + if (status & DSI_IRQ_##x) \ + printk(#x " "); +#ifdef VERBOSE_IRQ + PIS(VC0); + PIS(VC1); + PIS(VC2); + PIS(VC3); +#endif + PIS(WAKEUP); + PIS(RESYNC); + PIS(PLL_LOCK); + PIS(PLL_UNLOCK); + PIS(PLL_RECALL); + PIS(COMPLEXIO_ERR); + PIS(HS_TX_TIMEOUT); + PIS(LP_RX_TIMEOUT); + PIS(TE_TRIGGER); + PIS(ACK_TRIGGER); + PIS(SYNC_LOST); + PIS(LDO_POWER_GOOD); + PIS(TA_TIMEOUT); +#undef PIS + + printk("\n"); +} + +static void print_irq_status_vc(int channel, u32 status) +{ +#ifndef VERBOSE_IRQ + if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0) + return; +#endif + printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status); + +#define PIS(x) \ + if (status & DSI_VC_IRQ_##x) \ + printk(#x " "); + PIS(CS); + PIS(ECC_CORR); +#ifdef VERBOSE_IRQ + PIS(PACKET_SENT); +#endif + PIS(FIFO_TX_OVF); + PIS(FIFO_RX_OVF); + PIS(BTA); + PIS(ECC_NO_CORR); + PIS(FIFO_TX_UDF); + PIS(PP_BUSY_CHANGE); +#undef PIS + printk("\n"); +} + +static void print_irq_status_cio(u32 status) +{ + printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status); + +#define PIS(x) \ + if (status & DSI_CIO_IRQ_##x) \ + printk(#x " "); + PIS(ERRSYNCESC1); + PIS(ERRSYNCESC2); + PIS(ERRSYNCESC3); + PIS(ERRESC1); + PIS(ERRESC2); + PIS(ERRESC3); + PIS(ERRCONTROL1); + PIS(ERRCONTROL2); + PIS(ERRCONTROL3); + PIS(STATEULPS1); + PIS(STATEULPS2); + PIS(STATEULPS3); + PIS(ERRCONTENTIONLP0_1); + PIS(ERRCONTENTIONLP1_1); + PIS(ERRCONTENTIONLP0_2); + PIS(ERRCONTENTIONLP1_2); + PIS(ERRCONTENTIONLP0_3); + PIS(ERRCONTENTIONLP1_3); + PIS(ULPSACTIVENOT_ALL0); + PIS(ULPSACTIVENOT_ALL1); +#undef PIS + + printk("\n"); +} + +static int debug_irq; + +/* called from dss */ +void dsi_irq_handler(void) +{ + u32 irqstatus, vcstatus, ciostatus; + int i; + + irqstatus = dsi_read_reg(DSI_IRQSTATUS); + + if (irqstatus & DSI_IRQ_ERROR_MASK) { + DSSERR("DSI error, irqstatus %x\n", irqstatus); + print_irq_status(irqstatus); + spin_lock(&dsi.errors_lock); + dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK; + spin_unlock(&dsi.errors_lock); + } else if (debug_irq) { + print_irq_status(irqstatus); + } + + for (i = 0; i < 4; ++i) { + if ((irqstatus & (1<<i)) == 0) + continue; + + vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + + if (vcstatus & DSI_VC_IRQ_BTA) + complete(&dsi.bta_completion); + + if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { + DSSERR("DSI VC(%d) error, vc irqstatus %x\n", + i, vcstatus); + print_irq_status_vc(i, vcstatus); + } else if (debug_irq) { + print_irq_status_vc(i, vcstatus); + } + + dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus); + } + + if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { + ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus); + + DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); + print_irq_status_cio(ciostatus); + } + + dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); +} + + +static void _dsi_initialize_irq(void) +{ + u32 l; + int i; + + /* disable all interrupts */ + dsi_write_reg(DSI_IRQENABLE, 0); + for (i = 0; i < 4; ++i) + dsi_write_reg(DSI_VC_IRQENABLE(i), 0); + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0); + + /* clear interrupt status */ + l = dsi_read_reg(DSI_IRQSTATUS); + dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK); + + for (i = 0; i < 4; ++i) { + l = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + dsi_write_reg(DSI_VC_IRQSTATUS(i), l); + } + + l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l); + + /* enable error irqs */ + l = DSI_IRQ_ERROR_MASK; + dsi_write_reg(DSI_IRQENABLE, l); + + l = DSI_VC_IRQ_ERROR_MASK; + for (i = 0; i < 4; ++i) + dsi_write_reg(DSI_VC_IRQENABLE(i), l); + + /* XXX zonda responds incorrectly, causing control error: + Exit from LP-ESC mode to LP11 uses wrong transition states on the + data lines LP0 and LN0. */ + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, + -1 & (~DSI_CIO_IRQ_ERRCONTROL2)); +} + +static u32 dsi_get_errors(void) +{ + unsigned long flags; + u32 e; + spin_lock_irqsave(&dsi.errors_lock, flags); + e = dsi.errors; + dsi.errors = 0; + spin_unlock_irqrestore(&dsi.errors_lock, flags); + return e; +} + +static void dsi_vc_enable_bta_irq(int channel) +{ + u32 l; + + dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA); + + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l |= DSI_VC_IRQ_BTA; + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); +} + +static void dsi_vc_disable_bta_irq(int channel) +{ + u32 l; + + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l &= ~DSI_VC_IRQ_BTA; + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); +} + +/* DSI func clock. this could also be DSI2_PLL_FCLK */ +static inline void enable_clocks(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +/* source clock for DSI PLL. this could also be PCLKFREE */ +static inline void dsi_enable_pll_clock(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_FCK2); + else + dss_clk_disable(DSS_CLK_FCK2); + + if (enable && dsi.pll_locked) { + if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) + DSSERR("cannot lock PLL when enabling clocks\n"); + } +} + +#ifdef DEBUG +static void _dsi_print_reset_status(void) +{ + u32 l; + + if (!dss_debug) + return; + + /* A dummy read using the SCP interface to any DSIPHY register is + * required after DSIPHY reset to complete the reset of the DSI complex + * I/O. */ + l = dsi_read_reg(DSI_DSIPHY_CFG5); + + printk(KERN_DEBUG "DSI resets: "); + + l = dsi_read_reg(DSI_PLL_STATUS); + printk("PLL (%d) ", FLD_GET(l, 0, 0)); + + l = dsi_read_reg(DSI_COMPLEXIO_CFG1); + printk("CIO (%d) ", FLD_GET(l, 29, 29)); + + l = dsi_read_reg(DSI_DSIPHY_CFG5); + printk("PHY (%x, %d, %d, %d)\n", + FLD_GET(l, 28, 26), + FLD_GET(l, 29, 29), + FLD_GET(l, 30, 30), + FLD_GET(l, 31, 31)); +} +#else +#define _dsi_print_reset_status() +#endif + +static inline int dsi_if_enable(bool enable) +{ + DSSDBG("dsi_if_enable(%d)\n", enable); + + enable = enable ? 1 : 0; + REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */ + + if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) { + DSSERR("Failed to set dsi_if_enable to %d\n", enable); + return -EIO; + } + + return 0; +} + +static unsigned long dsi_fclk_rate(void) +{ + unsigned long r; + + if (dss_get_dsi_clk_source() == 0) { + /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */ + r = dss_clk_get_rate(DSS_CLK_FCK1); + } else { + /* DSI FCLK source is DSI2_PLL_FCLK */ + r = dsi.dsi2_pll_fclk; + } + + return r; +} + +static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev) +{ + unsigned n; + unsigned long dsi_fclk; + unsigned long lp_clk, lp_clk_req; + + dsi_fclk = dsi_fclk_rate(); + + lp_clk_req = dssdev->phy.dsi.lp_clk_hz; + + for (n = 1; n < (1 << 13) - 1; ++n) { + lp_clk = dsi_fclk / 2 / n; + if (lp_clk <= lp_clk_req) + break; + } + + if (n == (1 << 13) - 1) { + DSSERR("Failed to find LP_CLK_DIVISOR\n"); + return -EINVAL; + } + + DSSDBG("LP_CLK_DIV %u, LP_CLK %lu (req %lu)\n", n, lp_clk, lp_clk_req); + + REG_FLD_MOD(DSI_CLK_CTRL, n, 12, 0); /* LP_CLK_DIVISOR */ + if (dsi_fclk > 30*1000*1000) + REG_FLD_MOD(DSI_CLK_CTRL, 1, 21, 21); /* LP_RX_SYNCHRO_ENABLE */ + + return 0; +} + + +enum dsi_pll_power_state { + DSI_PLL_POWER_OFF = 0x0, + DSI_PLL_POWER_ON_HSCLK = 0x1, + DSI_PLL_POWER_ON_ALL = 0x2, + DSI_PLL_POWER_ON_DIV = 0x3, +}; + +static int dsi_pll_power(enum dsi_pll_power_state state) +{ + int t = 0; + + REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ + + /* PLL_PWR_STATUS */ + while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) { + udelay(1); + if (t++ > 1000) { + DSSERR("Failed to set DSI PLL power mode to %d\n", + state); + return -ENODEV; + } + } + + return 0; +} + +int dsi_pll_calc_pck(bool is_tft, unsigned long req_pck, + struct dsi_clock_info *cinfo) +{ + struct dsi_clock_info cur, best; + int min_fck_per_pck; + int match = 0; + unsigned long dss_clk_fck2; + + dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2); + + if (req_pck == dsi.cache_req_pck && + dsi.cache_cinfo.clkin == dss_clk_fck2) { + DSSDBG("DSI clock info found from cache\n"); + *cinfo = dsi.cache_cinfo; + return 0; + } + + min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; + + if (min_fck_per_pck && + req_pck * min_fck_per_pck > DISPC_MAX_FCK) { + DSSERR("Requested pixel clock not possible with the current " + "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " + "the constraint off.\n"); + min_fck_per_pck = 0; + } + + DSSDBG("dsi_pll_calc\n"); + +retry: + memset(&best, 0, sizeof(best)); + + memset(&cur, 0, sizeof(cur)); + cur.clkin = dss_clk_fck2; + cur.use_dss2_fck = 1; + cur.highfreq = 0; + + /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ + /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ + for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { + if (cur.highfreq == 0) + cur.fint = cur.clkin / cur.regn; + else + cur.fint = cur.clkin / (2 * cur.regn); + + if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) + continue; + + /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { + unsigned long a, b; + + a = 2 * cur.regm * (cur.clkin/1000); + b = cur.regn * (cur.highfreq + 1); + cur.dsiphy = a / b * 1000; + + if (cur.dsiphy > 1800 * 1000 * 1000) + break; + + /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */ + for (cur.regm3 = 1; cur.regm3 < REGM3_MAX; + ++cur.regm3) { + cur.dsi1_pll_fclk = cur.dsiphy / cur.regm3; + + /* this will narrow down the search a bit, + * but still give pixclocks below what was + * requested */ + if (cur.dsi1_pll_fclk < req_pck) + break; + + if (cur.dsi1_pll_fclk > DISPC_MAX_FCK) + continue; + + if (min_fck_per_pck && + cur.dsi1_pll_fclk < + req_pck * min_fck_per_pck) + continue; + + match = 1; + + find_lck_pck_divs(is_tft, req_pck, + cur.dsi1_pll_fclk, + &cur.lck_div, + &cur.pck_div); + + cur.lck = cur.dsi1_pll_fclk / cur.lck_div; + cur.pck = cur.lck / cur.pck_div; + + if (abs(cur.pck - req_pck) < + abs(best.pck - req_pck)) { + best = cur; + + if (cur.pck == req_pck) + goto found; + } + } + } + } +found: + if (!match) { + if (min_fck_per_pck) { + DSSERR("Could not find suitable clock settings.\n" + "Turning FCK/PCK constraint off and" + "trying again.\n"); + min_fck_per_pck = 0; + goto retry; + } + + DSSERR("Could not find suitable clock settings.\n"); + + return -EINVAL; + } + + /* DSI2_PLL_FCLK (regm4) is not used. Set it to something sane. */ + best.regm4 = best.dsiphy / 48000000; + if (best.regm4 > REGM4_MAX) + best.regm4 = REGM4_MAX; + else if (best.regm4 == 0) + best.regm4 = 1; + best.dsi2_pll_fclk = best.dsiphy / best.regm4; + + if (cinfo) + *cinfo = best; + + dsi.cache_req_pck = req_pck; + dsi.cache_clk_freq = 0; + dsi.cache_cinfo = best; + + return 0; +} + +static int dsi_pll_calc_ddrfreq(unsigned long clk_freq, + struct dsi_clock_info *cinfo) +{ + struct dsi_clock_info cur, best; + const bool use_dss2_fck = 1; + unsigned long datafreq; + unsigned long dss_clk_fck2; + + DSSDBG("dsi_pll_calc_ddrfreq\n"); + + dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2); + + if (clk_freq == dsi.cache_clk_freq && + dsi.cache_cinfo.clkin == dss_clk_fck2) { + DSSDBG("DSI clock info found from cache\n"); + *cinfo = dsi.cache_cinfo; + return 0; + } + + datafreq = clk_freq * 4; + + memset(&best, 0, sizeof(best)); + + memset(&cur, 0, sizeof(cur)); + cur.use_dss2_fck = use_dss2_fck; + if (use_dss2_fck) { + cur.clkin = dss_clk_fck2; + cur.highfreq = 0; + } else { + /* TODO: Add support for LCD2 */ + cur.clkin = dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD); + if (cur.clkin < 32000000) + cur.highfreq = 0; + else + cur.highfreq = 1; + } + + /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ + /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ + for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { + if (cur.highfreq == 0) + cur.fint = cur.clkin / cur.regn; + else + cur.fint = cur.clkin / (2 * cur.regn); + + if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) + continue; + + /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { + unsigned long a, b; + + a = 2 * cur.regm * (cur.clkin/1000); + b = cur.regn * (cur.highfreq + 1); + cur.dsiphy = a / b * 1000; + + if (cur.dsiphy > 1800 * 1000 * 1000) + break; + + if (abs(cur.dsiphy - datafreq) < + abs(best.dsiphy - datafreq)) { + best = cur; + /* DSSDBG("best %ld\n", best.dsiphy); */ + } + + if (cur.dsiphy == datafreq) + goto found; + } + } +found: + /* DSI1_PLL_FCLK (regm3) is not used. Set it to something sane. */ + best.regm3 = best.dsiphy / 48000000; + if (best.regm3 > REGM3_MAX) + best.regm3 = REGM3_MAX; + else if (best.regm3 == 0) + best.regm3 = 1; + best.dsi1_pll_fclk = best.dsiphy / best.regm3; + + /* DSI2_PLL_FCLK (regm4) is not used. Set it to something sane. */ + best.regm4 = best.dsiphy / 48000000; + if (best.regm4 > REGM4_MAX) + best.regm4 = REGM4_MAX; + else if (best.regm4 == 0) + best.regm4 = 1; + best.dsi2_pll_fclk = best.dsiphy / best.regm4; + + if (cinfo) + *cinfo = best; + + dsi.cache_clk_freq = clk_freq; + dsi.cache_req_pck = 0; + dsi.cache_cinfo = best; + + return 0; +} + +int dsi_pll_program(struct dsi_clock_info *cinfo) +{ + int r = 0; + u32 l; + + DSSDBG("dsi_pll_program\n"); + + dsi.dsiphy = cinfo->dsiphy; + dsi.ddr_clk = dsi.dsiphy / 4; + dsi.dsi1_pll_fclk = cinfo->dsi1_pll_fclk; + dsi.dsi2_pll_fclk = cinfo->dsi2_pll_fclk; + + DSSDBG("DSI Fint %ld\n", cinfo->fint); + + DSSDBG("clkin (%s) rate %ld, highfreq %d\n", + cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree", + cinfo->clkin, + cinfo->highfreq); + + /* DSIPHY == CLKIN4DDR */ + DSSDBG("DSIPHY = 2 * %d / %d * %lu / %d = %lu\n", + cinfo->regm, + cinfo->regn, + cinfo->clkin, + cinfo->highfreq + 1, + cinfo->dsiphy); + + DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", + dsi.dsiphy / 1000 / 1000 / 2); + + DSSDBG("Clock lane freq %ld Hz\n", dsi.ddr_clk); + + DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n", + cinfo->regm3, cinfo->dsi1_pll_fclk); + DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n", + cinfo->regm4, cinfo->dsi2_pll_fclk); + + REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */ + + l = dsi_read_reg(DSI_PLL_CONFIGURATION1); + l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ + l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */ + l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */ + l = FLD_MOD(l, cinfo->regm3 - 1, 22, 19); /* DSI_CLOCK_DIV */ + l = FLD_MOD(l, cinfo->regm4 - 1, 26, 23); /* DSIPROTO_CLOCK_DIV */ + dsi_write_reg(DSI_PLL_CONFIGURATION1, l); + + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = FLD_MOD(l, 7, 4, 1); /* DSI_PLL_FREQSEL */ + /* DSI_PLL_CLKSEL */ + l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, 11, 11); + l = FLD_MOD(l, cinfo->highfreq, 12, 12); /* DSI_PLL_HIGHFREQ */ + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ + l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ + l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + + REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ + + if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) { + DSSERR("dsi pll go bit not going down.\n"); + r = -EIO; + goto err; + } + + if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) { + DSSERR("cannot lock PLL\n"); + r = -EIO; + goto err; + } + + dsi.pll_locked = 1; + + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ + l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ + l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ + l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */ + l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */ + l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */ + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ + l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */ + l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */ + l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */ + l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */ + l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ + l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ + l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + + DSSDBG("PLL config done\n"); +err: + return r; +} + +int dsi_pll_init(bool enable_hsclk, bool enable_hsdiv) +{ + int r = 0; + enum dsi_pll_power_state pwstate; + struct dispc_clock_info cinfo; + + DSSDBG("PLL init\n"); + + enable_clocks(1); + dsi_enable_pll_clock(1); + + /* XXX this should be calculated depending on the screen size, + * required framerate and DSI speed. + * For now 48MHz is enough for 864x480@60 with 360Mbps/lane + * with two lanes */ + r = dispc_calc_clock_div(1, 48 * 1000 * 1000, &cinfo); + if (r) + goto err0; + + r = dispc_set_clock_div(&cinfo); + if (r) { + DSSERR("Failed to set basic clocks\n"); + goto err0; + } + + r = regulator_enable(dsi.vdds_dsi_reg); + if (r) + goto err0; + + /* XXX PLL does not come out of reset without this... */ + dispc_pck_free_enable(1); + + if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { + DSSERR("PLL not coming out of reset.\n"); + r = -ENODEV; + goto err1; + } + + /* XXX ... but if left on, we get problems when planes do not + * fill the whole display. No idea about this */ + dispc_pck_free_enable(0); + + if (enable_hsclk && enable_hsdiv) + pwstate = DSI_PLL_POWER_ON_ALL; + else if (enable_hsclk) + pwstate = DSI_PLL_POWER_ON_HSCLK; + else if (enable_hsdiv) + pwstate = DSI_PLL_POWER_ON_DIV; + else + pwstate = DSI_PLL_POWER_OFF; + + r = dsi_pll_power(pwstate); + + if (r) + goto err1; + + DSSDBG("PLL init done\n"); + + return 0; +err1: + regulator_disable(dsi.vdds_dsi_reg); +err0: + enable_clocks(0); + dsi_enable_pll_clock(0); + return r; +} + +void dsi_pll_uninit(void) +{ + enable_clocks(0); + dsi_enable_pll_clock(0); + + dsi.pll_locked = 0; + dsi_pll_power(DSI_PLL_POWER_OFF); + regulator_disable(dsi.vdds_dsi_reg); + DSSDBG("PLL uninit done\n"); +} + +unsigned long dsi_get_dsi1_pll_rate(void) +{ + return dsi.dsi1_pll_fclk; +} + +unsigned long dsi_get_dsi2_pll_rate(void) +{ + return dsi.dsi2_pll_fclk; +} + +void dsi_dump_clocks(struct seq_file *s) +{ + int clksel; + + enable_clocks(1); + + clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); + + seq_printf(s, "- dsi -\n"); + + seq_printf(s, "dsi fclk source = %s\n", + dss_get_dsi_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi2_pll_fclk"); + + seq_printf(s, "dsi pll source = %s\n", + clksel == 0 ? + "dss2_alwon_fclk" : "pclkfree"); + + seq_printf(s, "DSIPHY\t\t%lu\nDDR_CLK\t\t%lu\n", + dsi.dsiphy, dsi.ddr_clk); + + seq_printf(s, "dsi1_pll_fck\t%lu (%s)\n" + "dsi2_pll_fck\t%lu (%s)\n", + dsi.dsi1_pll_fclk, + dss_get_dispc_clk_source() == 0 ? "off" : "on", + dsi.dsi2_pll_fclk, + dss_get_dsi_clk_source() == 0 ? "off" : "on"); + + enable_clocks(0); +} + +void dsi_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DSI_REVISION); + DUMPREG(DSI_SYSCONFIG); + DUMPREG(DSI_SYSSTATUS); + DUMPREG(DSI_IRQSTATUS); + DUMPREG(DSI_IRQENABLE); + DUMPREG(DSI_CTRL); + DUMPREG(DSI_COMPLEXIO_CFG1); + DUMPREG(DSI_COMPLEXIO_IRQ_STATUS); + DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE); + DUMPREG(DSI_CLK_CTRL); + DUMPREG(DSI_TIMING1); + DUMPREG(DSI_TIMING2); + DUMPREG(DSI_VM_TIMING1); + DUMPREG(DSI_VM_TIMING2); + DUMPREG(DSI_VM_TIMING3); + DUMPREG(DSI_CLK_TIMING); + DUMPREG(DSI_TX_FIFO_VC_SIZE); + DUMPREG(DSI_RX_FIFO_VC_SIZE); + DUMPREG(DSI_COMPLEXIO_CFG2); + DUMPREG(DSI_RX_FIFO_VC_FULLNESS); + DUMPREG(DSI_VM_TIMING4); + DUMPREG(DSI_TX_FIFO_VC_EMPTINESS); + DUMPREG(DSI_VM_TIMING5); + DUMPREG(DSI_VM_TIMING6); + DUMPREG(DSI_VM_TIMING7); + DUMPREG(DSI_STOPCLK_TIMING); + + DUMPREG(DSI_VC_CTRL(0)); + DUMPREG(DSI_VC_TE(0)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(0)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0)); + DUMPREG(DSI_VC_IRQSTATUS(0)); + DUMPREG(DSI_VC_IRQENABLE(0)); + + DUMPREG(DSI_VC_CTRL(1)); + DUMPREG(DSI_VC_TE(1)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(1)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1)); + DUMPREG(DSI_VC_IRQSTATUS(1)); + DUMPREG(DSI_VC_IRQENABLE(1)); + + DUMPREG(DSI_VC_CTRL(2)); + DUMPREG(DSI_VC_TE(2)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(2)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2)); + DUMPREG(DSI_VC_IRQSTATUS(2)); + DUMPREG(DSI_VC_IRQENABLE(2)); + + DUMPREG(DSI_VC_CTRL(3)); + DUMPREG(DSI_VC_TE(3)); + DUMPREG(DSI_VC_LONG_PACKET_HEADER(3)); + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3)); + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3)); + DUMPREG(DSI_VC_IRQSTATUS(3)); + DUMPREG(DSI_VC_IRQENABLE(3)); + + DUMPREG(DSI_DSIPHY_CFG0); + DUMPREG(DSI_DSIPHY_CFG1); + DUMPREG(DSI_DSIPHY_CFG2); + DUMPREG(DSI_DSIPHY_CFG5); + + DUMPREG(DSI_PLL_CONTROL); + DUMPREG(DSI_PLL_STATUS); + DUMPREG(DSI_PLL_GO); + DUMPREG(DSI_PLL_CONFIGURATION1); + DUMPREG(DSI_PLL_CONFIGURATION2); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +enum dsi_complexio_power_state { + DSI_COMPLEXIO_POWER_OFF = 0x0, + DSI_COMPLEXIO_POWER_ON = 0x1, + DSI_COMPLEXIO_POWER_ULPS = 0x2, +}; + +static int dsi_complexio_power(enum dsi_complexio_power_state state) +{ + int t = 0; + + /* PWR_CMD */ + REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27); + + /* PWR_STATUS */ + while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) { + udelay(1); + if (t++ > 1000) { + DSSERR("failed to set complexio power state to " + "%d\n", state); + return -ENODEV; + } + } + + return 0; +} + +static void dsi_complexio_config(struct omap_dss_device *dssdev) +{ + u32 r; + + int clk_lane = dssdev->phy.dsi.clk_lane; + int data1_lane = dssdev->phy.dsi.data1_lane; + int data2_lane = dssdev->phy.dsi.data2_lane; + int clk_pol = dssdev->phy.dsi.clk_pol; + int data1_pol = dssdev->phy.dsi.data1_pol; + int data2_pol = dssdev->phy.dsi.data2_pol; + + r = dsi_read_reg(DSI_COMPLEXIO_CFG1); + r = FLD_MOD(r, clk_lane, 2, 0); + r = FLD_MOD(r, clk_pol, 3, 3); + r = FLD_MOD(r, data1_lane, 6, 4); + r = FLD_MOD(r, data1_pol, 7, 7); + r = FLD_MOD(r, data2_lane, 10, 8); + r = FLD_MOD(r, data2_pol, 11, 11); + dsi_write_reg(DSI_COMPLEXIO_CFG1, r); + + /* The configuration of the DSI complex I/O (number of data lanes, + position, differential order) should not be changed while + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for + the hardware to take into account a new configuration of the complex + I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to + follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, + then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set + DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the + DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the + DSI complex I/O configuration is unknown. */ + + /* + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + REG_FLD_MOD(DSI_CTRL, 0, 0, 0); + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + */ +} + +static inline unsigned ns2ddr(unsigned ns) +{ + /* convert time in ns to ddr ticks, rounding up */ + return (ns * (dsi.ddr_clk/1000/1000) + 999) / 1000; +} + +static inline unsigned ddr2ns(unsigned ddr) +{ + return ddr * 1000 * 1000 / (dsi.ddr_clk / 1000); +} + +static void dsi_complexio_timings(void) +{ + u32 r; + u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; + u32 tlpx_half, tclk_trail, tclk_zero; + u32 tclk_prepare; + + /* calculate timings */ + + /* 1 * DDR_CLK = 2 * UI */ + + /* min 40ns + 4*UI max 85ns + 6*UI */ + ths_prepare = ns2ddr(70) + 2; + + /* min 145ns + 10*UI */ + ths_prepare_ths_zero = ns2ddr(175) + 2; + + /* min max(8*UI, 60ns+4*UI) */ + ths_trail = ns2ddr(60) + 5; + + /* min 100ns */ + ths_exit = ns2ddr(145); + + /* tlpx min 50n */ + tlpx_half = ns2ddr(25); + + /* min 60ns */ + tclk_trail = ns2ddr(60) + 2; + + /* min 38ns, max 95ns */ + tclk_prepare = ns2ddr(65); + + /* min tclk-prepare + tclk-zero = 300ns */ + tclk_zero = ns2ddr(260); + + DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", + ths_prepare, ddr2ns(ths_prepare), + ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero)); + DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", + ths_trail, ddr2ns(ths_trail), + ths_exit, ddr2ns(ths_exit)); + + DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " + "tclk_zero %u (%uns)\n", + tlpx_half, ddr2ns(tlpx_half), + tclk_trail, ddr2ns(tclk_trail), + tclk_zero, ddr2ns(tclk_zero)); + DSSDBG("tclk_prepare %u (%uns)\n", + tclk_prepare, ddr2ns(tclk_prepare)); + + /* program timings */ + + r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = FLD_MOD(r, ths_prepare, 31, 24); + r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); + r = FLD_MOD(r, ths_trail, 15, 8); + r = FLD_MOD(r, ths_exit, 7, 0); + dsi_write_reg(DSI_DSIPHY_CFG0, r); + + r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = FLD_MOD(r, tlpx_half, 22, 16); + r = FLD_MOD(r, tclk_trail, 15, 8); + r = FLD_MOD(r, tclk_zero, 7, 0); + dsi_write_reg(DSI_DSIPHY_CFG1, r); + + r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = FLD_MOD(r, tclk_prepare, 7, 0); + dsi_write_reg(DSI_DSIPHY_CFG2, r); +} + + +static int dsi_complexio_init(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("dsi_complexio_init\n"); + + /* CIO_CLK_ICG, enable L3 clk to CIO */ + REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14); + + /* A dummy read using the SCP interface to any DSIPHY register is + * required after DSIPHY reset to complete the reset of the DSI complex + * I/O. */ + dsi_read_reg(DSI_DSIPHY_CFG5); + + if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) { + DSSERR("ComplexIO PHY not coming out of reset.\n"); + r = -ENODEV; + goto err; + } + + dsi_complexio_config(dssdev); + + r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); + + if (r) + goto err; + + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { + DSSERR("ComplexIO not coming out of reset.\n"); + r = -ENODEV; + goto err; + } + + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) { + DSSERR("ComplexIO LDO power down.\n"); + r = -ENODEV; + goto err; + } + + dsi_complexio_timings(); + + /* + The configuration of the DSI complex I/O (number of data lanes, + position, differential order) should not be changed while + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the + hardware to recognize a new configuration of the complex I/O (done + in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow + this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next + reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20] + LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN + bit to 1. If the sequence is not followed, the DSi complex I/O + configuration is undetermined. + */ + dsi_if_enable(1); + dsi_if_enable(0); + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ + dsi_if_enable(1); + dsi_if_enable(0); + + DSSDBG("CIO init done\n"); +err: + return r; +} + +static void dsi_complexio_uninit(void) +{ + dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF); +} + +static int _dsi_wait_reset(void) +{ + int i = 0; + + while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) { + if (i++ > 5) { + DSSERR("soft reset failed\n"); + return -ENODEV; + } + udelay(1); + } + + return 0; +} + +static int _dsi_reset(void) +{ + /* Soft reset */ + REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1); + return _dsi_wait_reset(); +} + + +static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) +{ + u32 r = 0; + int add = 0; + int i; + + dsi.vc[0].fifo_size = size1; + dsi.vc[1].fifo_size = size2; + dsi.vc[2].fifo_size = size3; + dsi.vc[3].fifo_size = size4; + + for (i = 0; i < 4; i++) { + u8 v; + int size = dsi.vc[i].fifo_size; + + if (add + size > 4) { + DSSERR("Illegal FIFO configuration\n"); + BUG(); + } + + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); + r |= v << (8 * i); + /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */ + add += size; + } + + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r); +} + +static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) +{ + u32 r = 0; + int add = 0; + int i; + + dsi.vc[0].fifo_size = size1; + dsi.vc[1].fifo_size = size2; + dsi.vc[2].fifo_size = size3; + dsi.vc[3].fifo_size = size4; + + for (i = 0; i < 4; i++) { + u8 v; + int size = dsi.vc[i].fifo_size; + + if (add + size > 4) { + DSSERR("Illegal FIFO configuration\n"); + BUG(); + } + + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); + r |= v << (8 * i); + /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */ + add += size; + } + + dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r); +} + +static int dsi_force_tx_stop_mode_io(void) +{ + u32 r; + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + dsi_write_reg(DSI_TIMING1, r); + + if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) { + DSSERR("TX_STOP bit not going down\n"); + return -EIO; + } + + return 0; +} + +static void dsi_vc_print_status(int channel) +{ + u32 r; + + r = dsi_read_reg(DSI_VC_CTRL(channel)); + DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, " + "TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ", + channel, + FLD_GET(r, 5, 5), + FLD_GET(r, 6, 6), + FLD_GET(r, 15, 15), + FLD_GET(r, 16, 16), + FLD_GET(r, 20, 20)); + + r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS); + DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff); +} + +static int dsi_vc_enable(int channel, bool enable) +{ + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) + DSSDBG("dsi_vc_enable channel %d, enable %d\n", + channel, enable); + + enable = enable ? 1 : 0; + + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0); + + if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) { + DSSERR("Failed to set dsi_vc_enable to %d\n", enable); + return -EIO; + } + + return 0; +} + +static void dsi_vc_initial_config(int channel) +{ + u32 r; + + DSSDBGF("%d", channel); + + r = dsi_read_reg(DSI_VC_CTRL(channel)); + + if (FLD_GET(r, 15, 15)) /* VC_BUSY */ + DSSERR("VC(%d) busy when trying to configure it!\n", + channel); + + r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */ + r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ + r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */ + r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */ + r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ + r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ + r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ + + r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ + r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ + + dsi_write_reg(DSI_VC_CTRL(channel), r); + dsi.vc[channel].mode = DSI_VC_MODE_L4; +} + +static void dsi_vc_config_l4(int channel) +{ + if (dsi.vc[channel].mode == DSI_VC_MODE_L4) + return; + + DSSDBGF("%d", channel); + + dsi_vc_enable(channel, 0); + + if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + DSSERR("vc(%d) busy when trying to config for L4\n", channel); + + REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ + + dsi_vc_enable(channel, 1); + + dsi.vc[channel].mode = DSI_VC_MODE_L4; +} + +static void dsi_vc_config_vp(int channel) +{ + if (dsi.vc[channel].mode == DSI_VC_MODE_VP) + return; + + DSSDBGF("%d", channel); + + dsi_vc_enable(channel, 0); + + if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + DSSERR("vc(%d) busy when trying to config for VP\n", channel); + + REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ + + dsi_vc_enable(channel, 1); + + dsi.vc[channel].mode = DSI_VC_MODE_VP; +} + + +static void dsi_vc_enable_hs(int channel, bool enable) +{ + DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); + + dsi_vc_enable(channel, 0); + dsi_if_enable(0); + + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9); + + dsi_vc_enable(channel, 1); + dsi_if_enable(1); + + dsi_force_tx_stop_mode_io(); +} + +static void dsi_vc_flush_long_data(int channel) +{ + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + u32 val; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); + } +} + +static void dsi_show_rx_ack_with_err(u16 err) +{ + DSSERR("\tACK with ERROR (%#x):\n", err); + if (err & (1 << 0)) + DSSERR("\t\tSoT Error\n"); + if (err & (1 << 1)) + DSSERR("\t\tSoT Sync Error\n"); + if (err & (1 << 2)) + DSSERR("\t\tEoT Sync Error\n"); + if (err & (1 << 3)) + DSSERR("\t\tEscape Mode Entry Command Error\n"); + if (err & (1 << 4)) + DSSERR("\t\tLP Transmit Sync Error\n"); + if (err & (1 << 5)) + DSSERR("\t\tHS Receive Timeout Error\n"); + if (err & (1 << 6)) + DSSERR("\t\tFalse Control Error\n"); + if (err & (1 << 7)) + DSSERR("\t\t(reserved7)\n"); + if (err & (1 << 8)) + DSSERR("\t\tECC Error, single-bit (corrected)\n"); + if (err & (1 << 9)) + DSSERR("\t\tECC Error, multi-bit (not corrected)\n"); + if (err & (1 << 10)) + DSSERR("\t\tChecksum Error\n"); + if (err & (1 << 11)) + DSSERR("\t\tData type not recognized\n"); + if (err & (1 << 12)) + DSSERR("\t\tInvalid VC ID\n"); + if (err & (1 << 13)) + DSSERR("\t\tInvalid Transmission Length\n"); + if (err & (1 << 14)) + DSSERR("\t\t(reserved14)\n"); + if (err & (1 << 15)) + DSSERR("\t\tDSI Protocol Violation\n"); +} + +static u16 dsi_vc_flush_receive_data(int channel) +{ + /* RX_FIFO_NOT_EMPTY */ + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + u32 val; + u8 dt; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + DSSDBG("\trawval %#08x\n", val); + dt = FLD_GET(val, 5, 0); + if (dt == DSI_DT_RX_ACK_WITH_ERR) { + u16 err = FLD_GET(val, 23, 8); + dsi_show_rx_ack_with_err(err); + } else if (dt == DSI_DT_RX_SHORT_READ_1) { + DSSDBG("\tDCS short response, 1 byte: %#x\n", + FLD_GET(val, 23, 8)); + } else if (dt == DSI_DT_RX_SHORT_READ_2) { + DSSDBG("\tDCS short response, 2 byte: %#x\n", + FLD_GET(val, 23, 8)); + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + DSSDBG("\tDCS long response, len %d\n", + FLD_GET(val, 23, 8)); + dsi_vc_flush_long_data(channel); + } else { + DSSERR("\tunknown datatype 0x%02x\n", dt); + } + } + return 0; +} + +static int dsi_vc_send_bta(int channel) +{ + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO && + (dsi.debug_write || dsi.debug_read)) + DSSDBG("dsi_vc_send_bta %d\n", channel); + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); + dsi_vc_flush_receive_data(channel); + } + + REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ + + return 0; +} + +int dsi_vc_send_bta_sync(int channel) +{ + int r = 0; + u32 err; + + INIT_COMPLETION(dsi.bta_completion); + + dsi_vc_enable_bta_irq(channel); + + r = dsi_vc_send_bta(channel); + if (r) + goto err; + + if (wait_for_completion_timeout(&dsi.bta_completion, + msecs_to_jiffies(500)) == 0) { + DSSERR("Failed to receive BTA\n"); + r = -EIO; + goto err; + } + + err = dsi_get_errors(); + if (err) { + DSSERR("Error while sending BTA: %x\n", err); + r = -EIO; + goto err; + } +err: + dsi_vc_disable_bta_irq(channel); + + return r; +} +EXPORT_SYMBOL(dsi_vc_send_bta_sync); + +static inline void dsi_vc_write_long_header(int channel, u8 data_type, + u16 len, u8 ecc) +{ + u32 val; + u8 data_id; + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + /*data_id = data_type | channel << 6; */ + data_id = data_type | dsi.vc[channel].dest_per << 6; + + val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | + FLD_VAL(ecc, 31, 24); + + dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val); +} + +static inline void dsi_vc_write_long_payload(int channel, + u8 b1, u8 b2, u8 b3, u8 b4) +{ + u32 val; + + val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0; + +/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", + b1, b2, b3, b4, val); */ + + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val); +} + +static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, + u8 ecc) +{ + /*u32 val; */ + int i; + u8 *p; + int r = 0; + u8 b1, b2, b3, b4; + + if (dsi.debug_write) + DSSDBG("dsi_vc_send_long, %d bytes\n", len); + + /* len + header */ + if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) { + DSSERR("unable to send long packet: packet too long.\n"); + return -EINVAL; + } + dsi_vc_config_l4(channel); + + dsi_vc_write_long_header(channel, data_type, len, ecc); + + /*dsi_vc_print_status(0); */ + + p = data; + for (i = 0; i < len >> 2; i++) { + if (dsi.debug_write) + DSSDBG("\tsending full packet %d\n", i); + /*dsi_vc_print_status(0); */ + + b1 = *p++; + b2 = *p++; + b3 = *p++; + b4 = *p++; + + dsi_vc_write_long_payload(channel, b1, b2, b3, b4); + } + + i = len % 4; + if (i) { + b1 = 0; b2 = 0; b3 = 0; + + if (dsi.debug_write) + DSSDBG("\tsending remainder bytes %d\n", i); + + switch (i) { + case 3: + b1 = *p++; + b2 = *p++; + b3 = *p++; + break; + case 2: + b1 = *p++; + b2 = *p++; + break; + case 1: + b1 = *p++; + break; + } + + dsi_vc_write_long_payload(channel, b1, b2, b3, 0); + } + + return r; +} + +static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc) +{ + u32 r; + u8 data_id; + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + if (dsi.debug_write) + DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", + channel, + data_type, data & 0xff, (data >> 8) & 0xff); + + dsi_vc_config_l4(channel); + + if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) { + DSSERR("ERROR FIFO FULL, aborting transfer\n"); + return -EINVAL; + } + + data_id = data_type | channel << 6; + + r = (data_id << 0) | (data << 8) | (ecc << 24); + + dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r); + + return 0; +} + +int dsi_vc_send_null(int channel) +{ + u8 nullpkg[] = {0, 0, 0, 0}; + return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0); +} +EXPORT_SYMBOL(dsi_vc_send_null); + +int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) +{ + int r; + + BUG_ON(len == 0); + + if (len == 1) { + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0, + data[0], 0); + } else if (len == 2) { + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1, + data[0] | (data[1] << 8), 0); + } else { + /* 0x39 = DCS Long Write */ + r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE, + data, len, 0); + } + + return r; +} +EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); + +int dsi_vc_dcs_write(int channel, u8 *data, int len) +{ + int r; + + r = dsi_vc_dcs_write_nosync(channel, data, len); + if (r) + return r; + + /* Some devices need time to process the msg in low power mode. + This also makes the write synchronous, and checks that + the peripheral is still alive */ + r = dsi_vc_send_bta_sync(channel); + + return r; +} +EXPORT_SYMBOL(dsi_vc_dcs_write); + +int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) +{ + u32 val; + u8 dt; + int r; + + if (dsi.debug_read) + DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %u)\n", channel, dcs_cmd); + + r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0); + if (r) + return r; + + r = dsi_vc_send_bta_sync(channel); + if (r) + return r; + + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) { + DSSERR("RX fifo empty when trying to read.\n"); + return -EIO; + } + + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi.debug_read) + DSSDBG("\theader: %08x\n", val); + dt = FLD_GET(val, 5, 0); + if (dt == DSI_DT_RX_ACK_WITH_ERR) { + u16 err = FLD_GET(val, 23, 8); + dsi_show_rx_ack_with_err(err); + return -EIO; + + } else if (dt == DSI_DT_RX_SHORT_READ_1) { + u8 data = FLD_GET(val, 15, 8); + if (dsi.debug_read) + DSSDBG("\tDCS short response, 1 byte: %02x\n", data); + + if (buflen < 1) + return -EIO; + + buf[0] = data; + + return 1; + } else if (dt == DSI_DT_RX_SHORT_READ_2) { + u16 data = FLD_GET(val, 23, 8); + if (dsi.debug_read) + DSSDBG("\tDCS short response, 2 byte: %04x\n", data); + + if (buflen < 2) + return -EIO; + + buf[0] = data & 0xff; + buf[1] = (data >> 8) & 0xff; + + return 2; + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { + int w; + int len = FLD_GET(val, 23, 8); + if (dsi.debug_read) + DSSDBG("\tDCS long response, len %d\n", len); + + if (len > buflen) + return -EIO; + + /* two byte checksum ends the packet, not included in len */ + for (w = 0; w < len + 2;) { + int b; + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi.debug_read) + DSSDBG("\t\t%02x %02x %02x %02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); + + for (b = 0; b < 4; ++b) { + if (w < len) + buf[w] = (val >> (b * 8)) & 0xff; + /* we discard the 2 byte checksum */ + ++w; + } + } + + return len; + + } else { + DSSERR("\tunknown datatype 0x%02x\n", dt); + return -EIO; + } +} +EXPORT_SYMBOL(dsi_vc_dcs_read); + + +int dsi_vc_set_max_rx_packet_size(int channel, u16 len) +{ + return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + len, 0); +} +EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); + + +static int dsi_set_lp_rx_timeout(int ns, int x4, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("LP_TX_TO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING2); + r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ + r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */ + r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */ + r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ + dsi_write_reg(DSI_TIMING2, r); + + DSSDBG("LP_RX_TO %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} + +static int dsi_set_ta_timeout(int ns, int x8, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("TA_TO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ + r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */ + r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */ + r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ + dsi_write_reg(DSI_TIMING1, r); + + DSSDBG("TA_TO %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} + +static int dsi_set_stop_state_counter(int ns, int x4, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("STOP_STATE_COUNTER_IO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING1); + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */ + r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */ + r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ + dsi_write_reg(DSI_TIMING1, r); + + DSSDBG("STOP_STATE_COUNTER %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} + +static int dsi_set_hs_tx_timeout(int ns, int x4, int x16) +{ + u32 r; + unsigned long fck; + int ticks; + + /* ticks in TxByteClkHS */ + + fck = dsi.ddr_clk / 4; + ticks = (fck / 1000 / 1000) * ns / 1000; + + if (ticks > 0x1fff) { + DSSERR("HS_TX_TO too high\n"); + return -EINVAL; + } + + r = dsi_read_reg(DSI_TIMING2); + r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ + r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */ + r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */ + r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ + dsi_write_reg(DSI_TIMING2, r); + + DSSDBG("HS_TX_TO %ld ns (%#x ticks)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks); + + return 0; +} +static int dsi_proto_config(struct omap_dss_device *dssdev) +{ + u32 r; + int buswidth = 0; + int div; + + dsi_config_tx_fifo(DSI_FIFO_SIZE_128, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0); + + dsi_config_rx_fifo(DSI_FIFO_SIZE_128, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0, + DSI_FIFO_SIZE_0); + + /* XXX what values for the timeouts? */ + dsi_set_stop_state_counter(1000, 0, 0); + + dsi_set_ta_timeout(50000, 1, 1); + + /* 3000ns * 16 */ + dsi_set_lp_rx_timeout(3000, 0, 1); + + /* 10000ns * 4 */ + dsi_set_hs_tx_timeout(10000, 1, 0); + + switch (dssdev->ctrl.pixel_size) { + case 16: + buswidth = 0; + break; + case 18: + buswidth = 1; + break; + case 24: + buswidth = 2; + break; + default: + BUG(); + } + + r = dsi_read_reg(DSI_CTRL); + r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ + r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ + r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ + + /* TODO: Change for LCD2 support */ + div = dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD) / + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD); + r = FLD_MOD(r, div == 2 ? 0 : 1, 4, 4); /* VP_CLK_RATIO */ + r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */ + r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ + r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ + r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ + r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ + r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ + r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */ + + dsi_write_reg(DSI_CTRL, r); + + dsi_vc_initial_config(0); + + /* set all vc targets to peripheral 0 */ + dsi.vc[0].dest_per = 0; + dsi.vc[1].dest_per = 0; + dsi.vc[2].dest_per = 0; + dsi.vc[3].dest_per = 0; + + return 0; +} + +static void dsi_proto_timings(struct omap_dss_device *dssdev) +{ + unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail; + unsigned tclk_pre, tclk_post; + unsigned ths_prepare, ths_prepare_ths_zero, ths_zero; + unsigned ths_trail, ths_exit; + unsigned ddr_clk_pre, ddr_clk_post; + unsigned enter_hs_mode_lat, exit_hs_mode_lat; + unsigned ths_eot; + u32 r; + + r = dsi_read_reg(DSI_DSIPHY_CFG0); + ths_prepare = FLD_GET(r, 31, 24); + ths_prepare_ths_zero = FLD_GET(r, 23, 16); + ths_zero = ths_prepare_ths_zero - ths_prepare; + ths_trail = FLD_GET(r, 15, 8); + ths_exit = FLD_GET(r, 7, 0); + + r = dsi_read_reg(DSI_DSIPHY_CFG1); + tlpx = FLD_GET(r, 22, 16) * 2; + tclk_trail = FLD_GET(r, 15, 8); + tclk_zero = FLD_GET(r, 7, 0); + + r = dsi_read_reg(DSI_DSIPHY_CFG2); + tclk_prepare = FLD_GET(r, 7, 0); + + /* min 8*UI */ + tclk_pre = 20; + /* min 60ns + 52*UI */ + tclk_post = ns2ddr(60) + 26; + + /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */ + if (dssdev->phy.dsi.data1_lane != 0 && + dssdev->phy.dsi.data2_lane != 0) + ths_eot = 2; + else + ths_eot = 4; + + ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare, + 4); + ddr_clk_post = DIV_ROUND_UP(tclk_post + tclk_trail, 4) + ths_eot; + + BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); + BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); + + r = dsi_read_reg(DSI_CLK_TIMING); + r = FLD_MOD(r, ddr_clk_pre, 15, 8); + r = FLD_MOD(r, ddr_clk_post, 7, 0); + dsi_write_reg(DSI_CLK_TIMING, r); + + DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", + ddr_clk_pre, + ddr_clk_post); + + enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) + + DIV_ROUND_UP(ths_prepare, 4) + + DIV_ROUND_UP(ths_zero + 3, 4); + + exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot; + + r = FLD_VAL(enter_hs_mode_lat, 31, 16) | + FLD_VAL(exit_hs_mode_lat, 15, 0); + dsi_write_reg(DSI_VM_TIMING7, r); + + DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", + enter_hs_mode_lat, exit_hs_mode_lat); +} + + +#define DSI_DECL_VARS \ + int __dsi_cb = 0; u32 __dsi_cv = 0; + +#define DSI_FLUSH(ch) \ + if (__dsi_cb > 0) { \ + /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ + __dsi_cb = __dsi_cv = 0; \ + } + +#define DSI_PUSH(ch, data) \ + do { \ + __dsi_cv |= (data) << (__dsi_cb * 8); \ + /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ + if (++__dsi_cb > 3) \ + DSI_FLUSH(ch); \ + } while (0) + +static int dsi_update_screen_l4(struct omap_dss_device *dssdev, + int x, int y, int w, int h) +{ + /* Note: supports only 24bit colors in 32bit container */ + int first = 1; + int fifo_stalls = 0; + int max_dsi_packet_size; + int max_data_per_packet; + int max_pixels_per_packet; + int pixels_left; + int bytespp = dssdev->ctrl.pixel_size / 8; + int scr_width; + u32 __iomem *data; + int start_offset; + int horiz_inc; + int current_x; + struct omap_overlay *ovl; + + debug_irq = 0; + + DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n", + x, y, w, h); + + ovl = dssdev->manager->overlays[0]; + + if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U) + return -EINVAL; + + if (dssdev->ctrl.pixel_size != 24) + return -EINVAL; + + scr_width = ovl->info.screen_width; + data = ovl->info.vaddr; + + start_offset = scr_width * y + x; + horiz_inc = scr_width - w; + current_x = x; + + /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes + * in fifo */ + + /* When using CPU, max long packet size is TX buffer size */ + max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; + + /* we seem to get better perf if we divide the tx fifo to half, + and while the other half is being sent, we fill the other half + max_dsi_packet_size /= 2; */ + + max_data_per_packet = max_dsi_packet_size - 4 - 1; + + max_pixels_per_packet = max_data_per_packet / bytespp; + + DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet); + + pixels_left = w * h; + + DSSDBG("total pixels %d\n", pixels_left); + + data += start_offset; + + while (pixels_left > 0) { + /* 0x2c = write_memory_start */ + /* 0x3c = write_memory_continue */ + u8 dcs_cmd = first ? 0x2c : 0x3c; + int pixels; + DSI_DECL_VARS; + first = 0; + +#if 1 + /* using fifo not empty */ + /* TX_FIFO_NOT_EMPTY */ + while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { + udelay(1); + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + return -EIO; + } + } +#elif 1 + /* using fifo emptiness */ + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < + max_dsi_packet_size) { + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + return -EIO; + } + } +#else + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) { + fifo_stalls++; + if (fifo_stalls > 0xfffff) { + DSSERR("fifo stalls overflow, pixels left %d\n", + pixels_left); + dsi_if_enable(0); + return -EIO; + } + } +#endif + pixels = min(max_pixels_per_packet, pixels_left); + + pixels_left -= pixels; + + dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, + 1 + pixels * bytespp, 0); + + DSI_PUSH(0, dcs_cmd); + + while (pixels-- > 0) { + u32 pix = __raw_readl(data++); + + DSI_PUSH(0, (pix >> 16) & 0xff); + DSI_PUSH(0, (pix >> 8) & 0xff); + DSI_PUSH(0, (pix >> 0) & 0xff); + + current_x++; + if (current_x == x+w) { + current_x = x; + data += horiz_inc; + } + } + + DSI_FLUSH(0); + } + + return 0; +} + +static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + int bytespp = dssdev->ctrl.pixel_size / 8; + int len; + int total_len; + int packet_payload; + int packet_len; + u32 l; + bool use_te_trigger; + const int channel = 0; + + use_te_trigger = dsi.te_enabled && !dsi.use_ext_te; + + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) + DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", + x, y, w, h); + + len = w * h * bytespp; + + /* XXX: one packet could be longer, I think? Line buffer is + * 1024 x 24bits, but we have to put DCS cmd there also. + * 1023 * 3 should work, but causes strange color effects. */ + packet_payload = min(w, (u16)1020) * bytespp; + + packet_len = packet_payload + 1; /* 1 byte for DCS cmd */ + total_len = (len / packet_payload) * packet_len; + + if (len % packet_payload) + total_len += (len % packet_payload) + 1; + + if (0) + dsi_vc_print_status(1); + + l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ + dsi_write_reg(DSI_VC_TE(channel), l); + + dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0); + + if (use_te_trigger) + l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ + else + l = FLD_MOD(l, 1, 31, 31); /* TE_START */ + dsi_write_reg(DSI_VC_TE(channel), l); + + /* We put SIDLEMODE to no-idle for the duration of the transfer, + * because DSS interrupts are not capable of waking up the CPU and the + * framedone interrupt could be delayed for quite a long time. I think + * the same goes for any DSS interrupts, but for some reason I have not + * seen the problem anywhere else than here. + */ + dispc_disable_sidle(); + + dss_start_update(dssdev); + + if (use_te_trigger) { + /* disable LP_RX_TO, so that we can receive TE. Time to wait + * for TE is longer than the timer allows */ + REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ + + dsi_vc_send_bta(channel); + } +} + +static void dsi_framedone_irq_callback(void *data, u32 mask) +{ + /* Note: We get FRAMEDONE when DISPC has finished sending pixels and + * turns itself off. However, DSI still has the pixels in its buffers, + * and is sending the data. + */ + + /* SIDLEMODE back to smart-idle */ + dispc_enable_sidle(); + + dsi.framedone_received = true; + wake_up(&dsi.waitqueue); +} + +static void dsi_set_update_region(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + spin_lock(&dsi.update_lock); + if (dsi.update_region.dirty) { + dsi.update_region.x = min(x, dsi.update_region.x); + dsi.update_region.y = min(y, dsi.update_region.y); + dsi.update_region.w = max(w, dsi.update_region.w); + dsi.update_region.h = max(h, dsi.update_region.h); + } else { + dsi.update_region.x = x; + dsi.update_region.y = y; + dsi.update_region.w = w; + dsi.update_region.h = h; + } + + dsi.update_region.device = dssdev; + dsi.update_region.dirty = true; + + spin_unlock(&dsi.update_lock); + +} + +static void dsi_start_auto_update(struct omap_dss_device *dssdev) +{ + u16 w, h; + int i; + + DSSDBG("starting auto update\n"); + + /* In automatic mode the overlay settings are applied like on DPI/SDI. + * Mark the overlays dirty, so that we get the overlays configured, as + * manual mode has left them in bad shape after config partia planes */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + if (ovl->manager == dssdev->manager) + ovl->info_dirty = true; + } + dssdev->manager->apply(dssdev->manager); + + dssdev->get_resolution(dssdev, &w, &h); + + dsi_set_update_region(dssdev, 0, 0, w, h); + + dsi_perf_mark_start_auto(); + + wake_up(&dsi.waitqueue); +} + +static int dsi_set_te(struct omap_dss_device *dssdev, bool enable) +{ + dssdev->driver->enable_te(dssdev, enable); + int r; + printk(KERN_INFO "\n dsi_set_te "); + r = dssdev->driver->enable_te(dssdev, enable); + printk(KERN_INFO "\n dsi_set_te DONE "); + /* XXX for some reason, DSI TE breaks if we don't wait here. + * Panel bug? Needs more studying */ + msleep(100); + return r; +} + +static void dsi_handle_framedone(void) +{ + int r; + const int channel = 0; + bool use_te_trigger; + + use_te_trigger = dsi.te_enabled && !dsi.use_ext_te; + + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) + DSSDBG("FRAMEDONE\n"); + + if (use_te_trigger) { + /* enable LP_RX_TO again after the TE */ + REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ + } + + /* Send BTA after the frame. We need this for the TE to work, as TE + * trigger is only sent for BTAs without preceding packet. Thus we need + * to BTA after the pixel packets so that next BTA will cause TE + * trigger. + * + * This is not needed when TE is not in use, but we do it anyway to + * make sure that the transfer has been completed. It would be more + * optimal, but more complex, to wait only just before starting next + * transfer. */ + r = dsi_vc_send_bta_sync(channel); + if (r) + DSSERR("BTA after framedone failed\n"); + +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC + dispc_fake_vsync_irq(); +#endif +} + +static int dsi_update_thread(void *data) + { + unsigned long timeout; + struct omap_dss_device *device; + u16 x, y, w, h; + + while (1) { + bool sched; + + wait_event_interruptible(dsi.waitqueue, + dsi.update_mode == OMAP_DSS_UPDATE_AUTO || + (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL && + dsi.update_region.dirty == true) || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + dsi_bus_lock(); + + if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED || + kthread_should_stop()) { + dsi_bus_unlock(); + break; + } + + dsi_perf_mark_setup(); + + if (dsi.update_region.dirty) { + spin_lock(&dsi.update_lock); + dsi.active_update_region = dsi.update_region; + dsi.update_region.dirty = false; + spin_unlock(&dsi.update_lock); + } + + device = dsi.active_update_region.device; + x = dsi.active_update_region.x; + y = dsi.active_update_region.y; + w = dsi.active_update_region.w; + h = dsi.active_update_region.h; + + if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + + if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL) { + dss_setup_partial_planes(device, + &x, &y, &w, &h); +#if 1 + /* XXX there seems to be a bug in this driver + * or OMAP hardware. Some updates with certain + * widths and x coordinates fail. These widths + * are always odd, so "fix" it here for now */ + if (w & 1) { + u16 dw, dh; + device->get_resolution(device, + &dw, &dh); + if (x + w == dw) + x &= ~1; + ++w; + dss_setup_partial_planes(device, + &x, &y, &w, &h); + } +#endif + } + + dispc_set_lcd_size(OMAP_DSS_CHANNEL_LCD, w, h); + /* TODO: Correct this while adding support for LCD2 */ + } + + if (dsi.active_update_region.dirty) { + dsi.active_update_region.dirty = false; + /* XXX TODO we don't need to send the coords, if they + * are the same that are already programmed to the + * panel. That should speed up manual update a bit */ + device->driver->setup_update(device, x, y, w, h); + } + + dsi_perf_mark_start(); + + if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + dsi_vc_config_vp(0); + + if (dsi.te_enabled && dsi.use_ext_te) + device->driver->wait_for_te(device); + + dsi.framedone_received = false; + + dsi_update_screen_dispc(device, x, y, w, h); + + /* wait for framedone */ + timeout = msecs_to_jiffies(1000); + timeout = wait_event_timeout(dsi.waitqueue, + dsi.framedone_received == true, + timeout); + + if (timeout == 0) { + DSSERR("framedone timeout\n"); + DSSERR("failed update %d,%d %dx%d\n", + x, y, w, h); + + dispc_enable_sidle(); + /* TODO: update for LCD2 support */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + } else { + dsi_handle_framedone(); + dsi_perf_show("DISPC"); + } + } else { + dsi_update_screen_l4(device, x, y, w, h); + dsi_perf_show("L4"); + } + + sched = atomic_read(&dsi.bus_lock.count) < 0; + + complete_all(&dsi.update_completion); + + dsi_bus_unlock(); + + /* XXX We need to give others chance to get the bus lock. Is + * there a better way for this? */ + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO && sched) + schedule_timeout_interruptible(1); + } + + DSSDBG("update thread exiting\n"); + + return 0; + } + + +/* Display funcs */ + +static int dsi_display_init_dispc(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL, + DISPC_IRQ_FRAMEDONE); + if (r) { + DSSERR("can't get FRAMEDONE irq\n"); + return r; + } + /* TODO: Change here for LCD2 support*/ + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_LCD_DISPLAY_TFT); + + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_PARALLELMODE_DSI); + dispc_enable_fifohandcheck(1); + + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD, dssdev->ctrl.pixel_size); + + { + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + }; + + dispc_set_lcd_timings(OMAP_DSS_CHANNEL_LCD, &timings); + } + + return 0; +} + +static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev) +{ + omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL, + DISPC_IRQ_FRAMEDONE); +} + +static int dsi_display_init_dsi(struct omap_dss_device *dssdev) +{ + struct dsi_clock_info cinfo; + int r; + + _dsi_print_reset_status(); + + r = dsi_pll_init(1, 0); + if (r) + goto err0; + + r = dsi_pll_calc_ddrfreq(dssdev->phy.dsi.ddr_clk_hz, &cinfo); + if (r) + goto err1; + + r = dsi_pll_program(&cinfo); + if (r) + goto err1; + + DSSDBG("PLL OK\n"); + + r = dsi_complexio_init(dssdev); + if (r) + goto err1; + + _dsi_print_reset_status(); + + dsi_proto_timings(dssdev); + dsi_set_lp_clk_divisor(dssdev); + + if (1) + _dsi_print_reset_status(); + + r = dsi_proto_config(dssdev); + if (r) + goto err2; + + /* enable interface */ + dsi_vc_enable(0, 1); + dsi_if_enable(1); + dsi_force_tx_stop_mode_io(); + + if (dssdev->driver->enable) { + r = dssdev->driver->enable(dssdev); + if (r) + goto err3; + } + + /* enable high-speed after initial config */ + dsi_vc_enable_hs(0, 1); + + return 0; +err3: + dsi_if_enable(0); +err2: + dsi_complexio_uninit(); +err1: + dsi_pll_uninit(); +err0: + return r; +} + +static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) +{ + if (dssdev->driver->disable) + dssdev->driver->disable(dssdev); + + dsi_complexio_uninit(); + dsi_pll_uninit(); +} + +static int dsi_core_init(void) +{ + /* Autoidle */ + REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0); + + /* ENWAKEUP */ + REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2); + + /* SIDLEMODE smart-idle */ + REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3); + + _dsi_initialize_irq(); + + return 0; +} + +static int dsi_display_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("dsi_display_enable\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("dssdev already enabled\n"); + r = -EINVAL; + goto err1; + } + + enable_clocks(1); + dsi_enable_pll_clock(1); + + r = _dsi_reset(); + if (r) + goto err2; + + dsi_core_init(); + + r = dsi_display_init_dispc(dssdev); + if (r) + goto err2; + + r = dsi_display_init_dsi(dssdev); + if (r) + goto err3; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + dsi.use_ext_te = dssdev->phy.dsi.ext_te; + r = dsi_set_te(dssdev, dsi.te_enabled); + if (r) + goto err3; + + dsi.update_mode = dsi.user_update_mode; + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) + dsi_start_auto_update(dssdev); + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; + +err3: + dsi_display_uninit_dispc(dssdev); +err2: + enable_clocks(0); + dsi_enable_pll_clock(0); +err1: + omap_dss_stop_device(dssdev); +err0: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + DSSDBG("dsi_display_enable FAILED\n"); + return r; +} + +static void dsi_display_disable(struct omap_dss_device *dssdev) +{ + DSSDBG("dsi_display_disable\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + goto end; + + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + dsi_display_uninit_dispc(dssdev); + + dsi_display_uninit_dsi(dssdev); + + enable_clocks(0); + dsi_enable_pll_clock(0); + + omap_dss_stop_device(dssdev); +end: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); +} + +static int dsi_display_suspend(struct omap_dss_device *dssdev) +{ + DSSDBG("dsi_display_suspend\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + goto end; + + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + dsi_display_uninit_dispc(dssdev); + + dsi_display_uninit_dsi(dssdev); + + enable_clocks(0); + dsi_enable_pll_clock(0); +end: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; +} + +static int dsi_display_resume(struct omap_dss_device *dssdev) +{ + int r; + + DSSDBG("dsi_display_resume\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + DSSERR("dssdev not suspended\n"); + r = -EINVAL; + goto err0; + } + + enable_clocks(1); + dsi_enable_pll_clock(1); + + r = _dsi_reset(); + if (r) + goto err1; + + dsi_core_init(); + + r = dsi_display_init_dispc(dssdev); + if (r) + goto err1; + + r = dsi_display_init_dsi(dssdev); + if (r) + goto err2; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + r = dsi_set_te(dssdev, dsi.te_enabled); + if (r) + goto err2; + + dsi.update_mode = dsi.user_update_mode; + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) + dsi_start_auto_update(dssdev); + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; + +err2: + dsi_display_uninit_dispc(dssdev); +err1: + enable_clocks(0); + dsi_enable_pll_clock(0); +err0: + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + DSSDBG("dsi_display_resume FAILED\n"); + return r; +} + +static int dsi_display_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + int r = 0; + u16 dw, dh; + + DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h); + + mutex_lock(&dsi.lock); + + if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL) + goto end; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto end; + + dssdev->get_resolution(dssdev, &dw, &dh); + + if (x > dw || y > dh) + goto end; + + if (x + w > dw) + w = dw - x; + + if (y + h > dh) + h = dh - y; + + if (w == 0 || h == 0) + goto end; + + dsi_set_update_region(dssdev, x, y, w, h); + + wake_up(&dsi.waitqueue); + +end: + mutex_unlock(&dsi.lock); + + return r; +} + +static int dsi_display_sync(struct omap_dss_device *dssdev) +{ + bool wait; + + DSSDBG("dsi_display_sync()\n"); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL && + dsi.update_region.dirty) { + INIT_COMPLETION(dsi.update_completion); + wait = true; + } else { + wait = false; + } + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + if (wait) + wait_for_completion_interruptible(&dsi.update_completion); + + DSSDBG("dsi_display_sync() done\n"); + return 0; +} + +static int dsi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + DSSDBGF("%d", mode); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + if (dsi.update_mode != mode) { + dsi.user_update_mode = mode; + dsi.update_mode = mode; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE && + mode == OMAP_DSS_UPDATE_AUTO) + dsi_start_auto_update(dssdev); + } + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; +} + +static enum omap_dss_update_mode dsi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return dsi.update_mode; +} + + +static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable) +{ + int r = 0; + DSSDBGF("%d", enable); + + if (!dssdev->driver->enable_te) + return -ENOENT; + + dsi_bus_lock(); + + dsi.te_enabled = enable; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto end; + + r = dsi_set_te(dssdev, enable); +end: + dsi_bus_unlock(); + + return r; +} + +static int dsi_display_get_te(struct omap_dss_device *dssdev) +{ + return dsi.te_enabled; +} + +static int dsi_display_set_rotate(struct omap_dss_device *dssdev, u8 rotate) +{ + + DSSDBGF("%d", rotate); + + if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) + return -EINVAL; + + dsi_bus_lock(); + dssdev->driver->set_rotate(dssdev, rotate); + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { + u16 w, h; + /* the display dimensions may have changed, so set a new + * update region */ + dssdev->get_resolution(dssdev, &w, &h); + dsi_set_update_region(dssdev, 0, 0, w, h); + } + dsi_bus_unlock(); + + return 0; +} + +static u8 dsi_display_get_rotate(struct omap_dss_device *dssdev) +{ + if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) + return 0; + + return dssdev->driver->get_rotate(dssdev); +} + +static int dsi_display_set_mirror(struct omap_dss_device *dssdev, bool mirror) +{ + DSSDBGF("%d", mirror); + + if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) + return -EINVAL; + + dsi_bus_lock(); + dssdev->driver->set_mirror(dssdev, mirror); + dsi_bus_unlock(); + + return 0; +} + +static bool dsi_display_get_mirror(struct omap_dss_device *dssdev) +{ + if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) + return 0; + + return dssdev->driver->get_mirror(dssdev); +} + +static int dsi_display_run_test(struct omap_dss_device *dssdev, int test_num) +{ + int r; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EIO; + + DSSDBGF("%d", test_num); + + dsi_bus_lock(); + + /* run test first in low speed mode */ + dsi_vc_enable_hs(0, 0); + + if (dssdev->driver->run_test) { + r = dssdev->driver->run_test(dssdev, test_num); + if (r) + goto end; + } + + /* then in high speed */ + dsi_vc_enable_hs(0, 1); + + if (dssdev->driver->run_test) { + r = dssdev->driver->run_test(dssdev, test_num); + if (r) + goto end; + } + +end: + dsi_vc_enable_hs(0, 1); + + dsi_bus_unlock(); + + return r; +} + +static int dsi_display_memory_read(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h) +{ + int r; + + DSSDBGF(""); + + if (!dssdev->driver->memory_read) + return -EINVAL; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EIO; + + dsi_bus_lock(); + + r = dssdev->driver->memory_read(dssdev, buf, size, + x, y, w, h); + + dsi_bus_unlock(); + + return r; +} + +void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high) +{ + unsigned burst_size_bytes; + + *burst_size = OMAP_DSS_BURST_16x32; + burst_size_bytes = 16 * 32 / 8; + + *fifo_high = fifo_size - burst_size_bytes; + *fifo_low = 0; +} + +int dsi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("DSI init\n"); + + dssdev->enable = dsi_display_enable; + dssdev->disable = dsi_display_disable; + dssdev->suspend = dsi_display_suspend; + dssdev->resume = dsi_display_resume; + dssdev->update = dsi_display_update; + dssdev->sync = dsi_display_sync; + dssdev->set_update_mode = dsi_display_set_update_mode; + dssdev->get_update_mode = dsi_display_get_update_mode; + dssdev->enable_te = dsi_display_enable_te; + dssdev->get_te = dsi_display_get_te; + + dssdev->get_rotate = dsi_display_get_rotate; + dssdev->set_rotate = dsi_display_set_rotate; + + dssdev->get_mirror = dsi_display_get_mirror; + dssdev->set_mirror = dsi_display_set_mirror; + + dssdev->run_test = dsi_display_run_test; + dssdev->memory_read = dsi_display_memory_read; + + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + + dsi.vc[0].dssdev = dssdev; + dsi.vc[1].dssdev = dssdev; + + return 0; +} + +int dsi_init(struct platform_device *pdev) +{ + u32 rev; + struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO-1 + }; + + spin_lock_init(&dsi.errors_lock); + dsi.errors = 0; + + /* XXX fail properly */ + + init_completion(&dsi.bta_completion); + init_completion(&dsi.update_completion); + + dsi.thread = kthread_create(dsi_update_thread, NULL, "dsi"); + if (IS_ERR(dsi.thread)) { + DSSERR("cannot create kthread\n"); + return PTR_ERR(dsi.thread); + } + sched_setscheduler(dsi.thread, SCHED_FIFO, ¶m); + + init_waitqueue_head(&dsi.waitqueue); + spin_lock_init(&dsi.update_lock); + + mutex_init(&dsi.lock); + mutex_init(&dsi.bus_lock); + + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; + dsi.user_update_mode = OMAP_DSS_UPDATE_DISABLED; + + dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS); + if (!dsi.base) { + DSSERR("can't ioremap DSI\n"); + return -ENOMEM; + } + + dsi.vdds_dsi_reg = regulator_get(&pdev->dev, "vdds_dsi"); + if (IS_ERR(dsi.vdds_dsi_reg)) { + iounmap(dsi.base); + DSSERR("can't get VDDS_DSI regulator\n"); + return PTR_ERR(dsi.vdds_dsi_reg); + } + + enable_clocks(1); + + rev = dsi_read_reg(DSI_REVISION); + printk(KERN_INFO "OMAP DSI rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + enable_clocks(0); + + wake_up_process(dsi.thread); + + return 0; +} + +void dsi_exit(void) +{ + kthread_stop(dsi.thread); + + regulator_put(dsi.vdds_dsi_reg); + + iounmap(dsi.base); + + DSSDBG("omap_dsi_exit\n"); +} + diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c new file mode 100644 index 000000000000..211db7dd3f9b --- /dev/null +++ b/drivers/video/omap2/dss/dss.c @@ -0,0 +1,370 @@ +/* + * linux/drivers/video/omap2/dss/dss.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "DSS" + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/seq_file.h> + +#include <mach/display.h> +#include "dss.h" + +#ifndef CONFIG_ARCH_OMAP4 +/* DSS */ +#define DSS_BASE 0x48050000 +/* DISPLAY CONTROLLER */ +#define DISPC_BASE 0x48050400 +#else +/* DSS */ +#define DSS_BASE 0x48040000 +/* DISPLAY CONTROLLER */ +#define DISPC_BASE 0x48041000 +#endif + +#define DSS_SZ_REGS SZ_512 + +struct dss_reg { + u16 idx; +}; + +#define DSS_REG(idx) ((const struct dss_reg) { idx }) + +#define DSS_REVISION DSS_REG(0x0000) +#define DSS_SYSCONFIG DSS_REG(0x0010) +#define DSS_SYSSTATUS DSS_REG(0x0014) +#define DSS_CONTROL DSS_REG(0x0040) +#define DSS_SDI_CONTROL DSS_REG(0x0044) +#define DSS_PLL_CONTROL DSS_REG(0x0048) +#define DSS_SDI_STATUS DSS_REG(0x005C) + +#ifdef CONFIG_ARCH_OMAP4 +#define DSS_STATUS DSS_REG(0x005C) +#endif + + + +#define REG_GET(idx, start, end) \ + FLD_GET(dss_read_reg(idx), start, end) + +#define REG_FLD_MOD(idx, val, start, end) \ + dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) + +static struct { + void __iomem *base; + + u32 ctx[DSS_SZ_REGS / sizeof(u32)]; +} dss; + +static int _omap_dss_wait_reset(void); + +static inline void dss_write_reg(const struct dss_reg idx, u32 val) +{ + __raw_writel(val, dss.base + idx.idx); +} + +static inline u32 dss_read_reg(const struct dss_reg idx) +{ + return __raw_readl(dss.base + idx.idx); +} + +#define SR(reg) \ + dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg) +#define RR(reg) \ + dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) + +void dss_save_context(void) +{ + if (cpu_is_omap24xx()) + return; + + SR(SYSCONFIG); + SR(CONTROL); + +#ifdef CONFIG_OMAP2_DSS_SDI + SR(SDI_CONTROL); + SR(PLL_CONTROL); +#endif +} + +void dss_restore_context(void) +{ + if (_omap_dss_wait_reset()) + DSSERR("DSS not coming out of reset after sleep\n"); + + RR(SYSCONFIG); + RR(CONTROL); + +#ifdef CONFIG_OMAP2_DSS_SDI + RR(SDI_CONTROL); + RR(PLL_CONTROL); +#endif +} + +#undef SR +#undef RR + +void dss_sdi_init(u8 datapairs) +{ + u32 l; + + BUG_ON(datapairs > 3 || datapairs < 1); + + l = dss_read_reg(DSS_SDI_CONTROL); + l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */ + l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */ + l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */ + dss_write_reg(DSS_SDI_CONTROL, l); + + l = dss_read_reg(DSS_PLL_CONTROL); + l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */ + l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */ + l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */ + dss_write_reg(DSS_PLL_CONTROL, l); +} + +void dss_sdi_enable(void) +{ + dispc_pck_free_enable(1); + + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ + udelay(1); /* wait 2x PCLK */ + + /* Lock SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ + + /* Waiting for PLL lock request to complete */ + while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) + ; + + /* Clearing PLL_GO bit */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28); + + /* Waiting for PLL to lock */ + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) + ; + + dispc_lcd_enable_signal(1); + + /* Waiting for SDI reset to complete */ + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) + ; +} + +void dss_sdi_disable(void) +{ + dispc_lcd_enable_signal(0); + + dispc_pck_free_enable(0); + + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ +} + +void dss_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(DSS_REVISION); + DUMPREG(DSS_SYSCONFIG); + DUMPREG(DSS_SYSSTATUS); + DUMPREG(DSS_CONTROL); + DUMPREG(DSS_SDI_CONTROL); + DUMPREG(DSS_PLL_CONTROL); + DUMPREG(DSS_SDI_STATUS); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +void dss_select_clk_source(bool dsi, bool dispc) +{ + u32 r; + r = dss_read_reg(DSS_CONTROL); + r = FLD_MOD(r, dsi, 1, 1); /* DSI_CLK_SWITCH */ + r = FLD_MOD(r, dispc, 0, 0); /* DISPC_CLK_SWITCH */ + /* TODO: extend for LCD2 and HDMI */ + dss_write_reg(DSS_CONTROL, r); +} + +int dss_get_dsi_clk_source(void) +{ + return FLD_GET(dss_read_reg(DSS_CONTROL), 1, 1); +} + +int dss_get_dispc_clk_source(void) +{ + return FLD_GET(dss_read_reg(DSS_CONTROL), 0, 0); +} + +static irqreturn_t dss_irq_handler_omap2(int irq, void *arg) +{ + dispc_irq_handler(); + + return IRQ_HANDLED; +} + +static irqreturn_t dss_irq_handler_omap3(int irq, void *arg) +{ + /* INT_24XX_DSS_IRQ is dedicated for DISPC interrupt request only */ + /* DSI1, DSI2 and HDMI to be handled in seperate handlers */ + dispc_irq_handler(); + + return IRQ_HANDLED; +} + +static int _omap_dss_wait_reset(void) +{ + unsigned timeout = 1000; + + while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) { + udelay(1); + if (!--timeout) { + DSSERR("soft reset failed\n"); + return -ENODEV; + } + } + + return 0; +} + +static int _omap_dss_reset(void) +{ + /* Soft reset */ + REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1); + return _omap_dss_wait_reset(); +} + +void dss_set_venc_output(enum omap_dss_venc_type type) +{ + int l = 0; + + if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) + l = 0; + else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) + l = 1; + else + BUG(); + + /* venc out selection. 0 = comp, 1 = svideo */ + REG_FLD_MOD(DSS_CONTROL, l, 6, 6); +} + +void dss_set_dac_pwrdn_bgz(bool enable) +{ + REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ +} + +void dss_switch_tv_hdmi(int hdmi) +{ + REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* 0x1 for HDMI, 0x0 TV */ +} + +int dss_init(bool skip_init) +{ + int r; + u32 rev; + + dss.base = ioremap(DSS_BASE, DSS_SZ_REGS); + if (!dss.base) { + DSSERR("can't ioremap DSS\n"); + r = -ENOMEM; + goto fail0; + } + + if (!skip_init) { + /* disable LCD and DIGIT output. This seems to fix the synclost + * problem that we get, if the bootloader starts the DSS and + * the kernel resets it */ + omap_writel(omap_readl(DISPC_BASE + 0x40) & ~0x3, (DISPC_BASE + 0x40)); + + /* We need to wait here a bit, otherwise we sometimes start to + * get synclost errors, and after that only power cycle will + * restore DSS functionality. I have no idea why this happens. + * And we have to wait _before_ resetting the DSS, but after + * enabling clocks. + */ + msleep(50); +#ifndef CONFIG_ARCH_OMAP4 + _omap_dss_reset(); +#endif + } +#ifndef CONFIG_ARCH_OMAP4 + /* autoidle */ + REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); +#endif + /* Select DPLL */ + REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); + +#ifdef CONFIG_OMAP2_DSS_VENC + REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */ + REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ + REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ +#endif + +#ifndef CONFIG_ARCH_OMAP4 + r = request_irq(INT_24XX_DSS_IRQ, + cpu_is_omap24xx() + ? dss_irq_handler_omap2 + : dss_irq_handler_omap3, + 0, "OMAP DSS", NULL); +#else + r = request_irq(INT_44XX_DSS_IRQ, + dss_irq_handler_omap3, + 0, "OMAP DSS", (void *)1); +#endif + if (r < 0) { + DSSERR("omap2 dss: request_irq failed\n"); + goto fail1; + } + + dss_save_context(); + + rev = dss_read_reg(DSS_REVISION); + printk(KERN_INFO "OMAP DSS rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + return 0; + +fail1: + iounmap(dss.base); +fail0: + return r; +} + +void dss_exit(void) +{ +#ifndef CONFIG_ARCH_OMAP4 + free_irq(INT_24XX_DSS_IRQ, NULL); +#else + free_irq(INT_44XX_DSS_IRQ, NULL); +#endif + + iounmap(dss.base); +} + diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h new file mode 100644 index 000000000000..53cde16684c5 --- /dev/null +++ b/drivers/video/omap2/dss/dss.h @@ -0,0 +1,387 @@ +/* + * linux/drivers/video/omap2/dss/dss.h + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAP2_DSS_H +#define __OMAP2_DSS_H + +#ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT +#define DEBUG +#endif + +#ifdef DEBUG +extern unsigned int dss_debug; +#ifdef DSS_SUBSYS_NAME +#define DSSDBG(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSDBG(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSDBGF(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME \ + ": %s(" format ")\n", \ + __func__, \ + ## __VA_ARGS__) +#else +#define DSSDBGF(format, ...) \ + if (dss_debug) \ + printk(KERN_DEBUG "omapdss: " \ + ": %s(" format ")\n", \ + __func__, \ + ## __VA_ARGS__) +#endif + +#else /* DEBUG */ +#define DSSDBG(format, ...) +#define DSSDBGF(format, ...) +#endif + + +#ifdef DSS_SUBSYS_NAME +#define DSSERR(format, ...) \ + printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \ + ## __VA_ARGS__) +#else +#define DSSERR(format, ...) \ + printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSINFO(format, ...) \ + printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSINFO(format, ...) \ + printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__) +#endif + +#ifdef DSS_SUBSYS_NAME +#define DSSWARN(format, ...) \ + printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \ + ## __VA_ARGS__) +#else +#define DSSWARN(format, ...) \ + printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__) +#endif + +/* OMAP TRM gives bitfields as start:end, where start is the higher bit + number. For example 7:0 */ +#define FLD_MASK(start, end) (((1 << (start - end + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << end) & FLD_MASK(start, end)) +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + +#define DISPC_MAX_FCK 173000000 + +enum omap_burst_size { + OMAP_DSS_BURST_4x32 = 0, /*OMAP_DSS_BURST_2x128 in OMAP4*/ + OMAP_DSS_BURST_8x32 = 1, /*OMAP_DSS_BURST_4x128 in OMAP4*/ + OMAP_DSS_BURST_16x32 = 2, /*OMAP_DSS_BURST_8x128 in OMAP4*/ +}; + +enum omap_parallel_interface_mode { + OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */ + OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */ + OMAP_DSS_PARALLELMODE_DSI, +}; + +enum dss_clock { + DSS_CLK_ICK = 1 << 0, + DSS_CLK_FCK1 = 1 << 1, + DSS_CLK_FCK2 = 1 << 2, + DSS_CLK_54M = 1 << 3, + DSS_CLK_96M = 1 << 4, +}; + +struct dispc_clock_info { + /* rates that we get with dividers below */ + unsigned long fck; + unsigned long lck; + unsigned long pck; + + /* dividers */ + u16 fck_div; + u16 lck_div; + u16 pck_div; +}; + +struct dsi_clock_info { + /* rates that we get with dividers below */ + unsigned long fint; + unsigned long dsiphy; + unsigned long clkin; + unsigned long dsi1_pll_fclk; + unsigned long dsi2_pll_fclk; + unsigned long lck; + unsigned long pck; + + /* dividers */ + u16 regn; + u16 regm; + u16 regm3; + u16 regm4; + + u16 lck_div; + u16 pck_div; + + u8 highfreq; + bool use_dss2_fck; +}; + +struct seq_file; +struct platform_device; + +/* core */ +void dss_clk_enable(enum dss_clock clks); +void dss_clk_disable(enum dss_clock clks); +unsigned long dss_clk_get_rate(enum dss_clock clk); +int dss_need_ctx_restore(void); +void dss_dump_clocks(struct seq_file *s); +struct bus_type *dss_get_bus(void); + +/* display */ +int dss_suspend_all_devices(void); +int dss_resume_all_devices(void); + +void dss_init_device(struct platform_device *pdev, + struct omap_dss_device *dssdev); +void dss_uninit_device(struct platform_device *pdev, + struct omap_dss_device *dssdev); +bool dss_use_replication(struct omap_dss_device *dssdev, + enum omap_color_mode mode); +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl); +void dss_setup_partial_planes(struct omap_dss_device *dssdev, + u16 *x, u16 *y, u16 *w, u16 *h); +void dss_start_update(struct omap_dss_device *dssdev); +void default_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high); + +/* manager */ +int dss_init_overlay_managers(struct platform_device *pdev); +void dss_uninit_overlay_managers(struct platform_device *pdev); +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl); +void dss_setup_partial_planes(struct omap_dss_device *dssdev, + u16 *x, u16 *y, u16 *w, u16 *h); +void dss_start_update(struct omap_dss_device *dssdev); + +/* overlay */ +void dss_init_overlays(struct platform_device *pdev); +void dss_uninit_overlays(struct platform_device *pdev); +int dss_check_overlay(struct omap_overlay *ovl, + struct omap_dss_device *dssdev); +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); +#ifdef L4_EXAMPLE +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr); +#endif +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); + +/* DSS */ +int dss_init(bool skip_init); +void dss_exit(void); + +void dss_switch_tv_hdmi(int hdmi); + +void dss_save_context(void); +void dss_restore_context(void); + +void dss_dump_regs(struct seq_file *s); + +void dss_sdi_init(u8 datapairs); +void dss_sdi_enable(void); +void dss_sdi_disable(void); + +void dss_select_clk_source(bool dsi, bool dispc); +int dss_get_dsi_clk_source(void); +int dss_get_dispc_clk_source(void); +void dss_set_venc_output(enum omap_dss_venc_type type); +void dss_set_dac_pwrdn_bgz(bool enable); + +/* SDI */ +int sdi_init(bool skip_init); +void sdi_exit(void); +int sdi_init_display(struct omap_dss_device *display); + +/* DSI */ +int dsi_init(struct platform_device *pdev); +void dsi_exit(void); + +void dsi_dump_clocks(struct seq_file *s); +void dsi_dump_regs(struct seq_file *s); + +void dsi_save_context(void); +void dsi_restore_context(void); + +int dsi_init_display(struct omap_dss_device *display); +void dsi_irq_handler(void); +unsigned long dsi_get_dsi1_pll_rate(void); +unsigned long dsi_get_dsi2_pll_rate(void); +int dsi_pll_calc_pck(bool is_tft, unsigned long req_pck, + struct dsi_clock_info *cinfo); +int dsi_pll_program(struct dsi_clock_info *cinfo); +int dsi_pll_init(bool enable_hsclk, bool enable_hsdiv); +void dsi_pll_uninit(void); +void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, + u32 fifo_size, enum omap_burst_size *burst_size, + u32 *fifo_low, u32 *fifo_high); + +/* DPI */ +int dpi_init(void); +void dpi_exit(void); +int dpi_init_display(struct omap_dss_device *dssdev); + +/* DISPC */ +int dispc_init(void); +void dispc_exit(void); +void dispc_dump_clocks(struct seq_file *s); +void dispc_dump_regs(struct seq_file *s); +void dispc_irq_handler(void); +void dispc_fake_vsync_irq(void); + +void dispc_save_context(void); +void dispc_restore_context(void); + +void dispc_enable_sidle(void); +void dispc_disable_sidle(void); + +void dispc_lcd_enable_signal_polarity(bool act_high); +void dispc_lcd_enable_signal(bool enable); +void dispc_pck_free_enable(bool enable); +void dispc_enable_fifohandcheck(bool enable); + +void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height); +void dispc_set_digit_size(u16 width, u16 height); +u32 dispc_get_plane_fifo_size(enum omap_plane plane); +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); +void dispc_enable_fifomerge(bool enable); +void dispc_set_burst_size(enum omap_plane plane, + enum omap_burst_size burst_size); + +void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr); +void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); +#ifdef CONFIG_ARCH_OMAP4 +void dispc_set_plane_ba_uv0(enum omap_plane plane, u32 paddr); +void dispc_set_plane_ba_uv1(enum omap_plane plane, u32 paddr); +void dispc_set_zorder(enum omap_plane plane, + enum omap_overlay_zorder zorder); +void dispc_enable_zorder(enum omap_plane plane, bool enable); +void dispc_enable_preload(enum omap_plane plane, bool enable); +void dispc_enable_gamma_table(bool enable); +void dispc_set_idle_mode(void); +#endif + +void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y); +void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height); +void dispc_set_channel_out(enum omap_plane plane, + enum omap_channel channel_out); + +int dispc_setup_plane(enum omap_plane plane, + u32 paddr, u16 screen_width, + u16 pos_x, u16 pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + enum omap_color_mode color_mode, + bool ilace, + enum omap_dss_rotation_type rotation_type, + u8 rotation, bool mirror, + u8 global_alpha, enum omap_channel channel +#ifdef CONFIG_ARCH_OMAP4 + , u32 p_uv_addr +#endif + ); + +bool dispc_go_busy(enum omap_channel channel); +void dispc_go(enum omap_channel channel); +void dispc_enable_lcd_out(enum omap_channel channel, bool enable); +void dispc_enable_digit_out(bool enable); +int dispc_enable_plane(enum omap_plane plane, bool enable); +void dispc_enable_replication(enum omap_plane plane, bool enable); + +void dispc_set_parallel_interface_mode(enum omap_channel channel, + enum omap_parallel_interface_mode mode); +void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines); +void dispc_set_lcd_display_type(enum omap_channel channel, + enum omap_lcd_display_type type); +void dispc_set_loadmode(enum omap_dss_load_mode mode); + +void dispc_set_default_color(enum omap_channel channel, u32 color); +u32 dispc_get_default_color(enum omap_channel channel); +void dispc_set_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key); +void dispc_get_trans_key(enum omap_channel ch, + enum omap_dss_trans_key_type *type, + u32 *trans_key); +void dispc_enable_trans_key(enum omap_channel ch, bool enable); +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable); +bool dispc_trans_key_enabled(enum omap_channel ch); +bool dispc_alpha_blending_enabled(enum omap_channel ch); + +bool dispc_lcd_timings_ok(struct omap_video_timings *timings); +void dispc_set_lcd_timings(enum omap_channel channel, + struct omap_video_timings *timings); +unsigned long dispc_fclk_rate(void); +unsigned long dispc_lclk_rate(enum omap_channel channel); +unsigned long dispc_pclk_rate(enum omap_channel channel); +void dispc_set_pol_freq(enum omap_channel channel, + enum omap_panel_config config, u8 acbi, u8 acb); +void find_lck_pck_divs(bool is_tft, unsigned long req_pck, + unsigned long fck, u16 *lck_div, u16 *pck_div); +int dispc_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dispc_clock_info *cinfo); +int dispc_set_clock_div(struct dispc_clock_info *cinfo); +int dispc_get_clock_div(struct dispc_clock_info *cinfo); +void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div, + u16 pck_div); + +/* VENC */ +int venc_init(struct platform_device *pdev); +void venc_exit(void); +void venc_dump_regs(struct seq_file *s); +int venc_init_display(struct omap_dss_device *display); + +/* RFBI */ +int rfbi_init(void); +void rfbi_exit(void); +void rfbi_dump_regs(struct seq_file *s); + +int rfbi_configure(int rfbi_module, int bpp, int lines); +void rfbi_enable_rfbi(bool enable); +void rfbi_transfer_area(u16 width, u16 height, + void (callback)(void *data), void *data); +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); +unsigned long rfbi_get_max_tx_rate(void); +int rfbi_init_display(struct omap_dss_device *display); + +/* HDMI*/ +int hdmi_init(struct platform_device *pdev); +void hdmi_exit(void); +void hdmi_dump_regs(struct seq_file *s); +int hdmi_init_display(struct omap_dss_device *display); +#endif diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c new file mode 100644 index 000000000000..8a3333b246a1 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi.c @@ -0,0 +1,696 @@ +/* + * linux/drivers/video/omap2/dss/hdmi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * HDMI settings from TI's DSS driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "HDMI" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/platform_device.h> +#include <mach/display.h> +#include <mach/cpu.h> +#include <mach/hdmi_lib.h> +#include <mach/gpio.h> + +#include "dss.h" + +#define HDMI_PLLCTRL 0x58006200 +#define HDMI_PHY 0x58006300 + +/* PLL */ +#define PLLCTRL_PLL_CONTROL 0x0ul +#define PLLCTRL_PLL_STATUS 0x4ul +#define PLLCTRL_PLL_GO 0x8ul +#define PLLCTRL_CFG1 0xCul +#define PLLCTRL_CFG2 0x10ul +#define PLLCTRL_CFG3 0x14ul +#define PLLCTRL_CFG4 0x20ul + +/* HDMI PHY */ +#define HDMI_TXPHY_TX_CTRL 0x0ul +#define HDMI_TXPHY_DIGITAL_CTRL 0x4ul +#define HDMI_TXPHY_POWER_CTRL 0x8ul + + +const struct omap_video_timings omap_dss_hdmi_timings2 = { + .x_res = 1280, + .y_res = 720, + .pixel_clock = 74250, + .hsw = 40, + .hfp = 110, + .hbp = 220, + .vsw = 5, + .vfp = 5, + .vbp = 20, +}; +const struct omap_video_timings omap_dss_hdmi_timings = { + .x_res = 640, + .y_res = 480, + .pixel_clock = 74250, + .hsw = 96, + .hfp = 16, + .hbp = 48, + .vsw = 2, + .vfp = 10, + .vbp = 33, +}; + +const struct omap_video_timings omap_dss_hdmi_timings3 = { + .x_res = 1920, + .y_res = 1080, + .pixel_clock = 74250, + .hsw = 44, + .hfp = 88, + .hbp = 148, + .vsw = 5, + .vfp = 4, + .vbp = 36, +}; + + +static struct { + void __iomem *base_phy; + void __iomem *base_pll; + struct mutex lock; +} hdmi; + +static inline void hdmi_write_reg(u32 base, u16 idx, u32 val) +{ + void __iomem *b; + + switch (base) { + case HDMI_PHY: + b = hdmi.base_phy; + break; + case HDMI_PLLCTRL: + b = hdmi.base_pll; + break; + default: + BUG(); + } + __raw_writel(val, b + idx); + /* DBG("write = 0x%x idx =0x%x\r\n", val, idx); */ +} + +static inline u32 hdmi_read_reg(u32 base, u16 idx) +{ + void __iomem *b; + u32 l; + + switch (base) { + case HDMI_PHY: + b = hdmi.base_phy; + break; + case HDMI_PLLCTRL: + b = hdmi.base_pll; + break; + default: + BUG(); + } + l = __raw_readl(b + idx); + + /* DBG("addr = 0x%p rd = 0x%x idx = 0x%x\r\n", (b+idx), l, idx); */ + return l; +} + +#define FLD_MASK(start, end) (((1 << (start - end + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << end) & FLD_MASK(start, end)) +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + +#define REG_FLD_MOD(b, i, v, s, e) \ + hdmi_write_reg(b, i, FLD_MOD(hdmi_read_reg(b, i), v, s, e)) + +/* + * refclk = (sys_clk/(highfreq+1))/(n+1) + * so refclk = 38.4/2/(n+1) = 19.2/(n+1) + * choose n = 15, makes refclk = 1.2 + * + * m = tclk/cpf*refclk = tclk/2*1.2 + * + * for clkin = 38.2/2 = 192 + * phy = 2520 + * + * m = 2520*16/2* 192 = 105; + * + * for clkin = 38.4 + * phy = 2520 + * + */ + +#define CPF 2 + +typedef struct hdmi_pll_info { + u16 regn; + u16 regm; + u32 regmf; + u16 regm4; /* M4_CLOCK_DIV */ +} hdmi_pll_info; + +/* HDMI TRM Page 53 */ +static const hdmi_pll_info coef_hdmi[6] = { + {15, 105, 0, 4}, /* CEA861D_CODE1 */ + {15, 309, 98304, 14}, /* CEA861D_CODE4 */ + {15, 309, 98304, 14}, /* CEA861D_CODE1 */ + {15, 618, 196608, 28}, /* CEA861D_CODE16 */ + {15, 618, 34568, 1}, /* CEA861D_CODE1 */ + {9, 65, 0, 1}, /* test */ +}; + +static void compute_pll(int clkin, int phy, + int n, int *m) +{ + int hf = 0; + int refclk; + + if (clkin > 3200) /* 32 mHz */ + hf = 1; + + if (hf == 0) + refclk = clkin / (n + 1); + else + refclk = clkin / (2 * (n + 1)); + + /* HDMIPHY(MHz) = (CPF * regm / regn) * (clkin / (highfreq + 1)) */ + /* phy = CPF * regm * refclk */ + + *m = phy / (CPF * refclk); + +} + +static int hdmi_pll_init(int refsel, int dcofreq, struct hdmi_pll_info *fmt) +{ + u32 r; + unsigned t = 500000; + u32 pll = HDMI_PLLCTRL; + + DSSDBG("0x%x %d %d %d %d\n", pll, refsel, + fmt->regm, fmt->regn, dcofreq); + + /* PLL start always use manual mode */ + REG_FLD_MOD(pll, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); + + r = hdmi_read_reg(pll, PLLCTRL_CFG1); + r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1__PLL_REGM */ + r = FLD_MOD(r, fmt->regn, 8, 1); /* CFG1__PLL_REGN */ + r = FLD_MOD(r, fmt->regm4, 25, 21); /* M4_CLOCK_DIV */ + + hdmi_write_reg(pll, PLLCTRL_CFG1, r); + + r = hdmi_read_reg(pll, PLLCTRL_CFG2); + + /* SYS w/o divide by 2 [22:21] = donot care [11:11] = 0x0 */ + /* SYS divide by 2 [22:21] = 0x3 [11:11] = 0x1 */ + /* PCLK, REF1 or REF2 [22:21] = 0x0, 0x 1 or 0x2 [11:11] = 0x1 */ + r = FLD_MOD(r, 0x0, 11, 11); /* PLL_CLKSEL 1: PLL 0: SYS*/ + r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ + r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ + r = FLD_MOD(r, 0x1, 14, 14); /* PHY_CLKINEN de-assert during locking */ + r = FLD_MOD(r, 0x0, 20, 20); /* HSDIVBYPASS assert during locking */ + r = FLD_MOD(r, refsel, 22, 21); /* REFSEL */ + /* DPLL3 used by DISPC or HDMI itself*/ + r = FLD_MOD(r, 0x0, 17, 17); /* M4_CLOCK_PWDN */ + r = FLD_MOD(r, 0x1, 16, 16); /* M4_CLOCK_EN */ + + if (dcofreq) + r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ + else + r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ + + hdmi_write_reg(pll, PLLCTRL_CFG2, r); + + r = hdmi_read_reg(pll, PLLCTRL_CFG4); + r = FLD_MOD(r, 0, 24, 18); /* todo: M2 */ + r = FLD_MOD(r, fmt->regmf, 17, 0); + + /* go now */ + REG_FLD_MOD(pll, PLLCTRL_PLL_GO, 0x1ul, 0, 0); + + /* wait for bit change */ + while (FLD_GET(hdmi_read_reg(pll, PLLCTRL_PLL_GO), 0, 0)) + + /* Wait till the lock bit is set */ + /* read PLL status */ + DSSDBG("status 0x%x\r\n", hdmi_read_reg(pll, PLLCTRL_PLL_STATUS)); + + DSSDBG("CFG1 0x%x\r\n", hdmi_read_reg(pll, PLLCTRL_CFG1)); + DSSDBG("CFG2 0x%x\r\n", hdmi_read_reg(pll, PLLCTRL_CFG2)); + DSSDBG("CFG4 0x%x\r\n", hdmi_read_reg(pll, PLLCTRL_CFG4)); + + while (0 == FLD_GET(hdmi_read_reg(pll, PLLCTRL_PLL_STATUS), 1, 1)) { + udelay(1); + if (!--t) { + ERR("cannot lock PLL\n"); + return -EIO; + } + } + + DSSDBG("PLL locked!\n"); + + r = hdmi_read_reg(pll, PLLCTRL_CFG2); + r = FLD_MOD(r, 0, 0, 0); /* PLL_IDLE */ + r = FLD_MOD(r, 0, 5, 5); /* PLL_PLLLPMODE */ + r = FLD_MOD(r, 0, 6, 6); /* PLL_LOWCURRSTBY */ + r = FLD_MOD(r, 0, 8, 8); /* PLL_DRIFTGUARDEN */ + r = FLD_MOD(r, 0, 10, 9); /* PLL_LOCKSEL */ + r = FLD_MOD(r, 1, 13, 13); /* PLL_REFEN */ + r = FLD_MOD(r, 1, 14, 14); /* PHY_CLKINEN */ + r = FLD_MOD(r, 0, 15, 15); /* BYPASSEN */ + r = FLD_MOD(r, 0, 20, 20); /* HSDIVBYPASS */ + hdmi_write_reg(pll, PLLCTRL_CFG2, r); + + return 0; +} + +static int hdmi_pll_reset(void) +{ + int t = 0; + + /* SYSREEST controled by power FSM*/ + REG_FLD_MOD(HDMI_PLLCTRL, PLLCTRL_PLL_CONTROL, 0x0, 3, 3); + + /* READ 0x0 reset is in progress */ + while (!FLD_GET(hdmi_read_reg(HDMI_PLLCTRL, + PLLCTRL_PLL_STATUS), 0, 0)) { + udelay(1); + if (t++ > 1000) { + ERR("Failed to sysrest PLL\n"); + return -ENODEV; + } + } + return 0; +} + +int hdmi_pll_program(struct hdmi_pll_info *format) +{ + u32 r; + int refsel, range; + + HDMI_PllPwr_t PllPwrWaitParam; + + /* wait for wrapper rest */ + HDMI_W1_SetWaitSoftReset(); + + /* power off PLL */ + PllPwrWaitParam = HDMI_PLLPWRCMD_ALLOFF; + r = HDMI_W1_SetWaitPllPwrState(HDMI_WP, + PllPwrWaitParam); + if (r) + return r; + + /* power on PLL */ + PllPwrWaitParam = HDMI_PLLPWRCMD_BOTHON_ALLCLKS; + r = HDMI_W1_SetWaitPllPwrState(HDMI_WP, + PllPwrWaitParam); + if (r) + return r; + + hdmi_pll_reset(); + + refsel = 0x3; /* select SYSCLK reference */ + range = 0; + + r = hdmi_pll_init(refsel, range, format); + + return r; + +} + +/* double check the order */ +static int hdmi_phy_init(u32 w1, + u32 phy) +{ + u32 count; + int r; + + /* wait till PHY_PWR_STATUS=LDOON */ + /* HDMI_PHYPWRCMD_LDOON = 1 */ + r = HDMI_W1_SetWaitPhyPwrState(w1, 1); + if (r) + return r; + + /* wait till PHY_PWR_STATUS=TXON */ + r = HDMI_W1_SetWaitPhyPwrState(w1, 2); + if (r) + return r; + + /* read address 0 in order to get the SCPreset done completed */ + /* Dummy access performed to solve resetdone issue */ + hdmi_read_reg(phy, HDMI_TXPHY_TX_CTRL); + + /* write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ + hdmi_write_reg(phy, HDMI_TXPHY_DIGITAL_CTRL, + 0xF0000000); + + /* setup max LDO voltage */ + REG_FLD_MOD(phy, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); + + /* use HFBITCLK write HDMI_TXPHY_TX_CONTROL__FREQOUT field */ + REG_FLD_MOD(phy, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); + + count = 0; + while (count++ < 1000) + + return 0; +} + +static int hdmi_phy_off(u32 name) +{ + int r = 0; + u32 count; + + /* wait till PHY_PWR_STATUS=OFF */ + /* HDMI_PHYPWRCMD_OFF = 0 */ + r = HDMI_W1_SetWaitPhyPwrState(name, 0); + if (r) + return r; + + count = 0; + while (count++ < 200) + + return 0; +} + +/* driver */ +static int hdmi_panel_probe(struct omap_dss_device *dssdev) +{ + DSSDBG("ENTER hdmi_panel_probe()\n"); + + dssdev->panel.config = OMAP_DSS_LCD_TFT | + OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + dssdev->panel.timings = omap_dss_hdmi_timings; + + return 0; +} + +static void hdmi_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int hdmi_panel_enable(struct omap_dss_device *dssdev) +{ + return 0; +} + +static void hdmi_panel_disable(struct omap_dss_device *dssdev) +{ +} + +static int hdmi_panel_suspend(struct omap_dss_device *dssdev) +{ + return 0; +} + +static int hdmi_panel_resume(struct omap_dss_device *dssdev) +{ + return 0; +} + +static void hdmi_enable_clocks(int enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); +} + +static struct omap_dss_driver hdmi_driver = { + .probe = hdmi_panel_probe, + .remove = hdmi_panel_remove, + + .enable = hdmi_panel_enable, + .disable = hdmi_panel_disable, + .suspend = hdmi_panel_suspend, + .resume = hdmi_panel_resume, + + .driver = { + .name = "hdmi_panel", + .owner = THIS_MODULE, + }, +}; +/* driver end */ + +int hdmi_init(struct platform_device *pdev) +{ + hdmi_pll_info *pllptr; + + DSSDBG("Enter hdmi_init()\n"); + + mutex_init(&hdmi.lock); + + hdmi.base_pll = ioremap(HDMI_PLLCTRL, 64); + if (!hdmi.base_pll) { + ERR("can't ioremap pll\n"); + return -ENOMEM; + } + hdmi.base_phy = ioremap(HDMI_PHY, 64); + + if (!hdmi.base_phy) { + ERR("can't ioremap phy\n"); + return -ENOMEM; + } + + hdmi_enable_clocks(1); + + hdmi_lib_init(); + + hdmi_enable_clocks(0); + return omap_dss_register_driver(&hdmi_driver); + +} + +void hdmi_exit(void) +{ + hdmi_lib_exit(); + + iounmap(hdmi.base_pll); + iounmap(hdmi.base_phy); +} + + +static int hdmi_power_on(struct omap_dss_device *dssdev) +{ + int format = 1; + int r; + hdmi_pll_info *ptr; + + /* use EDID in the future */ + if (1280 == dssdev->panel.timings.x_res) + format = 4; + else if (1920 == dssdev->panel.timings.x_res) + format = 16; + + hdmi_enable_clocks(1); + + HDMI_W1_StopVideoFrame(HDMI_WP); + + dispc_enable_digit_out(0); + + /* while(dispc_go_busy(OMAP_DSS_CHANNEL_DIGIT)) + msleep(40); */ + + dispc_go(OMAP_DSS_CHANNEL_DIGIT); + + /* config the PLL and PHY first */ + ptr = &coef_hdmi[0]; /* index 0 for VGA */ + r = hdmi_pll_program(ptr); + + if (r) + DSSERR("Failed to lock PLL\n"); + + r = hdmi_phy_init(HDMI_WP, HDMI_PHY); + if (r) + DSSERR("Failed to start PHY\n"); + + DSS_HDMI_CONFIG(HDMI_CORE_SYS, HDMI_CORE_AV, + HDMI_WP, HDMI_PHY, HDMI_PLLCTRL, format); + + /* these settings are independent of overlays */ + dss_switch_tv_hdmi(1); + dispc_set_loadmode(OMAP_DSS_LOAD_CLUT_AND_FRAME); + + /* bypass TV gamma table*/ + dispc_enable_gamma_table(0); + + /* do not fall into any sort of idle */ + dispc_set_idle_mode(); + + /* tv size */ + dispc_set_digit_size(dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + + /* dss_dump_regs(NULL); */ + /* dispc_dump_regs(NULL); */ + + HDMI_W1_StartVideoFrame(HDMI_WP); + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + + dispc_enable_digit_out(1); + + /* while(dispc_go_busy(OMAP_DSS_CHANNEL_DIGIT)) */ + /* msleep(40); */ + dispc_go(OMAP_DSS_CHANNEL_DIGIT); + + return 0; +} + +static void hdmi_power_off(struct omap_dss_device *dssdev) +{ + /* todo: find out what needs to be done for power off */ + dispc_enable_digit_out(0); + + hdmi_phy_off(HDMI_WP); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + hdmi_enable_clocks(0); +} + +static int hdmi_enable_display(struct omap_dss_device *dssdev) +{ + int r = 0; + DSSDBG("ENTER hdmi_enable_display()\n"); + + mutex_lock(&hdmi.lock); + + /* the tv overlay manager is shared*/ + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } + + hdmi_power_on(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +err: + mutex_unlock(&hdmi.lock); + return r; + +} + +static void hdmi_disable_display(struct omap_dss_device *dssdev) +{ + DSSDBG("Enter hdmi_disable_display()\n"); + + mutex_lock(&hdmi.lock); + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + goto end; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) { + /* suspended is the same as disabled with venc */ + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + goto end; + } + + hdmi_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +end: + omap_dss_stop_device(dssdev); + mutex_unlock(&hdmi.lock); +} + +static int hdmi_display_suspend(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("hdmi_display_suspend\n"); + return r; +} + +static int hdmi_display_resume(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("hdmi_display_resume\n"); + + return r; +} + +static void hdmi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static void hdmi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("hdmi_set_timings\n"); + + dssdev->panel.timings = *timings; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* turn the hdmi off and on to get new timings to use */ + hdmi_disable_display(dssdev); + hdmi_enable_display(dssdev); + } +} + +static int hdmi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("hdmi_check_timings\n"); + + if (memcmp(&omap_dss_hdmi_timings, timings, sizeof(*timings)) == 0) + return 0; + + return -EINVAL; +} + +int hdmi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + dssdev->enable = hdmi_enable_display; + dssdev->disable = hdmi_disable_display; + dssdev->suspend = hdmi_display_suspend; + dssdev->resume = hdmi_display_resume; + dssdev->get_timings = hdmi_get_timings; + dssdev->set_timings = hdmi_set_timings; + dssdev->check_timings = hdmi_check_timings; + + return 0; +} + + + + + + diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c new file mode 100644 index 000000000000..7116c235bdcc --- /dev/null +++ b/drivers/video/omap2/dss/manager.c @@ -0,0 +1,1561 @@ +/* + * linux/drivers/video/omap2/dss/manager.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "MANAGER" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/jiffies.h> + +#include <mach/display.h> +#include <mach/cpu.h> + +#include "dss.h" + + +#ifdef CONFIG_ARCH_OMAP4 +#define MAX_DSS_MANAGERS 3 +#else +#define MAX_DSS_MANAGERS 2 +#endif + +static int num_managers; +static struct list_head manager_list; + +static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); +} + +static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + mgr->device ? mgr->device->name : "<none>"); +} + +static ssize_t manager_display_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + int r = 0; + size_t len = size; + struct omap_dss_device *dssdev = NULL; + + int match(struct omap_dss_device *dssdev, void *data) + { + const char *str = data; + return sysfs_streq(dssdev->name, str); + } + + if (buf[size-1] == '\n') + --len; + + if (len > 0) + dssdev = omap_dss_find_device((void *)buf, match); + + if (len > 0 && dssdev == NULL) + return -EINVAL; + + if (dssdev) + DSSDBG("display %s found\n", dssdev->name); + + if (mgr->device) { + r = mgr->unset_device(mgr); + if (r) { + DSSERR("failed to unset display\n"); + goto put_device; + } + } + + if (dssdev) { + r = mgr->set_device(mgr, dssdev); + if (r) { + DSSERR("failed to set manager\n"); + goto put_device; + } + + r = mgr->apply(mgr); + if (r) { + DSSERR("failed to apply dispc config\n"); + goto put_device; + } + } + +put_device: + if (dssdev) + omap_dss_put_device(dssdev); + + return r ? r : size; +} + +static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); +} + +static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + u32 color; + int r; + + if (sscanf(buf, "%d", &color) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.default_color = color; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static const char *trans_key_type_str[] = { + "gfx-destination", + "video-source", +}; + +static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, + char *buf) +{ + enum omap_dss_trans_key_type key_type; + + key_type = mgr->info.trans_key_type; + BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); + + return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); +} + +static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + enum omap_dss_trans_key_type key_type; + struct omap_overlay_manager_info info; + int r; + + for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { + if (sysfs_streq(buf, trans_key_type_str[key_type])) + break; + } + + if (key_type == ARRAY_SIZE(trans_key_type_str)) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_key_type = key_type; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); +} + +static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + u32 key_value; + int r; + + if (sscanf(buf, "%d", &key_value) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_key = key_value; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", (mgr->info.trans_enabled)? "true" : "false"); +} + +static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + int enable; + int r; + + if (sscanf(buf, "%d", &enable) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.trans_enabled = enable ? true : false; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_alpha_blending_enabled_show( + struct omap_overlay_manager *mgr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", (mgr->info.alpha_enabled)? "true" : "false"); +} + +static ssize_t manager_alpha_blending_enabled_store( + struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + int enable; + int r; + + if (sscanf(buf, "%d", &enable) != 1) + return -EINVAL; + + mgr->get_manager_info(mgr, &info); + + info.alpha_enabled = enable ? true : false; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +struct manager_attribute { + struct attribute attr; + ssize_t (*show)(struct omap_overlay_manager *, char *); + ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); +}; + +#define MANAGER_ATTR(_name, _mode, _show, _store) \ + struct manager_attribute manager_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); +static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, + manager_display_show, manager_display_store); +static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, + manager_default_color_show, manager_default_color_store); +static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, + manager_trans_key_type_show, manager_trans_key_type_store); +static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, + manager_trans_key_value_show, manager_trans_key_value_store); +static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, + manager_trans_key_enabled_show, + manager_trans_key_enabled_store); +static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, + manager_alpha_blending_enabled_show, + manager_alpha_blending_enabled_store); + + +static struct attribute *manager_sysfs_attrs[] = { + &manager_attr_name.attr, + &manager_attr_display.attr, + &manager_attr_default_color.attr, + &manager_attr_trans_key_type.attr, + &manager_attr_trans_key_value.attr, + &manager_attr_trans_key_enabled.attr, + &manager_attr_alpha_blending_enabled.attr, + NULL +}; + +static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct omap_overlay_manager *manager; + struct manager_attribute *manager_attr; + + manager = container_of(kobj, struct omap_overlay_manager, kobj); + manager_attr = container_of(attr, struct manager_attribute, attr); + + if (!manager_attr->show) + return -ENOENT; + + return manager_attr->show(manager, buf); +} + +static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + struct omap_overlay_manager *manager; + struct manager_attribute *manager_attr; + + manager = container_of(kobj, struct omap_overlay_manager, kobj); + manager_attr = container_of(attr, struct manager_attribute, attr); + + if (!manager_attr->store) + return -ENOENT; + + return manager_attr->store(manager, buf, size); +} + +static struct sysfs_ops manager_sysfs_ops = { + .show = manager_attr_show, + .store = manager_attr_store, +}; + +static struct kobj_type manager_ktype = { + .sysfs_ops = &manager_sysfs_ops, + .default_attrs = manager_sysfs_attrs, +}; + +/* + * We have 4 levels of cache for the dispc settings. First two are in SW and + * the latter two in HW. + * + * +--------------------+ + * |overlay/manager_info| + * +--------------------+ + * v + * apply() + * v + * +--------------------+ + * | dss_cache | + * +--------------------+ + * v + * configure() + * v + * +--------------------+ + * | shadow registers | + * +--------------------+ + * v + * VFP or lcd/digit_enable + * v + * +--------------------+ + * | registers | + * +--------------------+ + */ + +struct overlay_cache_data { + /* If true, cache changed, but not written to shadow registers. Set + * in apply(), cleared when registers written. */ + bool dirty; + /* If true, shadow registers contain changed values not yet in real + * registers. Set when writing to shadow registers, cleared at + * VSYNC/EVSYNC */ + bool shadow_dirty; + + bool enabled; + + u32 paddr; + void __iomem *vaddr; + u16 screen_width; + u16 width; + u16 height; + enum omap_color_mode color_mode; + u8 rotation; + enum omap_dss_rotation_type rotation_type; + bool mirror; + + u16 pos_x; + u16 pos_y; + u16 out_width; /* if 0, out_width == width */ + u16 out_height; /* if 0, out_height == height */ + u8 global_alpha; + + enum omap_channel channel; + bool replication; + bool ilace; + + enum omap_burst_size burst_size; + u32 fifo_low; + u32 fifo_high; + + bool manual_update; + +#ifdef CONFIG_ARCH_OMAP4 + u32 p_uv_addr; /* relevant for NV12 format only */ + enum omap_overlay_zorder zorder; +#endif +}; + +struct manager_cache_data { + /* If true, cache changed, but not written to shadow registers. Set + * in apply(), cleared when registers written. */ + bool dirty; + /* If true, shadow registers contain changed values not yet in real + * registers. Set when writing to shadow registers, cleared at + * VSYNC/EVSYNC */ + bool shadow_dirty; + + u32 default_color; + + enum omap_dss_trans_key_type trans_key_type; + u32 trans_key; + bool trans_enabled; + + bool alpha_enabled; + + bool manual_upd_display; + bool manual_update; + bool do_manual_update; + + /* manual update region */ + u16 x, y, w, h; +}; + +static struct { + spinlock_t lock; +#ifdef CONFIG_ARCH_OMAP4 + struct overlay_cache_data overlay_cache[4]; + struct manager_cache_data manager_cache[3]; +#else + struct overlay_cache_data overlay_cache[3]; + struct manager_cache_data manager_cache[2]; +#endif + + + bool irq_enabled; +} dss_cache; + +static int omap_dss_set_device(struct omap_overlay_manager *mgr, + struct omap_dss_device *dssdev) +{ + int i; + int r; + + if (dssdev->manager) { + DSSERR("display '%s' already has a manager '%s'\n", + dssdev->name, dssdev->manager->name); + return -EINVAL; + } + + if ((mgr->supported_displays & dssdev->type) == 0) { + DSSERR("display '%s' does not support manager '%s'\n", + dssdev->name, mgr->name); + return -EINVAL; + } + + for (i = 0; i < mgr->num_overlays; i++) { + struct omap_overlay *ovl = mgr->overlays[i]; + + if (ovl->manager != mgr || !ovl->info.enabled) + continue; + + r = dss_check_overlay(ovl, dssdev); + if (r) + return r; + } + + dssdev->manager = mgr; + mgr->device = dssdev; + mgr->device_changed = true; + + return 0; +} + +static int omap_dss_unset_device(struct omap_overlay_manager *mgr) +{ + if (!mgr->device) { + DSSERR("failed to unset display, display not set.\n"); + return -EINVAL; + } + + mgr->device->manager = NULL; + mgr->device = NULL; + mgr->device_changed = true; + + return 0; +} + +static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) +{ + unsigned long timeout = msecs_to_jiffies(500); + struct manager_cache_data *mc; + enum omap_channel channel; + u32 irq; + int r; + int i; + + if (!mgr->device) + return 0; + channel = mgr->device->channel; + + if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC + || mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + } else { + if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { + enum omap_dss_update_mode mode; + mode = mgr->device->get_update_mode(mgr->device); + if (mode != OMAP_DSS_UPDATE_AUTO) + return 0; +#ifdef CONFIG_ARCH_OMAP4 + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE + : DISPC_IRQ_FRAMEDONE2; +#else + irq = DISPC_IRQ_FRAMEDONE; +#endif + } else { +#ifdef CONFIG_ARCH_OMAP4 + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC + : DISPC_IRQ_VSYNC2; +#else + irq = DISPC_IRQ_VSYNC; +#endif + } + } + + mc = &dss_cache.manager_cache[mgr->id]; + i = 0; + while (1) { + unsigned long flags; + bool shadow_dirty, dirty; + + spin_lock_irqsave(&dss_cache.lock, flags); + dirty = mc->dirty; + shadow_dirty = mc->shadow_dirty; + spin_unlock_irqrestore(&dss_cache.lock, flags); + + if (!dirty && !shadow_dirty) { + r = 0; + break; + } + + /* 4 iterations is the worst case: + * 1 - initial iteration, dirty = true (between VFP and VSYNC) + * 2 - first VSYNC, dirty = true + * 3 - dirty = false, shadow_dirty = true + * 4 - shadow_dirty = false */ + if (i++ == 3) { + DSSERR("mgr(%d)->wait_for_go() not finishing\n", + mgr->id); + r = 0; + break; + } + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + if (r == -ERESTARTSYS) + break; + + if (r) { + DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); + break; + } + } + + return r; +} + +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) +{ + unsigned long timeout = msecs_to_jiffies(500); + enum omap_channel channel; + struct overlay_cache_data *oc; + struct omap_dss_device *dssdev; + u32 irq; + int r; + int i; + + if (!ovl->manager || !ovl->manager->device) + return 0; + + dssdev = ovl->manager->device; + channel = dssdev->channel; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + } else { + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { + enum omap_dss_update_mode mode; + mode = dssdev->get_update_mode(dssdev); + if (mode != OMAP_DSS_UPDATE_AUTO) + return 0; +#ifdef CONFIG_ARCH_OMAP4 + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE + : DISPC_IRQ_FRAMEDONE2; +#else + irq = DISPC_IRQ_FRAMEDONE; +#endif + } else { +#ifdef CONFIG_ARCH_OMAP4 + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC + : DISPC_IRQ_VSYNC2; +#else + irq = DISPC_IRQ_VSYNC; +#endif + } + } + + oc = &dss_cache.overlay_cache[ovl->id]; + i = 0; + while (1) { + unsigned long flags; + bool shadow_dirty, dirty; + + spin_lock_irqsave(&dss_cache.lock, flags); + dirty = oc->dirty; + shadow_dirty = oc->shadow_dirty; + spin_unlock_irqrestore(&dss_cache.lock, flags); + + if (!dirty && !shadow_dirty) { + r = 0; + break; + } + + /* 4 iterations is the worst case: + * 1 - initial iteration, dirty = true (between VFP and VSYNC) + * 2 - first VSYNC, dirty = true + * 3 - dirty = false, shadow_dirty = true + * 4 - shadow_dirty = false */ + if (i++ == 3) { + DSSERR("ovl(%d)->wait_for_go() not finishing\n", + ovl->id); + r = 0; + break; + } + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + if (r == -ERESTARTSYS) + break; + + if (r) { + DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); + break; + } + } + + return r; +} + +static int overlay_enabled(struct omap_overlay *ovl) +{ + return ovl->info.enabled && ovl->manager && ovl->manager->device; +} + +/* Is rect1 a subset of rect2? */ +static bool rectangle_subset(int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) +{ + if (x1 < x2 || y1 < y2) + return false; + + if (x1 + w1 > x2 + w2) + return false; + + if (y1 + h1 > y2 + h2) + return false; + + return true; +} + +/* Do rect1 and rect2 overlap? */ +static bool rectangle_intersects(int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) +{ + if (x1 >= x2 + w2) + return false; + + if (x2 >= x1 + w1) + return false; + + if (y1 >= y2 + h2) + return false; + + if (y2 >= y1 + h1) + return false; + + return true; +} + +static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) +{ + if (oc->out_width != 0 && oc->width != oc->out_width) + return true; + + if (oc->out_height != 0 && oc->height != oc->out_height) + return true; + + return false; +} + +static int configure_overlay(enum omap_plane plane) +{ + struct overlay_cache_data *c; + struct manager_cache_data *mc; + u16 outw, outh; + u16 x, y, w, h; + u32 paddr; + int r; + + DSSDBGF("%d", plane); + + c = &dss_cache.overlay_cache[plane]; + + if (!c->enabled) { + dispc_enable_plane(plane, 0); + return 0; + } + + mc = &dss_cache.manager_cache[c->channel]; + + x = c->pos_x; + y = c->pos_y; + w = c->width; + h = c->height; + outw = c->out_width == 0 ? c->width : c->out_width; + outh = c->out_height == 0 ? c->height : c->out_height; + paddr = c->paddr; +/* TODO: OMAP4: check if changes are needed here for NV12 ?*/ + if (c->manual_update && mc->do_manual_update) { + unsigned bpp; + /* If the overlay is outside the update region, disable it */ + if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, + x, y, outw, outh)) { + dispc_enable_plane(plane, 0); + return 0; + } + + switch (c->color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + bpp = 16; + break; + + case OMAP_DSS_COLOR_RGB24P: + bpp = 24; + break; + + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + bpp = 32; + break; + + default: + BUG(); + } + + if (mc->x > c->pos_x) { + x = 0; + w -= (mc->x - c->pos_x); + paddr += (mc->x - c->pos_x) * bpp / 8; + } else { + x = c->pos_x - mc->x; + } + + if (mc->y > c->pos_y) { + y = 0; + h -= (mc->y - c->pos_y); + paddr += (mc->y - c->pos_y) * c->screen_width * bpp / 8; + } else { + y = c->pos_y - mc->y; + } + + if (mc->w < (x+w)) + w -= (x+w) - (mc->w); + + if (mc->h < (y+h)) + h -= (y+h) - (mc->h); + + if (!dispc_is_overlay_scaled(c)) { + outw = w; + outh = h; + } + } + + r = dispc_setup_plane(plane, + paddr, + c->screen_width, + x, y, + w, h, + outw, outh, + c->color_mode, + c->ilace, + c->rotation_type, + c->rotation, + c->mirror, + c->global_alpha, c->channel +#ifdef CONFIG_ARCH_OMAP4 + , c->p_uv_addr +#endif + ); + + if (r) { + /* this shouldn't happen */ + DSSERR("dispc_setup_plane failed for ovl %d\n", plane); + dispc_enable_plane(plane, 0); + return r; + } + + dispc_enable_replication(plane, c->replication); +#ifdef CONFIG_OMAP2_DSS_HDMI + dispc_enable_preload(plane, 1); /* ZeBu hdmi test */ +#endif + dispc_set_burst_size(plane, c->burst_size); + dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); + +#ifdef CONFIG_ARCH_OMAP4 + dispc_set_zorder(plane, c->zorder); + dispc_enable_zorder(plane, 1); +#endif + dispc_enable_plane(plane, 1); + + return 0; +} + +static void configure_manager(enum omap_channel channel) +{ + struct manager_cache_data *c; + + DSSDBGF("%d", channel); + + c = &dss_cache.manager_cache[channel]; + + dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); + dispc_enable_trans_key(channel, c->trans_enabled); + dispc_enable_alpha_blending(channel, c->alpha_enabled); +} + +/* configure_dispc() tries to write values from cache to shadow registers. + * It writes only to those managers/overlays that are not busy. + * returns 0 if everything could be written to shadow registers. + * returns 1 if not everything could be written to shadow registers. */ +static int configure_dispc(void) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + int i; + int r; + + bool mgr_busy[MAX_DSS_MANAGERS]; + bool mgr_go[MAX_DSS_MANAGERS]; + bool busy; + + r = 0; + busy = false; + + for (i = 0; i < num_mgrs; i++) { + mgr_busy[i] = dispc_go_busy(i); + mgr_go[i] = false; + } + + /* Commit overlay settings */ + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + mc = &dss_cache.manager_cache[oc->channel]; + + if (!oc->dirty) + continue; + + if (oc->manual_update && !mc->do_manual_update) + continue; + + if (mgr_busy[oc->channel]) { + busy = true; + continue; + } + + r = configure_overlay(i); + if (r) + DSSERR("configure_overlay %d failed\n", i); + oc->dirty = false; + oc->shadow_dirty = true; + mgr_go[oc->channel] = true; + } + + /* Commit manager settings */ + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + + if (!mc->dirty) + continue; + + if (mc->manual_update && !mc->do_manual_update) + continue; + + if (mgr_busy[i]) { + busy = true; + continue; + } + + configure_manager(i); + mc->dirty = false; + mc->shadow_dirty = true; + mgr_go[i] = true; + } + + /* set GO */ + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + + if (!mgr_go[i]) + continue; + /* We don't need GO with manual update display. LCD iface will + * always be turned off after frame, and new settings will be + * taken in to use at next update */ + if (!mc->manual_upd_display) + dispc_go(i); + } + + if (busy) + r = 1; + else + r = 0; + + return r; +} + +/* Configure dispc for partial update. Return possibly modified update + * area */ +void dss_setup_partial_planes(struct omap_dss_device *dssdev, + u16 *xi, u16 *yi, u16 *wi, u16 *hi) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + struct omap_overlay_manager *mgr; + int i; + u16 x, y, w, h; + unsigned long flags; + + x = *xi; + y = *yi; + w = *wi; + h = *hi; + + DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", + *xi, *yi, *wi, *hi); + + mgr = dssdev->manager; + + if (!mgr) { + DSSDBG("no manager\n"); + return; + } + + spin_lock_irqsave(&dss_cache.lock, flags); + + /* We need to show the whole overlay if it is scaled. So look for + * those, and make the update area larger if found. + * Also mark the overlay cache dirty */ + for (i = 0; i < num_ovls; ++i) { + unsigned x1, y1, x2, y2; + unsigned outw, outh; + + oc = &dss_cache.overlay_cache[i]; + + if (oc->channel != mgr->id) + continue; + + oc->dirty = true; + + if (!oc->enabled) + continue; + + if (!dispc_is_overlay_scaled(oc)) + continue; + + outw = oc->out_width == 0 ? oc->width : oc->out_width; + outh = oc->out_height == 0 ? oc->height : oc->out_height; + + /* is the overlay outside the update region? */ + if (!rectangle_intersects(x, y, w, h, + oc->pos_x, oc->pos_y, + outw, outh)) + continue; + + /* if the overlay totally inside the update region? */ + if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, + x, y, w, h)) + continue; + + if (x > oc->pos_x) + x1 = oc->pos_x; + else + x1 = x; + + if (y > oc->pos_y) + y1 = oc->pos_y; + else + y1 = y; + + if ((x + w) < (oc->pos_x + outw)) + x2 = oc->pos_x + outw; + else + x2 = x + w; + + if ((y + h) < (oc->pos_y + outh)) + y2 = oc->pos_y + outh; + else + y2 = y + h; + + x = x1; + y = y1; + w = x2 - x1; + h = y2 - y1; + + DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", + i, x, y, w, h); + } + + mc = &dss_cache.manager_cache[mgr->id]; + mc->do_manual_update = true; + mc->x = x; + mc->y = y; + mc->w = w; + mc->h = h; + + configure_dispc(); + + mc->do_manual_update = false; + + spin_unlock_irqrestore(&dss_cache.lock, flags); + + *xi = x; + *yi = y; + *wi = w; + *hi = h; +} + +void dss_start_update(struct omap_dss_device *dssdev) +{ + struct manager_cache_data *mc; + struct overlay_cache_data *oc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + struct omap_overlay_manager *mgr; + int i; + + mgr = dssdev->manager; + + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + if (oc->channel != mgr->id) + continue; + + oc->shadow_dirty = false; + } + + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + if (mgr->id != i) + continue; + + mc->shadow_dirty = false; + } + + dispc_enable_lcd_out(dssdev->channel, 1); +} + +static void dss_apply_irq_handler(void *data, u32 mask) +{ + struct manager_cache_data *mc; + struct overlay_cache_data *oc; + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + int i, r; + bool mgr_busy[MAX_DSS_MANAGERS]; + + for (i = 0; i < num_mgrs; i++) + mgr_busy[i] = dispc_go_busy(i); + + spin_lock(&dss_cache.lock); + + for (i = 0; i < num_ovls; ++i) { + oc = &dss_cache.overlay_cache[i]; + if (!mgr_busy[oc->channel]) + oc->shadow_dirty = false; + } + + for (i = 0; i < num_mgrs; ++i) { + mc = &dss_cache.manager_cache[i]; + if (!mgr_busy[i]) + mc->shadow_dirty = false; + } + + r = configure_dispc(); + if (r == 1) + goto end; + + /* re-read busy flags */ + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + + /* keep running as long as there are busy managers, so that + * we can collect overlay-applied information */ + for (i = 0; i < num_mgrs; ++i) + if (mgr_busy[i]) + goto end; + + omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN +#ifdef CONFIG_ARCH_OMAP4 + | DISPC_IRQ_VSYNC2 +#endif + ); + + dss_cache.irq_enabled = false; + +end: + spin_unlock(&dss_cache.lock); +} + +static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + int i; + struct omap_overlay *ovl; + int num_planes_enabled = 0; + bool use_fifomerge; + unsigned long flags; + int r; + + DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); + + spin_lock_irqsave(&dss_cache.lock, flags); + + /* Configure overlays */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!overlay_enabled(ovl)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + + if (!ovl->info_dirty) { + if (oc->enabled) + ++num_planes_enabled; + continue; + } + + dssdev = ovl->manager->device; + + if (dss_check_overlay(ovl, dssdev)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + + ovl->info_dirty = false; + oc->dirty = true; + + oc->paddr = ovl->info.paddr; +#ifdef CONFIG_ARCH_OMAP4 + oc->p_uv_addr = ovl->info.p_uv_addr; + oc->zorder = ovl->info.zorder; +#endif + oc->vaddr = ovl->info.vaddr; + oc->screen_width = ovl->info.screen_width; + oc->width = ovl->info.width; + oc->height = ovl->info.height; + oc->color_mode = ovl->info.color_mode; + oc->rotation = ovl->info.rotation; + oc->rotation_type = ovl->info.rotation_type; + oc->mirror = ovl->info.mirror; + oc->pos_x = ovl->info.pos_x; + oc->pos_y = ovl->info.pos_y; + oc->out_width = ovl->info.out_width; + oc->out_height = ovl->info.out_height; + oc->global_alpha = ovl->info.global_alpha; + + oc->replication = + dss_use_replication(dssdev, ovl->info.color_mode); + + oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; + + oc->channel = ovl->manager->id; + /* TODO: to change with dssdev->channel? */ + + oc->enabled = true; + + oc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + + ++num_planes_enabled; + + } + /* Configure managers */ + list_for_each_entry(mgr, &manager_list, list) { + struct omap_dss_device *dssdev; + + if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) + continue; + mc = &dss_cache.manager_cache[mgr->id]; + + if (mgr->device_changed) { + mgr->device_changed = false; + mgr->info_dirty = true; + } + + if (!mgr->info_dirty) + continue; + + if (!mgr->device) + continue; + + dssdev = mgr->device; + + mgr->info_dirty = false; + mc->dirty = true; + + mc->default_color = mgr->info.default_color; + mc->trans_key_type = mgr->info.trans_key_type; + mc->trans_key = mgr->info.trans_key; + mc->trans_enabled = mgr->info.trans_enabled; + mc->alpha_enabled = mgr->info.alpha_enabled; + + mc->manual_upd_display = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + + mc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + } + + /* XXX TODO: Try to get fifomerge working. The problem is that it + * affects both managers, not individually but at the same time. This + * means the change has to be well synchronized. I guess the proper way + * is to have a two step process for fifo merge: + * fifomerge enable: + * 1. disable other planes, leaving one plane enabled + * 2. wait until the planes are disabled on HW + * 3. config merged fifo thresholds, enable fifomerge + * fifomerge disable: + * 1. config unmerged fifo thresholds, disable fifomerge + * 2. wait until fifo changes are in HW + * 3. enable planes + */ + use_fifomerge = false; + + /* Configure overlay fifos */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + u32 size; + + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!oc->enabled) + continue; + + dssdev = ovl->manager->device; + + size = dispc_get_plane_fifo_size(ovl->id); + if (use_fifomerge) + size *= 3; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_SDI: + case OMAP_DISPLAY_TYPE_VENC: + default_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; + case OMAP_DISPLAY_TYPE_HDMI: + oc->burst_size = 2; /* DISPC_BURST_8X128*/ + oc->fifo_low = 0xf0; + oc->fifo_high = 0xfc; + break; +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + dsi_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; +#endif + default: + BUG(); + } + } + + r = 0; + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (!dss_cache.irq_enabled) { + r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN +#ifdef CONFIG_ARCH_OMAP4 + | DISPC_IRQ_VSYNC2 +#endif + ); + + dss_cache.irq_enabled = true; + } + configure_dispc(); + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + spin_unlock_irqrestore(&dss_cache.lock, flags); + + return r; +} + +static int dss_check_manager(struct omap_overlay_manager *mgr) +{ + /* OMAP does not support destination color keying and alpha blending + * simultaneously. */ + + if (mgr->info.alpha_enabled && mgr->info.trans_enabled && + mgr->info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST) + return -EINVAL; + + mgr->info_dirty = true; + + return 0; +} + +static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info) +{ + int r; + struct omap_overlay_manager_info old_info; + + old_info = mgr->info; + mgr->info = *info; + + r = dss_check_manager(mgr); + if (r) { + mgr->info = old_info; + return r; + } + + return 0; +} + +static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info) +{ + *info = mgr->info; +} + +static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) +{ + ++num_managers; + list_add_tail(&manager->list, &manager_list); +} + +int dss_init_overlay_managers(struct platform_device *pdev) +{ + int i, r; + + spin_lock_init(&dss_cache.lock); + INIT_LIST_HEAD(&manager_list); + + num_managers = 0; + + for (i = 0; i < MAX_DSS_MANAGERS; ++i) { + struct omap_overlay_manager *mgr; + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + BUG_ON(mgr == NULL); + + switch (i) { + case 0: + mgr->name = "lcd"; + mgr->id = OMAP_DSS_CHANNEL_LCD; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | + OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI; + break; + case 1: + mgr->name = "tv"; + mgr->id = OMAP_DSS_CHANNEL_DIGIT; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_VENC | OMAP_DISPLAY_TYPE_HDMI; + break; +#ifdef CONFIG_ARCH_OMAP4 + case 2: + mgr->name = "lcd2"; + mgr->id = OMAP_DSS_CHANNEL_LCD2; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_SDI | + OMAP_DISPLAY_TYPE_DSI; + break; +#endif + } + + mgr->set_device = &omap_dss_set_device; + mgr->unset_device = &omap_dss_unset_device; + mgr->apply = &omap_dss_mgr_apply; + mgr->set_manager_info = &omap_dss_mgr_set_info; + mgr->get_manager_info = &omap_dss_mgr_get_info; + mgr->wait_for_go = &dss_mgr_wait_for_go; + + mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; + + dss_overlay_setup_dispc_manager(mgr); + + omap_dss_add_overlay_manager(mgr); + + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, + &pdev->dev.kobj, "manager%d", i); + + if (r) { + DSSERR("failed to create sysfs file\n"); + continue; + } + } + +#ifdef L4_EXAMPLE + { + int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) + { + DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); + + return 0; + } + + struct omap_overlay_manager *mgr; + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + BUG_ON(mgr == NULL); + + mgr->name = "l4"; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; + + mgr->set_device = &omap_dss_set_device; + mgr->unset_device = &omap_dss_unset_device; + mgr->apply = &omap_dss_mgr_apply_l4; + mgr->set_manager_info = &omap_dss_mgr_set_info; + mgr->get_manager_info = &omap_dss_mgr_get_info; + + dss_overlay_setup_l4_manager(mgr); + + omap_dss_add_overlay_manager(mgr); + + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, + &pdev->dev.kobj, "managerl4"); + + if (r) + DSSERR("failed to create sysfs file\n"); + } +#endif + + return 0; +} + +void dss_uninit_overlay_managers(struct platform_device *pdev) +{ + struct omap_overlay_manager *mgr; + + while (!list_empty(&manager_list)) { + mgr = list_first_entry(&manager_list, + struct omap_overlay_manager, list); + list_del(&mgr->list); + kobject_del(&mgr->kobj); + kobject_put(&mgr->kobj); + kfree(mgr); + } + + num_managers = 0; +} + +int omap_dss_get_num_overlay_managers(void) +{ + return num_managers; +} +EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); + +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) +{ + int i = 0; + struct omap_overlay_manager *mgr; + + list_for_each_entry(mgr, &manager_list, list) { + if (i++ == num) + return mgr; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_overlay_manager); + +#ifdef L4_EXAMPLE +static int ovl_mgr_apply_l4(struct omap_overlay_manager *mgr) +{ + DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); + return 0; +} +#endif + diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c new file mode 100644 index 000000000000..c1b23ced8d54 --- /dev/null +++ b/drivers/video/omap2/dss/overlay.c @@ -0,0 +1,772 @@ +/* + * linux/drivers/video/omap2/dss/overlay.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "OVERLAY" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/platform_device.h> +#include <linux/delay.h> + +#include <mach/display.h> + +#include "dss.h" + +#ifdef CONFIG_ARCH_OMAP4 +#define MAX_DSS_OVERLAYS 4 +#else +#define MAX_DSS_OVERLAYS 3 +#endif + +static int num_overlays; +static struct list_head overlay_list; + +static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); +} + +static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + ovl->manager ? ovl->manager->name : "<none>"); +} + +static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, + size_t size) +{ + int i, r; + struct omap_overlay_manager *mgr = NULL; + struct omap_overlay_manager *old_mgr; + int len = size; + + if (buf[size-1] == '\n') + --len; + + if (len > 0) { + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + mgr = omap_dss_get_overlay_manager(i); + if (mgr == NULL) + return -EINVAL; + if (strncmp(buf, mgr->name, len) == 0) + break; + + mgr = NULL; + } + } + + if (len > 0 && mgr == NULL) + return -EINVAL; + + if (mgr) + DSSDBG("manager %s found\n", mgr->name); + + if (mgr == ovl->manager) + return size; + + old_mgr = ovl->manager; + + /* detach old manager */ + if (old_mgr) { + r = ovl->unset_manager(ovl); + if (r) { + DSSERR("detach failed\n"); + return r; + } + + r = old_mgr->apply(old_mgr); + if (r) + return r; + } + + if (mgr) { + r = ovl->set_manager(ovl, mgr); + if (r) { + DSSERR("Failed to attach overlay\n"); + return r; + } + + r = mgr->apply(mgr); + if (r) + return r; + } + return size; +} + +static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.width, ovl->info.height); +} + +static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width); +} + +static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.pos_x, ovl->info.pos_y); +} + +static ssize_t overlay_position_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + char *last; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.pos_x = simple_strtoul(buf, &last, 10); + ++last; + if (last - buf >= size) + return -EINVAL; + + info.pos_y = simple_strtoul(last, &last, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d,%d\n", + ovl->info.out_width, ovl->info.out_height); +} + +static ssize_t overlay_output_size_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + char *last; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.out_width = simple_strtoul(buf, &last, 10); + ++last; + if (last - buf >= size) + return -EINVAL; + + info.out_height = simple_strtoul(last, &last, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", (ovl->info.enabled)? "true" : "false"); +} + +static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, + size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.enabled = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + ovl->info.global_alpha); +} + +static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + +#ifndef CONFIG_ARCH_OMAP4 + /* In OMAP2/3: Video1 plane does not support global alpha + * to always make it 255 completely opaque + */ + if (ovl->id == OMAP_DSS_VIDEO1) + info.global_alpha = 255; + else +#endif + info.global_alpha = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +#ifdef CONFIG_ARCH_OMAP4 +static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + ovl->info.zorder); +} + +static ssize_t overlay_zorder_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.zorder = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + +#endif + +struct overlay_attribute { + struct attribute attr; + ssize_t (*show)(struct omap_overlay *, char *); + ssize_t (*store)(struct omap_overlay *, const char *, size_t); +}; + +#define OVERLAY_ATTR(_name, _mode, _show, _store) \ + struct overlay_attribute overlay_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); +static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, + overlay_manager_show, overlay_manager_store); +static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); +static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); +static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, + overlay_position_show, overlay_position_store); +static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, + overlay_output_size_show, overlay_output_size_store); +static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, + overlay_enabled_show, overlay_enabled_store); +static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, + overlay_global_alpha_show, overlay_global_alpha_store); + +#ifdef CONFIG_ARCH_OMAP4 +static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR, + overlay_zorder_show, overlay_zorder_store); +#endif + +static struct attribute *overlay_sysfs_attrs[] = { + &overlay_attr_name.attr, + &overlay_attr_manager.attr, + &overlay_attr_input_size.attr, + &overlay_attr_screen_width.attr, + &overlay_attr_position.attr, + &overlay_attr_output_size.attr, + &overlay_attr_enabled.attr, + &overlay_attr_global_alpha.attr, +#ifdef CONFIG_ARCH_OMAP4 + &overlay_attr_zorder.attr, +#endif + NULL +}; + +static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct omap_overlay *overlay; + struct overlay_attribute *overlay_attr; + + overlay = container_of(kobj, struct omap_overlay, kobj); + overlay_attr = container_of(attr, struct overlay_attribute, attr); + + if (!overlay_attr->show) + return -ENOENT; + + return overlay_attr->show(overlay, buf); +} + +static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + struct omap_overlay *overlay; + struct overlay_attribute *overlay_attr; + + overlay = container_of(kobj, struct omap_overlay, kobj); + overlay_attr = container_of(attr, struct overlay_attribute, attr); + + if (!overlay_attr->store) + return -ENOENT; + + return overlay_attr->store(overlay, buf, size); +} + +static struct sysfs_ops overlay_sysfs_ops = { + .show = overlay_attr_show, + .store = overlay_attr_store, +}; + +static struct kobj_type overlay_ktype = { + .sysfs_ops = &overlay_sysfs_ops, + .default_attrs = overlay_sysfs_attrs, +}; + +/* Check if overlay parameters are compatible with display */ +int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) +{ + struct omap_overlay_info *info; + u16 outw, outh; + u16 dw, dh; + + if (!dssdev) + return 0; + + if (!ovl->info.enabled) + return 0; + + info = &ovl->info; + + if (info->paddr == 0) { + DSSDBG("check_overlay failed: paddr 0\n"); + return -EINVAL; + } + + dssdev->get_resolution(dssdev, &dw, &dh); + + DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n", + ovl->id, + info->pos_x, info->pos_y, + info->width, info->height, + info->out_width, info->out_height, + dw, dh); + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + outw = info->width; + outh = info->height; + } else { + if (info->out_width == 0) + outw = info->width; + else + outw = info->out_width; + + if (info->out_height == 0) + outh = info->height; + else + outh = info->out_height; + } + + if (dw < info->pos_x + outw) { + DSSDBG("check_overlay failed 1: %d < %d + %d\n", + dw, info->pos_x, outw); + return -EINVAL; + } + + if (dh < info->pos_y + outh) { + DSSDBG("check_overlay failed 2: %d < %d + %d\n", + dh, info->pos_y, outh); + return -EINVAL; + } + + if ((ovl->supported_modes & info->color_mode) == 0) { + DSSERR("overlay doesn't support mode %d\n", info->color_mode); + return -EINVAL; + } + +#ifdef CONFIG_ARCH_OMAP4 + if ((info->zorder < OMAP_DSS_OVL_ZORDER_0) || + (info->zorder > OMAP_DSS_OVL_ZORDER_3)) { + DSSERR("overlay doesn't support zorder %d\n", info->zorder); + return -EINVAL; + } + +#endif + + return 0; +} + +static int dss_ovl_set_overlay_info(struct omap_overlay *ovl, + struct omap_overlay_info *info) +{ + int r; + struct omap_overlay_info old_info; + + old_info = ovl->info; + ovl->info = *info; + + if (ovl->manager) { + r = dss_check_overlay(ovl, ovl->manager->device); + if (r) { + ovl->info = old_info; + return r; + } + } + + ovl->info_dirty = true; + + return 0; +} + +static void dss_ovl_get_overlay_info(struct omap_overlay *ovl, + struct omap_overlay_info *info) +{ + *info = ovl->info; +} + +static int dss_ovl_wait_for_go(struct omap_overlay *ovl) +{ + return dss_mgr_wait_for_go_ovl(ovl); +} + +static int omap_dss_set_manager(struct omap_overlay *ovl, + struct omap_overlay_manager *mgr) +{ + + if (!mgr) + return -EINVAL; + + if (ovl->manager) { + DSSERR("overlay '%s' already has a manager '%s'\n", + ovl->name, ovl->manager->name); + return -EINVAL; + } + + + if (ovl->info.enabled) { + DSSERR("overlay has to be disabled to change the manager\n"); + return -EINVAL; + } + + ovl->manager = mgr; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + /* XXX: on manual update display, in auto update mode, a bug happens + * here. When an overlay is first enabled on LCD, then it's disabled, + * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT + * errors. Waiting before changing the channel_out fixes it. I'm + * guessing that the overlay is still somehow being used for the LCD, + * but I don't understand how or why. */ + msleep(40); + dispc_set_channel_out(ovl->id, mgr->id); + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + return 0; +} + +static int omap_dss_unset_manager(struct omap_overlay *ovl) +{ + int r; + if (!ovl->manager) { + DSSERR("failed to detach overlay: manager not set\n"); + return -EINVAL; + } + + if (ovl->info.enabled) { + DSSERR("overlay has to be disabled to unset the manager\n"); + return -EINVAL; + } + + r = ovl->wait_for_go(ovl); + if (r) + return r; + ovl->manager = NULL; + /* XXX disable overlay? */ + + return 0; +} + +int omap_dss_get_num_overlays(void) +{ + return num_overlays; +} +EXPORT_SYMBOL(omap_dss_get_num_overlays); + +struct omap_overlay *omap_dss_get_overlay(int num) +{ + int i = 0; + struct omap_overlay *ovl; + + list_for_each_entry(ovl, &overlay_list, list) { + if (i++ == num) + return ovl; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_overlay); + +static void omap_dss_add_overlay(struct omap_overlay *overlay) +{ + ++num_overlays; + list_add_tail(&overlay->list, &overlay_list); +} + +static struct omap_overlay *dispc_overlays[MAX_DSS_OVERLAYS]; + +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) +{ + mgr->num_overlays = MAX_DSS_OVERLAYS; + mgr->overlays = dispc_overlays; +} + +#ifdef L4_EXAMPLE +static struct omap_overlay *l4_overlays[1]; +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr) +{ + mgr->num_overlays = 1; + mgr->overlays = l4_overlays; +} +#endif + +void dss_init_overlays(struct platform_device *pdev) +{ + int i, r; + + INIT_LIST_HEAD(&overlay_list); + + num_overlays = 0; + + for (i = 0; i < MAX_DSS_OVERLAYS; ++i) { + struct omap_overlay *ovl; + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); + + BUG_ON(ovl == NULL); + + switch (i) { + case 0: + ovl->name = "gfx"; + ovl->id = OMAP_DSS_GFX; + ovl->supported_modes = OMAP_DSS_COLOR_GFX_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; +#ifdef CONFIG_ARCH_OMAP4 + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_0; +#endif + break; + case 1: + ovl->name = "vid1"; + ovl->id = OMAP_DSS_VIDEO1; + ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; +#ifdef CONFIG_ARCH_OMAP4 + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_3; +#endif + break; + case 2: + ovl->name = "vid2"; + ovl->id = OMAP_DSS_VIDEO2; + ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; +#ifdef CONFIG_ARCH_OMAP4 + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_2; +#endif + break; +#ifdef CONFIG_ARCH_OMAP4 + case 3: + ovl->name = "vid3"; + ovl->id = OMAP_DSS_VIDEO3; + ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; +#ifdef CONFIG_ARCH_OMAP4 + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_1; +#endif + + break; +#endif + } + + ovl->set_manager = &omap_dss_set_manager; + ovl->unset_manager = &omap_dss_unset_manager; + ovl->set_overlay_info = &dss_ovl_set_overlay_info; + ovl->get_overlay_info = &dss_ovl_get_overlay_info; + ovl->wait_for_go = &dss_ovl_wait_for_go; + + omap_dss_add_overlay(ovl); + + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, + &pdev->dev.kobj, "overlay%d", i); + + if (r) { + DSSERR("failed to create sysfs file\n"); + continue; + } + + dispc_overlays[i] = ovl; + } + +#ifdef L4_EXAMPLE + { + struct omap_overlay *ovl; + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); + + BUG_ON(ovl == NULL); + + ovl->name = "l4"; + ovl->supported_modes = OMAP_DSS_COLOR_RGB24U; + + ovl->set_manager = &omap_dss_set_manager; + ovl->unset_manager = &omap_dss_unset_manager; + ovl->set_overlay_info = &dss_ovl_set_overlay_info; + ovl->get_overlay_info = &dss_ovl_get_overlay_info; + + omap_dss_add_overlay(ovl); + + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, + &pdev->dev.kobj, "overlayl4"); + + if (r) + DSSERR("failed to create sysfs file\n"); + + l4_overlays[0] = ovl; + } +#endif +} + +/* connect overlays to the new device, if not already connected. if force + * selected, connect always. */ +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) +{ + int i; + struct omap_overlay_manager *lcd_mgr; + struct omap_overlay_manager *tv_mgr; +#ifdef CONFIG_ARCH_OMAP4 + struct omap_overlay_manager *lcd2_mgr; +#endif + struct omap_overlay_manager *mgr = NULL; + + lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); + tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); + +#ifdef CONFIG_ARCH_OMAP4 + lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2); + + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { + if (!lcd2_mgr->device || force) { + if (lcd2_mgr->device) + lcd2_mgr->unset_device(lcd2_mgr); + lcd2_mgr->set_device(lcd2_mgr, dssdev); + mgr = lcd2_mgr; + } + } else +#endif + + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC + && dssdev->type != OMAP_DISPLAY_TYPE_HDMI) { + if (!lcd_mgr->device || force) { + if (lcd_mgr->device) + lcd_mgr->unset_device(lcd_mgr); + lcd_mgr->set_device(lcd_mgr, dssdev); + mgr = lcd_mgr; + } + } + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { + if (!tv_mgr->device || force) { + if (tv_mgr->device) + tv_mgr->unset_device(tv_mgr); + tv_mgr->set_device(tv_mgr, dssdev); + mgr = tv_mgr; + } + } + + if (mgr) { + for (i = 0; i < MAX_DSS_OVERLAYS; i++) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + if (!ovl->manager || force) { + if (ovl->manager) + omap_dss_unset_manager(ovl); + omap_dss_set_manager(ovl, mgr); + } + } + } +} + +void dss_uninit_overlays(struct platform_device *pdev) +{ + struct omap_overlay *ovl; + + while (!list_empty(&overlay_list)) { + ovl = list_first_entry(&overlay_list, + struct omap_overlay, list); + list_del(&ovl->list); + kobject_del(&ovl->kobj); + kobject_put(&ovl->kobj); + kfree(ovl); + } + + num_overlays = 0; +} + diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c new file mode 100644 index 000000000000..f2436f8d60f0 --- /dev/null +++ b/drivers/video/omap2/dss/rfbi.c @@ -0,0 +1,1315 @@ +/* + * linux/drivers/video/omap2/dss/rfbi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "RFBI" + +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/kfifo.h> +#include <linux/ktime.h> +#include <linux/hrtimer.h> +#include <linux/seq_file.h> + +#include <mach/board.h> +#include <mach/display.h> +#include "dss.h" + +/*#define MEASURE_PERF*/ + +#define RFBI_BASE 0x48050800 + +struct rfbi_reg { u16 idx; }; + +#define RFBI_REG(idx) ((const struct rfbi_reg) { idx }) + +#define RFBI_REVISION RFBI_REG(0x0000) +#define RFBI_SYSCONFIG RFBI_REG(0x0010) +#define RFBI_SYSSTATUS RFBI_REG(0x0014) +#define RFBI_CONTROL RFBI_REG(0x0040) +#define RFBI_PIXEL_CNT RFBI_REG(0x0044) +#define RFBI_LINE_NUMBER RFBI_REG(0x0048) +#define RFBI_CMD RFBI_REG(0x004c) +#define RFBI_PARAM RFBI_REG(0x0050) +#define RFBI_DATA RFBI_REG(0x0054) +#define RFBI_READ RFBI_REG(0x0058) +#define RFBI_STATUS RFBI_REG(0x005c) + +#define RFBI_CONFIG(n) RFBI_REG(0x0060 + (n)*0x18) +#define RFBI_ONOFF_TIME(n) RFBI_REG(0x0064 + (n)*0x18) +#define RFBI_CYCLE_TIME(n) RFBI_REG(0x0068 + (n)*0x18) +#define RFBI_DATA_CYCLE1(n) RFBI_REG(0x006c + (n)*0x18) +#define RFBI_DATA_CYCLE2(n) RFBI_REG(0x0070 + (n)*0x18) +#define RFBI_DATA_CYCLE3(n) RFBI_REG(0x0074 + (n)*0x18) + +#define RFBI_VSYNC_WIDTH RFBI_REG(0x0090) +#define RFBI_HSYNC_WIDTH RFBI_REG(0x0094) + +#define RFBI_CMD_FIFO_LEN_BYTES (16 * sizeof(struct update_param)) + +#define REG_FLD_MOD(idx, val, start, end) \ + rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) + +/* To work around an RFBI transfer rate limitation */ +#define OMAP_RFBI_RATE_LIMIT 1 + +enum omap_rfbi_cycleformat { + OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, + OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, + OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2, + OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3, +}; + +enum omap_rfbi_datatype { + OMAP_DSS_RFBI_DATATYPE_12 = 0, + OMAP_DSS_RFBI_DATATYPE_16 = 1, + OMAP_DSS_RFBI_DATATYPE_18 = 2, + OMAP_DSS_RFBI_DATATYPE_24 = 3, +}; + +enum omap_rfbi_parallelmode { + OMAP_DSS_RFBI_PARALLELMODE_8 = 0, + OMAP_DSS_RFBI_PARALLELMODE_9 = 1, + OMAP_DSS_RFBI_PARALLELMODE_12 = 2, + OMAP_DSS_RFBI_PARALLELMODE_16 = 3, +}; + +enum update_cmd { + RFBI_CMD_UPDATE = 0, + RFBI_CMD_SYNC = 1, +}; + +static int rfbi_convert_timings(struct rfbi_timings *t); +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); +static void process_cmd_fifo(void); + +static struct { + void __iomem *base; + + unsigned long l4_khz; + + enum omap_rfbi_datatype datatype; + enum omap_rfbi_parallelmode parallelmode; + + enum omap_rfbi_te_mode te_mode; + int te_enabled; + + void (*framedone_callback)(void *data); + void *framedone_callback_data; + + struct omap_dss_device *dssdev[2]; + + struct kfifo *cmd_fifo; + spinlock_t cmd_lock; + struct completion cmd_done; + atomic_t cmd_fifo_full; + atomic_t cmd_pending; +#ifdef MEASURE_PERF + unsigned perf_bytes; + ktime_t perf_setup_time; + ktime_t perf_start_time; +#endif +} rfbi; + +struct update_region { + u16 x; + u16 y; + u16 w; + u16 h; +}; + +struct update_param { + u8 rfbi_module; + u8 cmd; + + union { + struct update_region r; + struct completion *sync; + } par; +}; + +static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) +{ + __raw_writel(val, rfbi.base + idx.idx); +} + +static inline u32 rfbi_read_reg(const struct rfbi_reg idx) +{ + return __raw_readl(rfbi.base + idx.idx); +} + +static void rfbi_enable_clocks(bool enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +void omap_rfbi_write_command(const void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + const u8 *b = buf; + for (; len; len--) + rfbi_write_reg(RFBI_CMD, *b++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_CMD, *w++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_command); + +void omap_rfbi_read_data(void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + u8 *b = buf; + for (; len; len--) { + rfbi_write_reg(RFBI_READ, 0); + *b++ = rfbi_read_reg(RFBI_READ); + } + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + u16 *w = buf; + BUG_ON(len & ~1); + for (; len; len -= 2) { + rfbi_write_reg(RFBI_READ, 0); + *w++ = rfbi_read_reg(RFBI_READ); + } + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_read_data); + +void omap_rfbi_write_data(const void *buf, u32 len) +{ + rfbi_enable_clocks(1); + switch (rfbi.parallelmode) { + case OMAP_DSS_RFBI_PARALLELMODE_8: + { + const u8 *b = buf; + for (; len; len--) + rfbi_write_reg(RFBI_PARAM, *b++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_16: + { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_PARAM, *w++); + break; + } + + case OMAP_DSS_RFBI_PARALLELMODE_9: + case OMAP_DSS_RFBI_PARALLELMODE_12: + default: + BUG(); + + } + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_data); + +void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, + u16 x, u16 y, + u16 w, u16 h) +{ + int start_offset = scr_width * y + x; + int horiz_offset = scr_width - w; + int i; + + rfbi_enable_clocks(1); + + if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { + const u16 __iomem *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + const u8 __iomem *b = (const u8 __iomem *)pd; + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); + ++pd; + } + pd += horiz_offset; + } + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { + const u32 __iomem *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + const u8 __iomem *b = (const u8 __iomem *)pd; + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+2)); + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); + ++pd; + } + pd += horiz_offset; + } + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) { + const u16 __iomem *pd = buf; + pd += start_offset; + + for (; h; --h) { + for (i = 0; i < w; ++i) { + rfbi_write_reg(RFBI_PARAM, __raw_readw(pd)); + ++pd; + } + pd += horiz_offset; + } + } else { + BUG(); + } + + rfbi_enable_clocks(0); +} +EXPORT_SYMBOL(omap_rfbi_write_pixels); + +#ifdef MEASURE_PERF +static void perf_mark_setup(void) +{ + rfbi.perf_setup_time = ktime_get(); +} + +static void perf_mark_start(void) +{ + rfbi.perf_start_time = ktime_get(); +} + +static void perf_show(const char *name) +{ + ktime_t t, setup_time, trans_time; + u32 total_bytes; + u32 setup_us, trans_us, total_us; + + t = ktime_get(); + + setup_time = ktime_sub(rfbi.perf_start_time, rfbi.perf_setup_time); + setup_us = (u32)ktime_to_us(setup_time); + if (setup_us == 0) + setup_us = 1; + + trans_time = ktime_sub(t, rfbi.perf_start_time); + trans_us = (u32)ktime_to_us(trans_time); + if (trans_us == 0) + trans_us = 1; + + total_us = setup_us + trans_us; + + total_bytes = rfbi.perf_bytes; + + DSSINFO("%s update %u us + %u us = %u us (%uHz), %u bytes, " + "%u kbytes/sec\n", + name, + setup_us, + trans_us, + total_us, + 1000*1000 / total_us, + total_bytes, + total_bytes * 1000 / total_us); +} +#else +#define perf_mark_setup() +#define perf_mark_start() +#define perf_show(x) +#endif + +void rfbi_transfer_area(u16 width, u16 height, + void (callback)(void *data), void *data) +{ + u32 l; + + /*BUG_ON(callback == 0);*/ + BUG_ON(rfbi.framedone_callback != NULL); + + DSSDBG("rfbi_transfer_area %dx%d\n", width, height); + + dispc_set_lcd_size(OMAP_DSS_CHANNEL_LCD, width, height); + /* TODO: update for LCD2*/ + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 1); + + rfbi.framedone_callback = callback; + rfbi.framedone_callback_data = data; + + rfbi_enable_clocks(1); + + rfbi_write_reg(RFBI_PIXEL_CNT, width * height); + + l = rfbi_read_reg(RFBI_CONTROL); + l = FLD_MOD(l, 1, 0, 0); /* enable */ + if (!rfbi.te_enabled) + l = FLD_MOD(l, 1, 4, 4); /* ITE */ + + perf_mark_start(); + + rfbi_write_reg(RFBI_CONTROL, l); +} + +static void framedone_callback(void *data, u32 mask) +{ + void (*callback)(void *data); + + DSSDBG("FRAMEDONE\n"); + + perf_show("DISPC"); + + REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); + + rfbi_enable_clocks(0); + + callback = rfbi.framedone_callback; + rfbi.framedone_callback = NULL; + + /*callback(rfbi.framedone_callback_data);*/ + + atomic_set(&rfbi.cmd_pending, 0); + + process_cmd_fifo(); +} + +#if 1 /* VERBOSE */ +static void rfbi_print_timings(void) +{ + u32 l; + u32 time; + + l = rfbi_read_reg(RFBI_CONFIG(0)); + time = 1000000000 / rfbi.l4_khz; + if (l & (1 << 4)) + time *= 2; + + DSSDBG("Tick time %u ps\n", time); + l = rfbi_read_reg(RFBI_ONOFF_TIME(0)); + DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " + "REONTIME %d, REOFFTIME %d\n", + l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, + (l >> 20) & 0x0f, (l >> 24) & 0x3f); + + l = rfbi_read_reg(RFBI_CYCLE_TIME(0)); + DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " + "ACCESSTIME %d\n", + (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, + (l >> 22) & 0x3f); +} +#else +static void rfbi_print_timings(void) {} +#endif + + + + +static u32 extif_clk_period; + +static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) +{ + int bus_tick = extif_clk_period * div; + return (ps + bus_tick - 1) / bus_tick * bus_tick; +} + +static int calc_reg_timing(struct rfbi_timings *t, int div) +{ + t->clk_div = div; + + t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div); + + t->we_on_time = round_to_extif_ticks(t->we_on_time, div); + t->we_off_time = round_to_extif_ticks(t->we_off_time, div); + t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div); + + t->re_on_time = round_to_extif_ticks(t->re_on_time, div); + t->re_off_time = round_to_extif_ticks(t->re_off_time, div); + t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div); + + t->access_time = round_to_extif_ticks(t->access_time, div); + t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div); + t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div); + + DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + DSSDBG("[reg]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return rfbi_convert_timings(t); +} + +static int calc_extif_timings(struct rfbi_timings *t) +{ + u32 max_clk_div; + int div; + + rfbi_get_clk_info(&extif_clk_period, &max_clk_div); + for (div = 1; div <= max_clk_div; div++) { + if (calc_reg_timing(t, div) == 0) + break; + } + + if (div <= max_clk_div) + return 0; + + DSSERR("can't setup timings\n"); + return -1; +} + + +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) +{ + int r; + + if (!t->converted) { + r = calc_extif_timings(t); + if (r < 0) + DSSERR("Failed to calc timings\n"); + } + + BUG_ON(!t->converted); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); + rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); + + /* TIMEGRANULARITY */ + REG_FLD_MOD(RFBI_CONFIG(rfbi_module), + (t->tim[2] ? 1 : 0), 4, 4); + + rfbi_print_timings(); + rfbi_enable_clocks(0); +} + +static int ps_to_rfbi_ticks(int time, int div) +{ + unsigned long tick_ps; + int ret; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = 1000000000 / (rfbi.l4_khz) * div; + + ret = (time + tick_ps - 1) / tick_ps; + + return ret; +} + +#ifdef OMAP_RFBI_RATE_LIMIT +unsigned long rfbi_get_max_tx_rate(void) +{ + unsigned long l4_rate, dss1_rate; + int min_l4_ticks = 0; + int i; + + /* According to TI this can't be calculated so make the + * adjustments for a couple of known frequencies and warn for + * others. + */ + static const struct { + unsigned long l4_clk; /* HZ */ + unsigned long dss1_clk; /* HZ */ + unsigned long min_l4_ticks; + } ftab[] = { + { 55, 132, 7, }, /* 7.86 MPix/s */ + { 110, 110, 12, }, /* 9.16 MPix/s */ + { 110, 132, 10, }, /* 11 Mpix/s */ + { 120, 120, 10, }, /* 12 Mpix/s */ + { 133, 133, 10, }, /* 13.3 Mpix/s */ + }; + + l4_rate = rfbi.l4_khz / 1000; + dss1_rate = dss_clk_get_rate(DSS_CLK_FCK1) / 1000000; + + for (i = 0; i < ARRAY_SIZE(ftab); i++) { + /* Use a window instead of an exact match, to account + * for different DPLL multiplier / divider pairs. + */ + if (abs(ftab[i].l4_clk - l4_rate) < 3 && + abs(ftab[i].dss1_clk - dss1_rate) < 3) { + min_l4_ticks = ftab[i].min_l4_ticks; + break; + } + } + if (i == ARRAY_SIZE(ftab)) { + /* Can't be sure, return anyway the maximum not + * rate-limited. This might cause a problem only for the + * tearing synchronisation. + */ + DSSERR("can't determine maximum RFBI transfer rate\n"); + return rfbi.l4_khz * 1000; + } + return rfbi.l4_khz * 1000 / min_l4_ticks; +} +#else +int rfbi_get_max_tx_rate(void) +{ + return rfbi.l4_khz * 1000; +} +#endif + +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = 1000000000 / rfbi.l4_khz; + *max_clk_div = 2; +} + +static int rfbi_convert_timings(struct rfbi_timings *t) +{ + u32 l; + int reon, reoff, weon, weoff, cson, csoff, cs_pulse; + int actim, recyc, wecyc; + int div = t->clk_div; + + if (div <= 0 || div > 2) + return -1; + + /* Make sure that after conversion it still holds that: + * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, + * csoff > cson, csoff >= max(weoff, reoff), actim > reon + */ + weon = ps_to_rfbi_ticks(t->we_on_time, div); + weoff = ps_to_rfbi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + if (weon > 0x0f) + return -1; + if (weoff > 0x3f) + return -1; + + reon = ps_to_rfbi_ticks(t->re_on_time, div); + reoff = ps_to_rfbi_ticks(t->re_off_time, div); + if (reoff <= reon) + reoff = reon + 1; + if (reon > 0x0f) + return -1; + if (reoff > 0x3f) + return -1; + + cson = ps_to_rfbi_ticks(t->cs_on_time, div); + csoff = ps_to_rfbi_ticks(t->cs_off_time, div); + if (csoff <= cson) + csoff = cson + 1; + if (csoff < max(weoff, reoff)) + csoff = max(weoff, reoff); + if (cson > 0x0f) + return -1; + if (csoff > 0x3f) + return -1; + + l = cson; + l |= csoff << 4; + l |= weon << 10; + l |= weoff << 14; + l |= reon << 20; + l |= reoff << 24; + + t->tim[0] = l; + + actim = ps_to_rfbi_ticks(t->access_time, div); + if (actim <= reon) + actim = reon + 1; + if (actim > 0x3f) + return -1; + + wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); + if (wecyc < weoff) + wecyc = weoff; + if (wecyc > 0x3f) + return -1; + + recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); + if (recyc < reoff) + recyc = reoff; + if (recyc > 0x3f) + return -1; + + cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); + if (cs_pulse > 0x3f) + return -1; + + l = wecyc; + l |= recyc << 6; + l |= cs_pulse << 12; + l |= actim << 22; + + t->tim[1] = l; + + t->tim[2] = div - 1; + + t->converted = 1; + + return 0; +} + +/* xxx FIX module selection missing */ +int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int extif_div) +{ + int hs, vs; + int min; + u32 l; + + hs = ps_to_rfbi_ticks(hs_pulse_time, 1); + vs = ps_to_rfbi_ticks(vs_pulse_time, 1); + if (hs < 2) + return -EDOM; + if (mode == OMAP_DSS_RFBI_TE_MODE_2) + min = 2; + else /* OMAP_DSS_RFBI_TE_MODE_1 */ + min = 4; + if (vs < min) + return -EDOM; + if (vs == hs) + return -EINVAL; + rfbi.te_mode = mode; + DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", + mode, hs, vs, hs_pol_inv, vs_pol_inv); + + rfbi_enable_clocks(1); + rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); + rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); + + l = rfbi_read_reg(RFBI_CONFIG(0)); + if (hs_pol_inv) + l &= ~(1 << 21); + else + l |= 1 << 21; + if (vs_pol_inv) + l &= ~(1 << 20); + else + l |= 1 << 20; + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(omap_rfbi_setup_te); + +/* xxx FIX module selection missing */ +int omap_rfbi_enable_te(bool enable, unsigned line) +{ + u32 l; + + DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode); + if (line > (1 << 11) - 1) + return -EINVAL; + + rfbi_enable_clocks(1); + l = rfbi_read_reg(RFBI_CONFIG(0)); + l &= ~(0x3 << 2); + if (enable) { + rfbi.te_enabled = 1; + l |= rfbi.te_mode << 2; + } else + rfbi.te_enabled = 0; + rfbi_write_reg(RFBI_CONFIG(0), l); + rfbi_write_reg(RFBI_LINE_NUMBER, line); + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(omap_rfbi_enable_te); + +#if 0 +static void rfbi_enable_config(int enable1, int enable2) +{ + u32 l; + int cs = 0; + + if (enable1) + cs |= 1<<0; + if (enable2) + cs |= 1<<1; + + rfbi_enable_clocks(1); + + l = rfbi_read_reg(RFBI_CONTROL); + + l = FLD_MOD(l, cs, 3, 2); + l = FLD_MOD(l, 0, 1, 1); + + rfbi_write_reg(RFBI_CONTROL, l); + + + l = rfbi_read_reg(RFBI_CONFIG(0)); + l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */ + /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ + /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */ + + l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */ + l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */ + l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */ + + l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0); + rfbi_write_reg(RFBI_CONFIG(0), l); + + rfbi_enable_clocks(0); +} +#endif + +int rfbi_configure(int rfbi_module, int bpp, int lines) +{ + u32 l; + int cycle1 = 0, cycle2 = 0, cycle3 = 0; + enum omap_rfbi_cycleformat cycleformat; + enum omap_rfbi_datatype datatype; + enum omap_rfbi_parallelmode parallelmode; + + switch (bpp) { + case 12: + datatype = OMAP_DSS_RFBI_DATATYPE_12; + break; + case 16: + datatype = OMAP_DSS_RFBI_DATATYPE_16; + break; + case 18: + datatype = OMAP_DSS_RFBI_DATATYPE_18; + break; + case 24: + datatype = OMAP_DSS_RFBI_DATATYPE_24; + break; + default: + BUG(); + return 1; + } + rfbi.datatype = datatype; + + switch (lines) { + case 8: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8; + break; + case 9: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9; + break; + case 12: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12; + break; + case 16: + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16; + break; + default: + BUG(); + return 1; + } + rfbi.parallelmode = parallelmode; + + if ((bpp % lines) == 0) { + switch (bpp / lines) { + case 1: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1; + break; + case 2: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1; + break; + case 3: + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1; + break; + default: + BUG(); + return 1; + } + } else if ((2 * bpp % lines) == 0) { + if ((2 * bpp / lines) == 3) + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2; + else { + BUG(); + return 1; + } + } else { + BUG(); + return 1; + } + + switch (cycleformat) { + case OMAP_DSS_RFBI_CYCLEFORMAT_1_1: + cycle1 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_2_1: + cycle1 = lines; + cycle2 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_3_1: + cycle1 = lines; + cycle2 = lines; + cycle3 = lines; + break; + + case OMAP_DSS_RFBI_CYCLEFORMAT_3_2: + cycle1 = lines; + cycle2 = (lines / 2) | ((lines / 2) << 16); + cycle3 = (lines << 16); + break; + } + + rfbi_enable_clocks(1); + + REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ + + l = 0; + l |= FLD_VAL(parallelmode, 1, 0); + l |= FLD_VAL(0, 3, 2); /* TRIGGERMODE: ITE */ + l |= FLD_VAL(0, 4, 4); /* TIMEGRANULARITY */ + l |= FLD_VAL(datatype, 6, 5); + /* l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ + l |= FLD_VAL(0, 8, 7); /* L4FORMAT, 1pix/L4 */ + l |= FLD_VAL(cycleformat, 10, 9); + l |= FLD_VAL(0, 12, 11); /* UNUSEDBITS */ + l |= FLD_VAL(0, 16, 16); /* A0POLARITY */ + l |= FLD_VAL(0, 17, 17); /* REPOLARITY */ + l |= FLD_VAL(0, 18, 18); /* WEPOLARITY */ + l |= FLD_VAL(0, 19, 19); /* CSPOLARITY */ + l |= FLD_VAL(1, 20, 20); /* TE_VSYNC_POLARITY */ + l |= FLD_VAL(1, 21, 21); /* HSYNCPOLARITY */ + rfbi_write_reg(RFBI_CONFIG(rfbi_module), l); + + rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1); + rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2); + rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3); + + + l = rfbi_read_reg(RFBI_CONTROL); + l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */ + l = FLD_MOD(l, 0, 1, 1); /* clear bypass */ + rfbi_write_reg(RFBI_CONTROL, l); + + + DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", + bpp, lines, cycle1, cycle2, cycle3); + + rfbi_enable_clocks(0); + + return 0; +} +EXPORT_SYMBOL(rfbi_configure); + +static int rfbi_find_display(struct omap_dss_device *dssdev) +{ + if (dssdev == rfbi.dssdev[0]) + return 0; + + if (dssdev == rfbi.dssdev[1]) + return 1; + + BUG(); + return -1; +} + + +static void signal_fifo_waiters(void) +{ + if (atomic_read(&rfbi.cmd_fifo_full) > 0) { + /* DSSDBG("SIGNALING: Fifo not full for waiter!\n"); */ + complete(&rfbi.cmd_done); + atomic_dec(&rfbi.cmd_fifo_full); + } +} + +/* returns 1 for async op, and 0 for sync op */ +static int do_update(struct omap_dss_device *dssdev, struct update_region *upd) +{ + u16 x = upd->x; + u16 y = upd->y; + u16 w = upd->w; + u16 h = upd->h; + + perf_mark_setup(); + + if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + /*dssdev->driver->enable_te(dssdev, 1); */ + dss_setup_partial_planes(dssdev, &x, &y, &w, &h); + } + +#ifdef MEASURE_PERF + rfbi.perf_bytes = w * h * 2; /* XXX always 16bit */ +#endif + + dssdev->driver->setup_update(dssdev, x, y, w, h); + + if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { + rfbi_transfer_area(w, h, NULL, NULL); + return 1; + } else { + struct omap_overlay *ovl; + void __iomem *addr; + int scr_width; + + ovl = dssdev->manager->overlays[0]; + scr_width = ovl->info.screen_width; + addr = ovl->info.vaddr; + + omap_rfbi_write_pixels(addr, scr_width, x, y, w, h); + + perf_show("L4"); + + return 0; + } +} + +static void process_cmd_fifo(void) +{ + int len; + struct update_param p; + struct omap_dss_device *dssdev; + unsigned long flags; + + if (atomic_inc_return(&rfbi.cmd_pending) != 1) + return; + + while (true) { + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); + + len = __kfifo_get(rfbi.cmd_fifo, (unsigned char *)&p, + sizeof(struct update_param)); + if (len == 0) { + DSSDBG("nothing more in fifo\n"); + atomic_set(&rfbi.cmd_pending, 0); + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + break; + } + + /* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/ + + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + + BUG_ON(len != sizeof(struct update_param)); + BUG_ON(p.rfbi_module > 1); + + dssdev = rfbi.dssdev[p.rfbi_module]; + + if (p.cmd == RFBI_CMD_UPDATE) { + if (do_update(dssdev, &p.par.r)) + break; /* async op */ + } else if (p.cmd == RFBI_CMD_SYNC) { + DSSDBG("Signaling SYNC done!\n"); + complete(p.par.sync); + } else + BUG(); + } + + signal_fifo_waiters(); +} + +static void rfbi_push_cmd(struct update_param *p) +{ + int ret; + + while (1) { + unsigned long flags; + int available; + + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); + available = RFBI_CMD_FIFO_LEN_BYTES - + __kfifo_len(rfbi.cmd_fifo); + +/* DSSDBG("%d bytes left in fifo\n", available); */ + if (available < sizeof(struct update_param)) { + DSSDBG("Going to wait because FIFO FULL..\n"); + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + atomic_inc(&rfbi.cmd_fifo_full); + wait_for_completion(&rfbi.cmd_done); + /*DSSDBG("Woke up because fifo not full anymore\n");*/ + continue; + } + + ret = __kfifo_put(rfbi.cmd_fifo, (unsigned char *)p, + sizeof(struct update_param)); +/* DSSDBG("pushed %d bytes\n", ret);*/ + + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); + + BUG_ON(ret != sizeof(struct update_param)); + + break; + } +} + +static void rfbi_push_update(int rfbi_module, int x, int y, int w, int h) +{ + struct update_param p; + + p.rfbi_module = rfbi_module; + p.cmd = RFBI_CMD_UPDATE; + + p.par.r.x = x; + p.par.r.y = y; + p.par.r.w = w; + p.par.r.h = h; + + DSSDBG("RFBI pushed %d,%d %dx%d\n", x, y, w, h); + + rfbi_push_cmd(&p); + + process_cmd_fifo(); +} + +static void rfbi_push_sync(int rfbi_module, struct completion *sync_comp) +{ + struct update_param p; + + p.rfbi_module = rfbi_module; + p.cmd = RFBI_CMD_SYNC; + p.par.sync = sync_comp; + + rfbi_push_cmd(&p); + + DSSDBG("RFBI sync pushed to cmd fifo\n"); + + process_cmd_fifo(); +} + +void rfbi_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + DUMPREG(RFBI_REVISION); + DUMPREG(RFBI_SYSCONFIG); + DUMPREG(RFBI_SYSSTATUS); + DUMPREG(RFBI_CONTROL); + DUMPREG(RFBI_PIXEL_CNT); + DUMPREG(RFBI_LINE_NUMBER); + DUMPREG(RFBI_CMD); + DUMPREG(RFBI_PARAM); + DUMPREG(RFBI_DATA); + DUMPREG(RFBI_READ); + DUMPREG(RFBI_STATUS); + + DUMPREG(RFBI_CONFIG(0)); + DUMPREG(RFBI_ONOFF_TIME(0)); + DUMPREG(RFBI_CYCLE_TIME(0)); + DUMPREG(RFBI_DATA_CYCLE1(0)); + DUMPREG(RFBI_DATA_CYCLE2(0)); + DUMPREG(RFBI_DATA_CYCLE3(0)); + + DUMPREG(RFBI_CONFIG(1)); + DUMPREG(RFBI_ONOFF_TIME(1)); + DUMPREG(RFBI_CYCLE_TIME(1)); + DUMPREG(RFBI_DATA_CYCLE1(1)); + DUMPREG(RFBI_DATA_CYCLE2(1)); + DUMPREG(RFBI_DATA_CYCLE3(1)); + + DUMPREG(RFBI_VSYNC_WIDTH); + DUMPREG(RFBI_HSYNC_WIDTH); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +int rfbi_init(void) +{ + u32 rev; + u32 l; + + spin_lock_init(&rfbi.cmd_lock); + rfbi.cmd_fifo = kfifo_alloc(RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL, + &rfbi.cmd_lock); + if (IS_ERR(rfbi.cmd_fifo)) + return -ENOMEM; + + init_completion(&rfbi.cmd_done); + atomic_set(&rfbi.cmd_fifo_full, 0); + atomic_set(&rfbi.cmd_pending, 0); + + rfbi.base = ioremap(RFBI_BASE, SZ_256); + if (!rfbi.base) { + DSSERR("can't ioremap RFBI\n"); + return -ENOMEM; + } + + rfbi_enable_clocks(1); + + msleep(10); + + rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000; + + /* Enable autoidle and smart-idle */ + l = rfbi_read_reg(RFBI_SYSCONFIG); + l |= (1 << 0) | (2 << 3); + rfbi_write_reg(RFBI_SYSCONFIG, l); + + rev = rfbi_read_reg(RFBI_REVISION); + printk(KERN_INFO "OMAP RFBI rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + rfbi_enable_clocks(0); + + return 0; +} + +void rfbi_exit(void) +{ + DSSDBG("rfbi_exit\n"); + + kfifo_free(rfbi.cmd_fifo); + + iounmap(rfbi.base); +} + +/* struct omap_display support */ +static int rfbi_display_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + int rfbi_module; + + if (w == 0 || h == 0) + return 0; + + rfbi_module = rfbi_find_display(dssdev); + + rfbi_push_update(rfbi_module, x, y, w, h); + + return 0; +} + +static int rfbi_display_sync(struct omap_dss_device *dssdev) +{ + struct completion sync_comp; + int rfbi_module; + + rfbi_module = rfbi_find_display(dssdev); + + init_completion(&sync_comp); + rfbi_push_sync(rfbi_module, &sync_comp); + DSSDBG("Waiting for SYNC to happen...\n"); + wait_for_completion(&sync_comp); + DSSDBG("Released from SYNC\n"); + return 0; +} + +static int rfbi_display_enable_te(struct omap_dss_device *dssdev, bool enable) +{ + dssdev->driver->enable_te(dssdev, enable); + return 0; +} + +static int rfbi_display_enable(struct omap_dss_device *dssdev) +{ + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + r = omap_dispc_register_isr(framedone_callback, NULL, + DISPC_IRQ_FRAMEDONE); + if (r) { + DSSERR("can't get FRAMEDONE irq\n"); + goto err1; + } + + /* TODO: change here for LCD2 support */ + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_LCD_DISPLAY_TFT); + + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_PARALLELMODE_RFBI); + + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD, dssdev->ctrl.pixel_size); + + rfbi_configure(dssdev->phy.rfbi.channel, + dssdev->ctrl.pixel_size, + dssdev->phy.rfbi.data_lines); + + rfbi_set_timings(dssdev->phy.rfbi.channel, + &dssdev->ctrl.rfbi_timings); + + + if (dssdev->driver->enable) { + r = dssdev->driver->enable(dssdev); + if (r) + goto err2; + } + + return 0; +err2: + omap_dispc_unregister_isr(framedone_callback, NULL, + DISPC_IRQ_FRAMEDONE); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static void rfbi_display_disable(struct omap_dss_device *dssdev) +{ + dssdev->driver->disable(dssdev); + omap_dispc_unregister_isr(framedone_callback, NULL, + DISPC_IRQ_FRAMEDONE); + omap_dss_stop_device(dssdev); +} + +int rfbi_init_display(struct omap_dss_device *dssdev) +{ + dssdev->enable = rfbi_display_enable; + dssdev->disable = rfbi_display_disable; + dssdev->update = rfbi_display_update; + dssdev->sync = rfbi_display_sync; + dssdev->enable_te = rfbi_display_enable_te; + + rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; + + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + + return 0; +} diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c new file mode 100644 index 000000000000..47c841b3db3b --- /dev/null +++ b/drivers/video/omap2/dss/sdi.c @@ -0,0 +1,381 @@ +/* + * linux/drivers/video/omap2/dss/sdi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "SDI" + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/board.h> +#include <mach/display.h> +#include "dss.h" + +#define CONTROL_PADCONF_BASE 0x48002000 + +#define OMAP_SDI_PAD_DIS(pe, pu) ((7 << 0) | /* MODE 7 = safe */ \ + (((pe) ? 1 : 0) << 3) | /* PULL_ENA */ \ + (((pu) ? 1 : 0) << 4) | /* PULL_UP */ \ + (1 << 8)) /* INPUT_EN */ + +#define OMAP_SDI_PAD_EN (1 << 0) /* MODE 1 = SDI_xx */ + +#define OMAP_SDI_PAD_MASK OMAP_SDI_PAD_DIS(1, 1) + +static struct { + bool skip_init; + bool update_enabled; +} sdi; + +/* CONTROL_PADCONF_DSS_DATAXX */ +static const u16 sdi_pads[] = +{ + 0x0f0, /* 10[ 7..0]:SDI_DAT1N */ + 0x0f2, /* 10[15..0]:SDI_DAT1P */ + 0x0f4, /* 12[ 7..0]:SDI_DAT2N */ + 0x0f6, /* 12[15..0]:SDI_DAT2P */ + 0x0f8, /* 14[ 7..0]:SDI_DAT3N */ + 0x0fa, /* 14[15..0]:SDI_DAT3P */ + 0x108, /* 22[ 7..0]:SDI_CLKN */ + 0x10a, /* 22[15..0]:SDI_CLKP */ +}; + +/* + * Check if bootloader / platform code has configured the SDI pads properly. + * This means it either configured all required pads for SDI mode, or that it + * left all the required pads unconfigured. + */ +static int sdi_pad_init(struct omap_dss_device *dssdev) +{ + unsigned req_map; + bool configured = false; + bool unconfigured = false; + int data_pairs; + int i; + + data_pairs = dssdev->phy.sdi.datapairs; + req_map = (1 << (data_pairs * 2)) - 1; /* data lanes */ + req_map |= 3 << 6; /* clk lane */ + for (i = 0; i < ARRAY_SIZE(sdi_pads); i++) { + u32 reg; + u32 val; + + if (!((1 << i) & req_map)) + /* Ignore unneded pads. */ + continue; + reg = CONTROL_PADCONF_BASE + sdi_pads[i]; + val = omap_readw(reg); + switch (val & 0x07) { /* pad mode */ + case 1: + if (unconfigured) + break; + /* Is the pull configuration ok for SDI mode? */ + if ((val & OMAP_SDI_PAD_MASK) != OMAP_SDI_PAD_EN) + break; + configured = true; + break; + case 0: + case 7: + if (configured) + break; + unconfigured = true; + break; + default: + break; + } + } + if (i != ARRAY_SIZE(sdi_pads)) { + DSSERR("SDI: invalid pad configuration\n"); + return -1; + } + + return 0; +} + +static void sdi_pad_config(struct omap_dss_device *dssdev, bool enable) +{ + int data_pairs; + bool pad_off_pe, pad_off_pu; + unsigned req_map; + int i; + + data_pairs = dssdev->phy.sdi.datapairs; + pad_off_pe = dssdev->phy.sdi.pad_off_pe; + pad_off_pu = dssdev->phy.sdi.pad_off_pu; + req_map = (1 << (data_pairs * 2)) - 1; /* data lanes */ + req_map |= 3 << 6; /* clk lane */ + for (i = 0; i < ARRAY_SIZE(sdi_pads); i++) { + u32 reg; + u16 val; + + if (!((1 << i) & req_map)) + continue; + if (enable) + val = OMAP_SDI_PAD_EN; + else + val = OMAP_SDI_PAD_DIS(pad_off_pe, pad_off_pu); + reg = CONTROL_PADCONF_BASE + sdi_pads[i]; + omap_writew(val, reg); + } +} + +static void sdi_basic_init(void) +{ + + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_PARALLELMODE_BYPASS); + + /* TODO: change here for LCD2 support */ + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD, 24); + dispc_lcd_enable_signal_polarity(1); +} + +static int sdi_display_enable(struct omap_dss_device *dssdev) +{ + struct omap_video_timings *t = &dssdev->panel.timings; + struct dispc_clock_info cinfo; + u16 lck_div, pck_div; + unsigned long fck; + unsigned long pck; + int r; + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + DSSERR("dssdev already enabled\n"); + r = -EINVAL; + goto err1; + } + + sdi_pad_config(dssdev, 1); + + /* In case of skip_init sdi_init has already enabled the clocks */ + if (!sdi.skip_init) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + sdi_basic_init(); + + /* 15.5.9.1.2 */ + dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; + + /* TODO: update for LCD2 here */ + dispc_set_pol_freq(OMAP_DSS_CHANNEL_LCD, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); + + if (!sdi.skip_init) + r = dispc_calc_clock_div(1, t->pixel_clock * 1000, + &cinfo); + else + r = dispc_get_clock_div(&cinfo); + + if (r) + goto err2; + + fck = cinfo.fck; + lck_div = cinfo.lck_div; + pck_div = cinfo.pck_div; + + pck = fck / lck_div / pck_div / 1000; + + if (pck != t->pixel_clock) { + DSSWARN("Could not find exact pixel clock. Requested %d kHz, " + "got %lu kHz\n", + t->pixel_clock, pck); + + t->pixel_clock = pck; + } + + /* TODO: if needed, add LCD2 support here*/ + dispc_set_lcd_timings(OMAP_DSS_CHANNEL_LCD, t); + + r = dispc_set_clock_div(&cinfo); + if (r) + goto err2; + + if (!sdi.skip_init) { + dss_sdi_init(dssdev->phy.sdi.datapairs); + dss_sdi_enable(); + mdelay(2); + } + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 1); + + if (dssdev->driver->enable) { + r = dssdev->driver->enable(dssdev); + if (r) + goto err3; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + sdi.skip_init = 0; + + return 0; +err3: + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); +err2: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +err1: + omap_dss_stop_device(dssdev); +err0: + return r; +} + +static int sdi_display_resume(struct omap_dss_device *dssdev); + +static void sdi_display_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + return; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + sdi_display_resume(dssdev); + + if (dssdev->driver->disable) + dssdev->driver->disable(dssdev); + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + + dss_sdi_disable(); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + sdi_pad_config(dssdev, 0); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); +} + +static int sdi_display_suspend(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return -EINVAL; + + if (dssdev->driver->suspend) + dssdev->driver->suspend(dssdev); + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + + dss_sdi_disable(); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + sdi_pad_config(dssdev, 0); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int sdi_display_resume(struct omap_dss_device *dssdev) +{ + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) + return -EINVAL; + + sdi_pad_config(dssdev, 1); + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dss_sdi_enable(); + mdelay(2); + + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 1); + + if (dssdev->driver->resume) + dssdev->driver->resume(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static int sdi_display_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + if (mode == OMAP_DSS_UPDATE_MANUAL) + return -EINVAL; + + if (mode == OMAP_DSS_UPDATE_DISABLED) { + /* TODO: change here if LCD2 support is needed */ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + sdi.update_enabled = 0; + } else { + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 1); + sdi.update_enabled = 1; + } + + return 0; +} + +static enum omap_dss_update_mode sdi_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO : + OMAP_DSS_UPDATE_DISABLED; +} + +static void sdi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +int sdi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("SDI init\n"); + + dssdev->enable = sdi_display_enable; + dssdev->disable = sdi_display_disable; + dssdev->suspend = sdi_display_suspend; + dssdev->resume = sdi_display_resume; + dssdev->set_update_mode = sdi_display_set_update_mode; + dssdev->get_update_mode = sdi_display_get_update_mode; + dssdev->get_timings = sdi_get_timings; + + return sdi_pad_init(dssdev); +} + +int sdi_init(bool skip_init) +{ + /* we store this for first display enable, then clear it */ + sdi.skip_init = skip_init; + + /* + * Enable clocks already here, otherwise there would be a toggle + * of them until sdi_display_enable is called. + */ + if (skip_init) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + return 0; +} + +void sdi_exit(void) +{ +} diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c new file mode 100644 index 000000000000..d8a83b2410e6 --- /dev/null +++ b/drivers/video/omap2/dss/venc.c @@ -0,0 +1,782 @@ +/* + * linux/drivers/video/omap2/dss/venc.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * VENC settings from TI's DSS driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "VENC" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/seq_file.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#include <mach/display.h> +#include <mach/cpu.h> + +#include "dss.h" + +#define VENC_BASE 0x48050C00 + +/* Venc registers */ +#define VENC_REV_ID 0x00 +#define VENC_STATUS 0x04 +#define VENC_F_CONTROL 0x08 +#define VENC_VIDOUT_CTRL 0x10 +#define VENC_SYNC_CTRL 0x14 +#define VENC_LLEN 0x1C +#define VENC_FLENS 0x20 +#define VENC_HFLTR_CTRL 0x24 +#define VENC_CC_CARR_WSS_CARR 0x28 +#define VENC_C_PHASE 0x2C +#define VENC_GAIN_U 0x30 +#define VENC_GAIN_V 0x34 +#define VENC_GAIN_Y 0x38 +#define VENC_BLACK_LEVEL 0x3C +#define VENC_BLANK_LEVEL 0x40 +#define VENC_X_COLOR 0x44 +#define VENC_M_CONTROL 0x48 +#define VENC_BSTAMP_WSS_DATA 0x4C +#define VENC_S_CARR 0x50 +#define VENC_LINE21 0x54 +#define VENC_LN_SEL 0x58 +#define VENC_L21__WC_CTL 0x5C +#define VENC_HTRIGGER_VTRIGGER 0x60 +#define VENC_SAVID__EAVID 0x64 +#define VENC_FLEN__FAL 0x68 +#define VENC_LAL__PHASE_RESET 0x6C +#define VENC_HS_INT_START_STOP_X 0x70 +#define VENC_HS_EXT_START_STOP_X 0x74 +#define VENC_VS_INT_START_X 0x78 +#define VENC_VS_INT_STOP_X__VS_INT_START_Y 0x7C +#define VENC_VS_INT_STOP_Y__VS_EXT_START_X 0x80 +#define VENC_VS_EXT_STOP_X__VS_EXT_START_Y 0x84 +#define VENC_VS_EXT_STOP_Y 0x88 +#define VENC_AVID_START_STOP_X 0x90 +#define VENC_AVID_START_STOP_Y 0x94 +#define VENC_FID_INT_START_X__FID_INT_START_Y 0xA0 +#define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4 +#define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8 +#define VENC_TVDETGP_INT_START_STOP_X 0xB0 +#define VENC_TVDETGP_INT_START_STOP_Y 0xB4 +#define VENC_GEN_CTRL 0xB8 +#define VENC_OUTPUT_CONTROL 0xC4 +#define VENC_OUTPUT_TEST 0xC8 +#define VENC_DAC_B__DAC_C 0xC8 + +struct venc_config { + u32 f_control; + u32 vidout_ctrl; + u32 sync_ctrl; + u32 llen; + u32 flens; + u32 hfltr_ctrl; + u32 cc_carr_wss_carr; + u32 c_phase; + u32 gain_u; + u32 gain_v; + u32 gain_y; + u32 black_level; + u32 blank_level; + u32 x_color; + u32 m_control; + u32 bstamp_wss_data; + u32 s_carr; + u32 line21; + u32 ln_sel; + u32 l21__wc_ctl; + u32 htrigger_vtrigger; + u32 savid__eavid; + u32 flen__fal; + u32 lal__phase_reset; + u32 hs_int_start_stop_x; + u32 hs_ext_start_stop_x; + u32 vs_int_start_x; + u32 vs_int_stop_x__vs_int_start_y; + u32 vs_int_stop_y__vs_ext_start_x; + u32 vs_ext_stop_x__vs_ext_start_y; + u32 vs_ext_stop_y; + u32 avid_start_stop_x; + u32 avid_start_stop_y; + u32 fid_int_start_x__fid_int_start_y; + u32 fid_int_offset_y__fid_ext_start_x; + u32 fid_ext_start_y__fid_ext_offset_y; + u32 tvdetgp_int_start_stop_x; + u32 tvdetgp_int_start_stop_y; + u32 gen_ctrl; +}; + +/* from TRM */ +static const struct venc_config venc_config_pal_trm = { + .f_control = 0, + .vidout_ctrl = 1, + .sync_ctrl = 0x40, + .llen = 0x35F, /* 863 */ + .flens = 0x270, /* 624 */ + .hfltr_ctrl = 0, + .cc_carr_wss_carr = 0x2F7225ED, + .c_phase = 0, + .gain_u = 0x111, + .gain_v = 0x181, + .gain_y = 0x140, + .black_level = 0x3B, + .blank_level = 0x3B, + .x_color = 0x7, + .m_control = 0x2, + .bstamp_wss_data = 0x3F, + .s_carr = 0x2A098ACB, + .line21 = 0, + .ln_sel = 0x01290015, + .l21__wc_ctl = 0x0000F603, + .htrigger_vtrigger = 0, + + .savid__eavid = 0x06A70108, + .flen__fal = 0x00180270, + .lal__phase_reset = 0x00040135, + .hs_int_start_stop_x = 0x00880358, + .hs_ext_start_stop_x = 0x000F035F, + .vs_int_start_x = 0x01A70000, + .vs_int_stop_x__vs_int_start_y = 0x000001A7, + .vs_int_stop_y__vs_ext_start_x = 0x01AF0000, + .vs_ext_stop_x__vs_ext_start_y = 0x000101AF, + .vs_ext_stop_y = 0x00000025, + .avid_start_stop_x = 0x03530083, + .avid_start_stop_y = 0x026C002E, + .fid_int_start_x__fid_int_start_y = 0x0001008A, + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, + .fid_ext_start_y__fid_ext_offset_y = 0x01380001, + + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00FF0000, +}; + +/* from TRM */ +static const struct venc_config venc_config_ntsc_trm = { + .f_control = 0, + .vidout_ctrl = 1, + .sync_ctrl = 0x8040, + .llen = 0x359, + .flens = 0x20C, + .hfltr_ctrl = 0, + .cc_carr_wss_carr = 0x043F2631, + .c_phase = 0, + .gain_u = 0x102, + .gain_v = 0x16C, + .gain_y = 0x12F, + .black_level = 0x43, + .blank_level = 0x38, + .x_color = 0x7, + .m_control = 0x1, + .bstamp_wss_data = 0x38, + .s_carr = 0x21F07C1F, + .line21 = 0, + .ln_sel = 0x01310011, + .l21__wc_ctl = 0x0000F003, + .htrigger_vtrigger = 0, + + .savid__eavid = 0x069300F4, + .flen__fal = 0x0016020C, + .lal__phase_reset = 0x00060107, + .hs_int_start_stop_x = 0x008E0350, + .hs_ext_start_stop_x = 0x000F0359, + .vs_int_start_x = 0x01A00000, + .vs_int_stop_x__vs_int_start_y = 0x020701A0, + .vs_int_stop_y__vs_ext_start_x = 0x01AC0024, + .vs_ext_stop_x__vs_ext_start_y = 0x020D01AC, + .vs_ext_stop_y = 0x00000006, + .avid_start_stop_x = 0x03480078, + .avid_start_stop_y = 0x02060024, + .fid_int_start_x__fid_int_start_y = 0x0001008A, + .fid_int_offset_y__fid_ext_start_x = 0x01AC0106, + .fid_ext_start_y__fid_ext_offset_y = 0x01060006, + + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00F90000, +}; + +static const struct venc_config venc_config_pal_bdghi = { + .f_control = 0, + .vidout_ctrl = 0, + .sync_ctrl = 0, + .hfltr_ctrl = 0, + .x_color = 0, + .line21 = 0, + .ln_sel = 21, + .htrigger_vtrigger = 0, + .tvdetgp_int_start_stop_x = 0x00140001, + .tvdetgp_int_start_stop_y = 0x00010001, + .gen_ctrl = 0x00FB0000, + + .llen = 864-1, + .flens = 625-1, + .cc_carr_wss_carr = 0x2F7625ED, + .c_phase = 0xDF, + .gain_u = 0x111, + .gain_v = 0x181, + .gain_y = 0x140, + .black_level = 0x3e, + .blank_level = 0x3e, + .m_control = 0<<2 | 1<<1, + .bstamp_wss_data = 0x42, + .s_carr = 0x2a098acb, + .l21__wc_ctl = 0<<13 | 0x16<<8 | 0<<0, + .savid__eavid = 0x06A70108, + .flen__fal = 23<<16 | 624<<0, + .lal__phase_reset = 2<<17 | 310<<0, + .hs_int_start_stop_x = 0x00920358, + .hs_ext_start_stop_x = 0x000F035F, + .vs_int_start_x = 0x1a7<<16, + .vs_int_stop_x__vs_int_start_y = 0x000601A7, + .vs_int_stop_y__vs_ext_start_x = 0x01AF0036, + .vs_ext_stop_x__vs_ext_start_y = 0x27101af, + .vs_ext_stop_y = 0x05, + .avid_start_stop_x = 0x03530082, + .avid_start_stop_y = 0x0270002E, + .fid_int_start_x__fid_int_start_y = 0x0005008A, + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, + .fid_ext_start_y__fid_ext_offset_y = 0x01380005, +}; + +const struct omap_video_timings omap_dss_pal_timings = { + .x_res = 720, + .y_res = 574, + .pixel_clock = 13500, + .hsw = 64, + .hfp = 12, + .hbp = 68, + .vsw = 5, + .vfp = 5, + .vbp = 41, +}; +EXPORT_SYMBOL(omap_dss_pal_timings); + +const struct omap_video_timings omap_dss_ntsc_timings = { + .x_res = 720, + .y_res = 482, + .pixel_clock = 13500, + .hsw = 64, + .hfp = 16, + .hbp = 58, + .vsw = 6, + .vfp = 6, + .vbp = 31, +}; +EXPORT_SYMBOL(omap_dss_ntsc_timings); + +static struct { + void __iomem *base; + struct mutex venc_lock; + u32 wss_data; + struct regulator *vdda_dac_reg; +} venc; + +static inline void venc_write_reg(int idx, u32 val) +{ + __raw_writel(val, venc.base + idx); +} + +static inline u32 venc_read_reg(int idx) +{ + u32 l = __raw_readl(venc.base + idx); + return l; +} + +static void venc_write_config(const struct venc_config *config) +{ + DSSDBG("write venc conf\n"); + + venc_write_reg(VENC_LLEN, config->llen); + venc_write_reg(VENC_FLENS, config->flens); + venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr); + venc_write_reg(VENC_C_PHASE, config->c_phase); + venc_write_reg(VENC_GAIN_U, config->gain_u); + venc_write_reg(VENC_GAIN_V, config->gain_v); + venc_write_reg(VENC_GAIN_Y, config->gain_y); + venc_write_reg(VENC_BLACK_LEVEL, config->black_level); + venc_write_reg(VENC_BLANK_LEVEL, config->blank_level); + venc_write_reg(VENC_M_CONTROL, config->m_control); + venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | + venc.wss_data); + venc_write_reg(VENC_S_CARR, config->s_carr); + venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl); + venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid); + venc_write_reg(VENC_FLEN__FAL, config->flen__fal); + venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset); + venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x); + venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x); + venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x); + venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y, + config->vs_int_stop_x__vs_int_start_y); + venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X, + config->vs_int_stop_y__vs_ext_start_x); + venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y, + config->vs_ext_stop_x__vs_ext_start_y); + venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y); + venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x); + venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y); + venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y, + config->fid_int_start_x__fid_int_start_y); + venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, + config->fid_int_offset_y__fid_ext_start_x); + venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, + config->fid_ext_start_y__fid_ext_offset_y); + + venc_write_reg(VENC_DAC_B__DAC_C, venc_read_reg(VENC_DAC_B__DAC_C)); + venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl); + venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl); + venc_write_reg(VENC_X_COLOR, config->x_color); + venc_write_reg(VENC_LINE21, config->line21); + venc_write_reg(VENC_LN_SEL, config->ln_sel); + venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger); + venc_write_reg(VENC_TVDETGP_INT_START_STOP_X, + config->tvdetgp_int_start_stop_x); + venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y, + config->tvdetgp_int_start_stop_y); + venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl); + venc_write_reg(VENC_F_CONTROL, config->f_control); + venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl); +} + +static void venc_reset(void) +{ + int t = 1000; + + venc_write_reg(VENC_F_CONTROL, 1<<8); + while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) { + if (--t == 0) { + DSSERR("Failed to reset venc\n"); + return; + } + } + + /* the magical sleep that makes things work */ + msleep(20); +} + +static void venc_enable_clocks(int enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); +} + +static const struct venc_config *venc_timings_to_config( + struct omap_video_timings *timings) +{ + if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) + return &venc_config_pal_trm; + + if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) + return &venc_config_ntsc_trm; + + BUG(); +} + + + + + +/* driver */ +static int venc_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.timings = omap_dss_pal_timings; + + return 0; +} + +static void venc_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int venc_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + /* wait couple of vsyncs until enabling the LCD */ + msleep(50); + + if (dssdev->platform_enable) + r = dssdev->platform_enable(dssdev); + + return r; +} + +static void venc_panel_disable(struct omap_dss_device *dssdev) +{ + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + /* wait at least 5 vsyncs after disabling the LCD */ + + msleep(100); +} + +static int venc_panel_suspend(struct omap_dss_device *dssdev) +{ + venc_panel_disable(dssdev); + return 0; +} + +static int venc_panel_resume(struct omap_dss_device *dssdev) +{ + return venc_panel_enable(dssdev); +} + +static struct omap_dss_driver venc_driver = { + .probe = venc_panel_probe, + .remove = venc_panel_remove, + + .enable = venc_panel_enable, + .disable = venc_panel_disable, + .suspend = venc_panel_suspend, + .resume = venc_panel_resume, + + .driver = { + .name = "venc", + .owner = THIS_MODULE, + }, +}; +/* driver end */ + + + +int venc_init(struct platform_device *pdev) +{ + u8 rev_id; + + mutex_init(&venc.venc_lock); + + venc.wss_data = 0; + + venc.base = ioremap(VENC_BASE, SZ_1K); + if (!venc.base) { + DSSERR("can't ioremap VENC\n"); + return -ENOMEM; + } + + venc.vdda_dac_reg = regulator_get(&pdev->dev, "vdda_dac"); + if (IS_ERR(venc.vdda_dac_reg)) { + iounmap(venc.base); + DSSERR("can't get VDDA_DAC regulator\n"); + return PTR_ERR(venc.vdda_dac_reg); + } + + venc_enable_clocks(1); + + rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); + printk(KERN_INFO "OMAP VENC rev %d\n", rev_id); + + venc_enable_clocks(0); + + return omap_dss_register_driver(&venc_driver); +} + +void venc_exit(void) +{ + omap_dss_unregister_driver(&venc_driver); + + regulator_put(venc.vdda_dac_reg); + + iounmap(venc.base); +} + +static void venc_power_on(struct omap_dss_device *dssdev) +{ + venc_enable_clocks(1); + + venc_reset(); + venc_write_config(venc_timings_to_config(&dssdev->panel.timings)); + + dss_set_venc_output(dssdev->phy.venc.type); + dss_set_dac_pwrdn_bgz(1); + + if (dssdev->phy.venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE) { + if (cpu_is_omap24xx()) + venc_write_reg(VENC_OUTPUT_CONTROL, 0x2); + else + venc_write_reg(VENC_OUTPUT_CONTROL, 0xa); + } else { /* S-Video */ + venc_write_reg(VENC_OUTPUT_CONTROL, 0xd); + } + + dispc_set_digit_size(dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res/2); + + regulator_enable(venc.vdda_dac_reg); + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + + dispc_enable_digit_out(1); +} + +static void venc_power_off(struct omap_dss_device *dssdev) +{ + venc_write_reg(VENC_OUTPUT_CONTROL, 0); + dss_set_dac_pwrdn_bgz(0); + + dispc_enable_digit_out(0); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + regulator_disable(venc.vdda_dac_reg); + + venc_enable_clocks(0); +} + +static int venc_enable_display(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("venc_enable_display\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } + + venc_power_on(dssdev); + + venc.wss_data = 0; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +err: + mutex_unlock(&venc.venc_lock); + + return r; +} + +static void venc_disable_display(struct omap_dss_device *dssdev) +{ + DSSDBG("venc_disable_display\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + goto end; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) { + /* suspended is the same as disabled with venc */ + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + goto end; + } + + venc_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +end: + mutex_unlock(&venc.venc_lock); +} + +static int venc_display_suspend(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("venc_display_suspend\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto err; + } + + venc_power_off(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; +err: + mutex_unlock(&venc.venc_lock); + + return r; +} + +static int venc_display_resume(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("venc_display_resume\n"); + + mutex_lock(&venc.venc_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + goto err; + } + + venc_power_on(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +err: + mutex_unlock(&venc.venc_lock); + + return r; +} + +static void venc_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static void venc_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("venc_set_timings\n"); + + /* Reset WSS data when the TV standard changes. */ + if (memcmp(&dssdev->panel.timings, timings, sizeof(*timings))) + venc.wss_data = 0; + + dssdev->panel.timings = *timings; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* turn the venc off and on to get new timings to use */ + venc_disable_display(dssdev); + venc_enable_display(dssdev); + } +} + +static int venc_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("venc_check_timings\n"); + + if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) + return 0; + + if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) + return 0; + + return -EINVAL; +} + +static u32 venc_get_wss(struct omap_dss_device *dssdev) +{ + /* Invert due to VENC_L21_WC_CTL:INV=1 */ + return (venc.wss_data >> 8) ^ 0xfffff; +} + +static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) +{ + const struct venc_config *config; + + DSSDBG("venc_set_wss\n"); + + mutex_lock(&venc.venc_lock); + + config = venc_timings_to_config(&dssdev->panel.timings); + + /* Invert due to VENC_L21_WC_CTL:INV=1 */ + venc.wss_data = (wss ^ 0xfffff) << 8; + + venc_enable_clocks(1); + + venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | + venc.wss_data); + + venc_enable_clocks(0); + + mutex_unlock(&venc.venc_lock); + + return 0; +} + +int venc_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + dssdev->enable = venc_enable_display; + dssdev->disable = venc_disable_display; + dssdev->suspend = venc_display_suspend; + dssdev->resume = venc_display_resume; + dssdev->get_timings = venc_get_timings; + dssdev->set_timings = venc_set_timings; + dssdev->check_timings = venc_check_timings; + dssdev->get_wss = venc_get_wss; + dssdev->set_wss = venc_set_wss; + + return 0; +} + +void venc_dump_regs(struct seq_file *s) +{ +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) + + venc_enable_clocks(1); + + DUMPREG(VENC_F_CONTROL); + DUMPREG(VENC_VIDOUT_CTRL); + DUMPREG(VENC_SYNC_CTRL); + DUMPREG(VENC_LLEN); + DUMPREG(VENC_FLENS); + DUMPREG(VENC_HFLTR_CTRL); + DUMPREG(VENC_CC_CARR_WSS_CARR); + DUMPREG(VENC_C_PHASE); + DUMPREG(VENC_GAIN_U); + DUMPREG(VENC_GAIN_V); + DUMPREG(VENC_GAIN_Y); + DUMPREG(VENC_BLACK_LEVEL); + DUMPREG(VENC_BLANK_LEVEL); + DUMPREG(VENC_X_COLOR); + DUMPREG(VENC_M_CONTROL); + DUMPREG(VENC_BSTAMP_WSS_DATA); + DUMPREG(VENC_S_CARR); + DUMPREG(VENC_LINE21); + DUMPREG(VENC_LN_SEL); + DUMPREG(VENC_L21__WC_CTL); + DUMPREG(VENC_HTRIGGER_VTRIGGER); + DUMPREG(VENC_SAVID__EAVID); + DUMPREG(VENC_FLEN__FAL); + DUMPREG(VENC_LAL__PHASE_RESET); + DUMPREG(VENC_HS_INT_START_STOP_X); + DUMPREG(VENC_HS_EXT_START_STOP_X); + DUMPREG(VENC_VS_INT_START_X); + DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y); + DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X); + DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y); + DUMPREG(VENC_VS_EXT_STOP_Y); + DUMPREG(VENC_AVID_START_STOP_X); + DUMPREG(VENC_AVID_START_STOP_Y); + DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y); + DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X); + DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y); + DUMPREG(VENC_TVDETGP_INT_START_STOP_X); + DUMPREG(VENC_TVDETGP_INT_START_STOP_Y); + DUMPREG(VENC_GEN_CTRL); + DUMPREG(VENC_OUTPUT_CONTROL); + DUMPREG(VENC_OUTPUT_TEST); + + venc_enable_clocks(0); + +#undef DUMPREG +} diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig new file mode 100644 index 000000000000..77ca9c9d6390 --- /dev/null +++ b/drivers/video/omap2/omapfb/Kconfig @@ -0,0 +1,35 @@ +menuconfig FB_OMAP2 + tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)" + depends on FB && OMAP2_DSS + default y + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Frame buffer driver for OMAP2/3 based boards. + +config FB_OMAP2_DEBUG_SUPPORT + bool "Debug support for OMAP2/3 FB" + default y + depends on FB_OMAP2 + help + Support for debug output. You have to enable the actual printing + with debug module parameter. + +config FB_OMAP2_FORCE_AUTO_UPDATE + bool "Force main display to automatic update mode" + depends on FB_OMAP2 + help + Forces main display to automatic update mode (if possible), + and also enables tearsync (if possible). By default + displays that support manual update are started in manual + update mode. + +config FB_OMAP2_NUM_FBS + int "Number of framebuffers" + range 1 10 + default 3 + depends on FB_OMAP2 + help + Select the number of framebuffers created. OMAP2/3 has 3 overlays + so normally this would be 3. diff --git a/drivers/video/omap2/omapfb/Makefile b/drivers/video/omap2/omapfb/Makefile new file mode 100644 index 000000000000..51c2e00d9bf8 --- /dev/null +++ b/drivers/video/omap2/omapfb/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FB_OMAP2) += omapfb.o +omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c new file mode 100644 index 000000000000..257f7cbc0e00 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -0,0 +1,709 @@ +/* + * linux/drivers/video/omap2/omapfb-ioctl.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/fb.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/mm.h> +#include <linux/omapfb.h> +#include <linux/vmalloc.h> + +#include <mach/display.h> +#include <mach/vrfb.h> + +#include "omapfb.h" + +static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + struct omap_overlay *ovl; + struct omap_overlay_info info; + int r = 0; + + DBG("omapfb_setup_plane\n"); + + if (ofbi->num_overlays != 1) { + r = -EINVAL; + goto out; + } + + /* XXX uses only the first overlay */ + ovl = ofbi->overlays[0]; + + if (pi->enabled && !ofbi->region.size) { + /* + * This plane's memory was freed, can't enable it + * until it's reallocated. + */ + r = -EINVAL; + goto out; + } + + ovl->get_overlay_info(ovl, &info); + + info.pos_x = pi->pos_x; + info.pos_y = pi->pos_y; + info.out_width = pi->out_width; + info.out_height = pi->out_height; + info.enabled = pi->enabled; + + r = ovl->set_overlay_info(ovl, &info); + if (r) + goto out; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + goto out; + } + + if (display) { + u16 w, h; + + if (display->sync) + display->sync(display); + + display->get_resolution(display, &w, &h); + + if (display->update) + display->update(display, 0, 0, w, h); + } + +out: + if (r) + dev_err(fbdev->dev, "setup_plane failed\n"); + return r; +} + +static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + + if (ofbi->num_overlays != 1) { + memset(pi, 0, sizeof(*pi)); + } else { + struct omap_overlay_info *ovli; + struct omap_overlay *ovl; + + ovl = ofbi->overlays[0]; + ovli = &ovl->info; + + pi->pos_x = ovli->pos_x; + pi->pos_y = ovli->pos_y; + pi->enabled = ovli->enabled; + pi->channel_out = 0; /* xxx */ + pi->mirror = 0; + pi->out_width = ovli->out_width; + pi->out_height = ovli->out_height; + } + + return 0; +} + +static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb2_mem_region *rg; + int r, i; + size_t size; + + if (mi->type > OMAPFB_MEMTYPE_MAX) + return -EINVAL; + + size = PAGE_ALIGN(mi->size); + + rg = &ofbi->region; + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->info.enabled) + return -EBUSY; + } + + if (rg->size != size || rg->type != mi->type) { + r = omapfb_realloc_fbmem(fbi, size, mi->type); + if (r) { + dev_err(fbdev->dev, "realloc fbmem failed\n"); + return r; + } + } + + return 0; +} + +static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_mem_region *rg; + + rg = &ofbi->region; + memset(mi, 0, sizeof(*mi)); + + mi->size = rg->size; + mi->type = rg->type; + + return 0; +} + +static int omapfb_update_window(struct fb_info *fbi, + u32 x, u32 y, u32 w, u32 h) +{ + struct omap_dss_device *display = fb2display(fbi); + u16 dw, dh; + + if (!display) + return 0; + + if (w == 0 || h == 0) + return 0; + + display->get_resolution(display, &dw, &dh); + + if (x + w > dw || y + h > dh) + return -EINVAL; + + display->update(display, x, y, w, h); + + return 0; +} + +static int omapfb_set_update_mode(struct fb_info *fbi, + enum omapfb_update_mode mode) +{ + struct omap_dss_device *display = fb2display(fbi); + enum omap_dss_update_mode um; + int r; + + if (!display || !display->set_update_mode) + return -EINVAL; + + switch (mode) { + case OMAPFB_UPDATE_DISABLED: + um = OMAP_DSS_UPDATE_DISABLED; + break; + + case OMAPFB_AUTO_UPDATE: + um = OMAP_DSS_UPDATE_AUTO; + break; + + case OMAPFB_MANUAL_UPDATE: + um = OMAP_DSS_UPDATE_MANUAL; + break; + + default: + return -EINVAL; + } + + r = display->set_update_mode(display, um); + + return r; +} + +static int omapfb_get_update_mode(struct fb_info *fbi, + enum omapfb_update_mode *mode) +{ + struct omap_dss_device *display = fb2display(fbi); + enum omap_dss_update_mode m; + + if (!display || !display->get_update_mode) + return -EINVAL; + + m = display->get_update_mode(display); + + switch (m) { + case OMAP_DSS_UPDATE_DISABLED: + *mode = OMAPFB_UPDATE_DISABLED; + break; + case OMAP_DSS_UPDATE_AUTO: + *mode = OMAPFB_AUTO_UPDATE; + break; + case OMAP_DSS_UPDATE_MANUAL: + *mode = OMAPFB_MANUAL_UPDATE; + break; + default: + BUG(); + } + + return 0; +} + +/* XXX this color key handling is a hack... */ +static struct omapfb_color_key omapfb_color_keys[2]; + +static int _omapfb_set_color_key(struct omap_overlay_manager *mgr, + struct omapfb_color_key *ck) +{ + struct omap_overlay_manager_info info; + enum omap_dss_trans_key_type kt; + int r; + + mgr->get_manager_info(mgr, &info); + + if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) { + info.trans_enabled = false; + omapfb_color_keys[mgr->id] = *ck; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + + return r; + } + + switch (ck->key_type) { + case OMAPFB_COLOR_KEY_GFX_DST: + kt = OMAP_DSS_COLOR_KEY_GFX_DST; + break; + case OMAPFB_COLOR_KEY_VID_SRC: + kt = OMAP_DSS_COLOR_KEY_VID_SRC; + break; + default: + return -EINVAL; + } + + info.default_color = ck->background; + info.trans_key = ck->trans_key; + info.trans_key_type = kt; + info.trans_enabled = true; + + omapfb_color_keys[mgr->id] = *ck; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + + return r; +} + +static int omapfb_set_color_key(struct fb_info *fbi, + struct omapfb_color_key *ck) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + int r; + int i; + struct omap_overlay_manager *mgr = NULL; + + omapfb_lock(fbdev); + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->manager) { + mgr = ofbi->overlays[i]->manager; + break; + } + } + + if (!mgr) { + r = -EINVAL; + goto err; + } + + r = _omapfb_set_color_key(mgr, ck); +err: + omapfb_unlock(fbdev); + + return r; +} + +static int omapfb_get_color_key(struct fb_info *fbi, + struct omapfb_color_key *ck) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_overlay_manager *mgr = NULL; + int r = 0; + int i; + + omapfb_lock(fbdev); + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->manager) { + mgr = ofbi->overlays[i]->manager; + break; + } + } + + if (!mgr) { + r = -EINVAL; + goto err; + } + + *ck = omapfb_color_keys[mgr->id]; +err: + omapfb_unlock(fbdev); + + return r; +} + +static int omapfb_memory_read(struct fb_info *fbi, + struct omapfb_memory_read *mr) +{ + struct omap_dss_device *display = fb2display(fbi); + void *buf; + int r; + + if (!display || !display->memory_read) + return -ENOENT; + + if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size)) + return -EFAULT; + + if (mr->w * mr->h * 3 > mr->buffer_size) + return -EINVAL; + + buf = vmalloc(mr->buffer_size); + if (!buf) { + DBG("vmalloc failed\n"); + return -ENOMEM; + } + + r = display->memory_read(display, buf, mr->buffer_size, + mr->x, mr->y, mr->w, mr->h); + + if (r > 0) { + if (copy_to_user(mr->buffer, buf, mr->buffer_size)) + r = -EFAULT; + } + + vfree(buf); + + return r; +} + +static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev, + struct omapfb_ovl_colormode *mode) +{ + int ovl_idx = mode->overlay_idx; + int mode_idx = mode->mode_idx; + struct omap_overlay *ovl; + enum omap_color_mode supported_modes; + struct fb_var_screeninfo var; + int i; + + if (ovl_idx >= fbdev->num_overlays) + return -ENODEV; + ovl = fbdev->overlays[ovl_idx]; + supported_modes = ovl->supported_modes; + + mode_idx = mode->mode_idx; + + for (i = 0; i < sizeof(supported_modes) * 8; i++) { + if (!(supported_modes & (1 << i))) + continue; + /* + * It's possible that the FB doesn't support a mode + * that is supported by the overlay, so call the + * following here. + */ + if (dss_mode_to_fb_mode(1 << i, &var) < 0) + continue; + + mode_idx--; + if (mode_idx < 0) + break; + } + + if (i == sizeof(supported_modes) * 8) + return -ENOENT; + + mode->bits_per_pixel = var.bits_per_pixel; + mode->nonstd = var.nonstd; + mode->red = var.red; + mode->green = var.green; + mode->blue = var.blue; + mode->transp = var.transp; + + return 0; +} + +static int omapfb_wait_for_go(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + int r = 0; + int i; + + for (i = 0; i < ofbi->num_overlays; ++i) { + struct omap_overlay *ovl = ofbi->overlays[i]; + r = ovl->wait_for_go(ovl); + if (r) + break; + } + + return r; +} + +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + + union { + struct omapfb_update_window_old uwnd_o; + struct omapfb_update_window uwnd; + struct omapfb_plane_info plane_info; + struct omapfb_caps caps; + struct omapfb_mem_info mem_info; + struct omapfb_color_key color_key; + struct omapfb_ovl_colormode ovl_colormode; + enum omapfb_update_mode update_mode; + int test_num; + struct omapfb_memory_read memory_read; + } p; + + int r = 0; + + switch (cmd) { + case OMAPFB_SYNC_GFX: + DBG("ioctl SYNC_GFX\n"); + if (!display || !display->sync) { + /* DSS1 never returns an error here, so we neither */ + /*r = -EINVAL;*/ + break; + } + + r = display->sync(display); + break; + + case OMAPFB_UPDATE_WINDOW_OLD: + DBG("ioctl UPDATE_WINDOW_OLD\n"); + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd_o, + (void __user *)arg, + sizeof(p.uwnd_o))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window(fbi, p.uwnd_o.x, p.uwnd_o.y, + p.uwnd_o.width, p.uwnd_o.height); + break; + + case OMAPFB_UPDATE_WINDOW: + DBG("ioctl UPDATE_WINDOW\n"); + if (!display || !display->update) { + r = -EINVAL; + break; + } + + if (copy_from_user(&p.uwnd, (void __user *)arg, + sizeof(p.uwnd))) { + r = -EFAULT; + break; + } + + r = omapfb_update_window(fbi, p.uwnd.x, p.uwnd.y, + p.uwnd.width, p.uwnd.height); + break; + + case OMAPFB_SETUP_PLANE: + DBG("ioctl SETUP_PLANE\n"); + if (copy_from_user(&p.plane_info, (void __user *)arg, + sizeof(p.plane_info))) + r = -EFAULT; + else + r = omapfb_setup_plane(fbi, &p.plane_info); + break; + + case OMAPFB_QUERY_PLANE: + DBG("ioctl QUERY_PLANE\n"); + r = omapfb_query_plane(fbi, &p.plane_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.plane_info, + sizeof(p.plane_info))) + r = -EFAULT; + break; + + case OMAPFB_SETUP_MEM: + DBG("ioctl SETUP_MEM\n"); + if (copy_from_user(&p.mem_info, (void __user *)arg, + sizeof(p.mem_info))) + r = -EFAULT; + else + r = omapfb_setup_mem(fbi, &p.mem_info); + break; + + case OMAPFB_QUERY_MEM: + DBG("ioctl QUERY_MEM\n"); + r = omapfb_query_mem(fbi, &p.mem_info); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.mem_info, + sizeof(p.mem_info))) + r = -EFAULT; + break; + + case OMAPFB_GET_CAPS: + DBG("ioctl GET_CAPS\n"); + if (!display) { + r = -EINVAL; + break; + } + + memset(&p.caps, 0, sizeof(p.caps)); + p.caps.ctrl = display->caps; + + if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps))) + r = -EFAULT; + break; + + case OMAPFB_GET_OVERLAY_COLORMODE: + DBG("ioctl GET_OVERLAY_COLORMODE\n"); + if (copy_from_user(&p.ovl_colormode, (void __user *)arg, + sizeof(p.ovl_colormode))) { + r = -EFAULT; + break; + } + r = omapfb_get_ovl_colormode(fbdev, &p.ovl_colormode); + if (r < 0) + break; + if (copy_to_user((void __user *)arg, &p.ovl_colormode, + sizeof(p.ovl_colormode))) + r = -EFAULT; + break; + + case OMAPFB_SET_UPDATE_MODE: + DBG("ioctl SET_UPDATE_MODE\n"); + if (get_user(p.update_mode, (int __user *)arg)) + r = -EFAULT; + else + r = omapfb_set_update_mode(fbi, p.update_mode); + break; + + case OMAPFB_GET_UPDATE_MODE: + DBG("ioctl GET_UPDATE_MODE\n"); + r = omapfb_get_update_mode(fbi, &p.update_mode); + if (r) + break; + if (put_user(p.update_mode, + (enum omapfb_update_mode __user *)arg)) + r = -EFAULT; + break; + + case OMAPFB_SET_COLOR_KEY: + DBG("ioctl SET_COLOR_KEY\n"); + if (copy_from_user(&p.color_key, (void __user *)arg, + sizeof(p.color_key))) + r = -EFAULT; + else + r = omapfb_set_color_key(fbi, &p.color_key); + break; + + case OMAPFB_GET_COLOR_KEY: + DBG("ioctl GET_COLOR_KEY\n"); + r = omapfb_get_color_key(fbi, &p.color_key); + if (r) + break; + if (copy_to_user((void __user *)arg, &p.color_key, + sizeof(p.color_key))) + r = -EFAULT; + break; + + case OMAPFB_WAITFORVSYNC: + DBG("ioctl WAITFORVSYNC\n"); + if (!display) { + r = -EINVAL; + break; + } + + r = display->wait_vsync(display); + break; + + case OMAPFB_WAITFORGO: + DBG("ioctl WAITFORGO\n"); + if (!display) { + r = -EINVAL; + break; + } + + r = omapfb_wait_for_go(fbi); + break; + + /* LCD and CTRL tests do the same thing for backward + * compatibility */ + case OMAPFB_LCD_TEST: + DBG("ioctl LCD_TEST\n"); + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + case OMAPFB_CTRL_TEST: + DBG("ioctl CTRL_TEST\n"); + if (get_user(p.test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!display || !display->run_test) { + r = -EINVAL; + break; + } + + r = display->run_test(display, p.test_num); + + break; + + case OMAPFB_MEMORY_READ: + DBG("ioctl MEMORY_READ\n"); + + if (copy_from_user(&p.memory_read, (void __user *)arg, + sizeof(p.memory_read))) { + r = -EFAULT; + break; + } + + r = omapfb_memory_read(fbi, &p.memory_read); + + break; + + default: + dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd); + r = -EINVAL; + } + + if (r < 0) + DBG("ioctl failed: %d\n", r); + + return r; +} + + diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c new file mode 100644 index 000000000000..bf163c4642f0 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -0,0 +1,2156 @@ +/* + * linux/drivers/video/omap2/omapfb-main.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/omapfb.h> + +#include <mach/display.h> +#include <mach/vram.h> +#include <mach/vrfb.h> + +#include "omapfb.h" + +#define MODULE_NAME "omapfb" + +static char *def_mode; +static char *def_vram; +static int def_vrfb; +static int def_rotate; +static int def_mirror; + +#ifdef DEBUG +unsigned int omapfb_debug; +module_param_named(debug, omapfb_debug, bool, 0644); +static unsigned int omapfb_test_pattern = 1; /* change this to 0 to test logo */ +module_param_named(test, omapfb_test_pattern, bool, 0644); +#endif + +#ifdef DEBUG +static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color) +{ + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + void __iomem *addr = fbi->screen_base; + const unsigned bytespp = var->bits_per_pixel >> 3; + const unsigned line_len = fix->line_length / bytespp; + + int r = (color >> 16) & 0xff; + int g = (color >> 8) & 0xff; + int b = (color >> 0) & 0xff; + + if (var->bits_per_pixel == 16) { + u16 __iomem *p = (u16 __iomem *)addr; + p += y * line_len + x; + + r = r * 32 / 256; + g = g * 64 / 256; + b = b * 32 / 256; + + __raw_writew((r << 11) | (g << 5) | (b << 0), p); + } else if (var->bits_per_pixel == 24) { + u8 __iomem *p = (u8 __iomem *)addr; + p += (y * line_len + x) * 3; + + __raw_writeb(b, p + 0); + __raw_writeb(g, p + 1); + __raw_writeb(r, p + 2); + } else if (var->bits_per_pixel == 32) { + u32 __iomem *p = (u32 __iomem *)addr; + p += y * line_len + x; + __raw_writel(color, p); + } +} + +static void fill_fb(struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + const short w = var->xres_virtual; + const short h = var->yres_virtual; + void __iomem *addr = fbi->screen_base; + int y, x; + + if (!addr) + return; + + DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length); + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + if (x < 20 && y < 20) + draw_pixel(fbi, x, y, 0xffffff); + else if (x < 20 && (y > 20 && y < h - 20)) + draw_pixel(fbi, x, y, 0xff); + else if (y < 20 && (x > 20 && x < w - 20)) + draw_pixel(fbi, x, y, 0xff00); + else if (x > w - 20 && (y > 20 && y < h - 20)) + draw_pixel(fbi, x, y, 0xff0000); + else if (y > h - 20 && (x > 20 && x < w - 20)) + draw_pixel(fbi, x, y, 0xffff00); + else if (x == 20 || x == w - 20 || + y == 20 || y == h - 20) + draw_pixel(fbi, x, y, 0xffffff); + else if (x == y || w - x == h - y) + draw_pixel(fbi, x, y, 0xff00ff); + else if (w - x == y || x == h - y) + draw_pixel(fbi, x, y, 0x00ffff); + else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { + int t = x * 3 / w; + unsigned r = 0, g = 0, b = 0; + unsigned c; + if (var->bits_per_pixel == 16) { + if (t == 0) + b = (y % 32) * 256 / 32; + else if (t == 1) + g = (y % 64) * 256 / 64; + else if (t == 2) + r = (y % 32) * 256 / 32; + } else { + if (t == 0) + b = (y % 256); + else if (t == 1) + g = (y % 256); + else if (t == 2) + r = (y % 256); + } + c = (r << 16) | (g << 8) | (b << 0); + draw_pixel(fbi, x, y, c); + } else { + draw_pixel(fbi, x, y, 0); + } + } + } +} +#endif + +static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot) +{ + struct vrfb *vrfb = &ofbi->region.vrfb; + unsigned offset; + + switch (rot) { + case FB_ROTATE_UR: + offset = 0; + break; + case FB_ROTATE_CW: + offset = vrfb->yoffset; + break; + case FB_ROTATE_UD: + offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset; + break; + case FB_ROTATE_CCW: + offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN; + break; + default: + BUG(); + } + + offset *= vrfb->bytespp; + + return offset; +} + +static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi, int rot) +{ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + return ofbi->region.vrfb.paddr[rot] + + omapfb_get_vrfb_offset(ofbi, rot); + } else { + return ofbi->region.paddr; + } +} + +u32 omapfb_get_region_paddr(struct omapfb_info *ofbi) +{ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + return ofbi->region.vrfb.paddr[0]; + else + return ofbi->region.paddr; +} + +void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi) +{ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + return ofbi->region.vrfb.vaddr[0]; + else + return ofbi->region.vaddr; +} + +static struct omapfb_colormode omapfb_colormodes[] = { + { + .dssmode = OMAP_DSS_COLOR_UYVY, + .bits_per_pixel = 16, + .nonstd = OMAPFB_COLOR_YUV422, + }, { + .dssmode = OMAP_DSS_COLOR_YUV2, + .bits_per_pixel = 16, + .nonstd = OMAPFB_COLOR_YUY422, + }, { + .dssmode = OMAP_DSS_COLOR_ARGB16, + .bits_per_pixel = 16, + .red = { .length = 4, .offset = 8, .msb_right = 0 }, + .green = { .length = 4, .offset = 4, .msb_right = 0 }, + .blue = { .length = 4, .offset = 0, .msb_right = 0 }, + .transp = { .length = 4, .offset = 12, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGB16, + .bits_per_pixel = 16, + .red = { .length = 5, .offset = 11, .msb_right = 0 }, + .green = { .length = 6, .offset = 5, .msb_right = 0 }, + .blue = { .length = 5, .offset = 0, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGB24P, + .bits_per_pixel = 24, + .red = { .length = 8, .offset = 16, .msb_right = 0 }, + .green = { .length = 8, .offset = 8, .msb_right = 0 }, + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGB24U, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 16, .msb_right = 0 }, + .green = { .length = 8, .offset = 8, .msb_right = 0 }, + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_ARGB32, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 16, .msb_right = 0 }, + .green = { .length = 8, .offset = 8, .msb_right = 0 }, + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, + .transp = { .length = 8, .offset = 24, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGBA32, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 24, .msb_right = 0 }, + .green = { .length = 8, .offset = 16, .msb_right = 0 }, + .blue = { .length = 8, .offset = 8, .msb_right = 0 }, + .transp = { .length = 8, .offset = 0, .msb_right = 0 }, + }, { + .dssmode = OMAP_DSS_COLOR_RGBX32, + .bits_per_pixel = 32, + .red = { .length = 8, .offset = 24, .msb_right = 0 }, + .green = { .length = 8, .offset = 16, .msb_right = 0 }, + .blue = { .length = 8, .offset = 8, .msb_right = 0 }, + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, + }, +}; + +static bool cmp_var_to_colormode(struct fb_var_screeninfo *var, + struct omapfb_colormode *color) +{ + bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2) + { + return f1->length == f2->length && + f1->offset == f2->offset && + f1->msb_right == f2->msb_right; + } + + if (var->bits_per_pixel == 0 || + var->red.length == 0 || + var->blue.length == 0 || + var->green.length == 0) + return 0; + + return var->bits_per_pixel == color->bits_per_pixel && + cmp_component(&var->red, &color->red) && + cmp_component(&var->green, &color->green) && + cmp_component(&var->blue, &color->blue) && + cmp_component(&var->transp, &color->transp); +} + +static void assign_colormode_to_var(struct fb_var_screeninfo *var, + struct omapfb_colormode *color) +{ + var->bits_per_pixel = color->bits_per_pixel; + var->nonstd = color->nonstd; + var->red = color->red; + var->green = color->green; + var->blue = color->blue; + var->transp = color->transp; +} + +static enum omap_color_mode fb_mode_to_dss_mode(struct fb_var_screeninfo *var) +{ + enum omap_color_mode dssmode; + int i; + + /* first match with nonstd field */ + if (var->nonstd) { + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *mode = &omapfb_colormodes[i]; + if (var->nonstd == mode->nonstd) { + assign_colormode_to_var(var, mode); + return mode->dssmode; + } + } + + return -EINVAL; + } + + /* then try exact match of bpp and colors */ + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *mode = &omapfb_colormodes[i]; + if (cmp_var_to_colormode(var, mode)) { + assign_colormode_to_var(var, mode); + return mode->dssmode; + } + } + + /* match with bpp if user has not filled color fields + * properly */ + switch (var->bits_per_pixel) { + case 1: + dssmode = OMAP_DSS_COLOR_CLUT1; + break; + case 2: + dssmode = OMAP_DSS_COLOR_CLUT2; + break; + case 4: + dssmode = OMAP_DSS_COLOR_CLUT4; + break; + case 8: + dssmode = OMAP_DSS_COLOR_CLUT8; + break; + case 12: + dssmode = OMAP_DSS_COLOR_RGB12U; + break; + case 16: + dssmode = OMAP_DSS_COLOR_RGB16; + break; + case 24: + dssmode = OMAP_DSS_COLOR_RGB24P; + break; + case 32: + dssmode = OMAP_DSS_COLOR_RGB24U; + break; + default: + return -EINVAL; + } + /* DBG("fb_mode_to_dss_mode bits_per_pixel = %d\n", var->bits_per_pixel);*/ + + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *mode = &omapfb_colormodes[i]; + if (dssmode == mode->dssmode) { + assign_colormode_to_var(var, mode); + return mode->dssmode; + } + } + + return -EINVAL; +} + +int dss_mode_to_fb_mode(enum omap_color_mode dssmode, + struct fb_var_screeninfo *var) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *mode = &omapfb_colormodes[i]; + if (dssmode == mode->dssmode) { + assign_colormode_to_var(var, mode); + return 0; + } + } + return -ENOENT; +} + +void set_fb_fix(struct fb_info *fbi) +{ + struct fb_fix_screeninfo *fix = &fbi->fix; + struct fb_var_screeninfo *var = &fbi->var; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_mem_region *rg = &ofbi->region; + + DBG("set_fb_fix\n"); + + /* used by open/write in fbmem.c */ + fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi); + + DBG("changing rotation to %d\n", var->rotate); + + /* used by mmap in fbmem.c */ + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + switch (var->nonstd) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + fix->line_length = + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; + break; + default: + fix->line_length = + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; + break; + } + } else + fix->line_length = + (var->xres_virtual * var->bits_per_pixel) >> 3; + fix->smem_start = omapfb_get_region_paddr(ofbi); + fix->smem_len = rg->size; + + fix->type = FB_TYPE_PACKED_PIXELS; + + if (var->nonstd) + fix->visual = FB_VISUAL_PSEUDOCOLOR; + else { + switch (var->bits_per_pixel) { + case 32: + case 24: + case 16: + case 12: + fix->visual = FB_VISUAL_TRUECOLOR; + /* 12bpp is stored in 16 bits */ + break; + case 1: + case 2: + case 4: + case 8: + fix->visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + } + + fix->accel = FB_ACCEL_NONE; + + fix->xpanstep = 1; + fix->ypanstep = 1; + + if (rg->size && ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + enum omap_color_mode mode = 0; + mode = fb_mode_to_dss_mode(var); + + omap_vrfb_setup(&rg->vrfb, rg->paddr, + var->xres_virtual, + var->yres_virtual, + mode); + } +} + +/* check new var and possibly modify it to be ok */ +int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omap_dss_device *display = fb2display(fbi); + unsigned long max_frame_size; + unsigned long line_size; + int xres_min, yres_min; + int xres_max, yres_max; + enum omap_color_mode mode = 0; + int i; + int bytespp; + + DBG("check_fb_var %d\n", ofbi->id); + + if (ofbi->region.size == 0) + return 0; + + mode = fb_mode_to_dss_mode(var); + if (mode < 0) { + DBG("cannot convert var to omap dss mode\n"); + return -EINVAL; + } + + for (i = 0; i < ofbi->num_overlays; ++i) { + if ((ofbi->overlays[i]->supported_modes & mode) == 0) { + DBG("invalid mode\n"); + return -EINVAL; + } + } + + if (var->rotate < 0 || var->rotate > 3) + return -EINVAL; + + xres_min = OMAPFB_PLANE_XRES_MIN; + xres_max = 2048; + yres_min = OMAPFB_PLANE_YRES_MIN; + yres_max = 2048; + + bytespp = var->bits_per_pixel >> 3; + + /* XXX: some applications seem to set virtual res to 0. */ + if (var->xres_virtual == 0) + var->xres_virtual = var->xres; + + if (var->yres_virtual == 0) + var->yres_virtual = var->yres; + + if (var->xres_virtual < xres_min || var->yres_virtual < yres_min) + return -EINVAL; + + if (var->xres < xres_min) + var->xres = xres_min; + if (var->yres < yres_min) + var->yres = yres_min; + if (var->xres > xres_max) + var->xres = xres_max; + if (var->yres > yres_max) + var->yres = yres_max; + + if (var->xres > var->xres_virtual) + var->xres = var->xres_virtual; + if (var->yres > var->yres_virtual) + var->yres = var->yres_virtual; + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + line_size = OMAP_VRFB_LINE_LEN * bytespp; + else + line_size = var->xres_virtual * bytespp; + + max_frame_size = ofbi->region.size; + + DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size); + + if (line_size * var->yres_virtual > max_frame_size) { + DBG("can't fit FB into memory, reducing y\n"); + var->yres_virtual = max_frame_size / line_size; + + if (var->yres_virtual < yres_min) + var->yres_virtual = yres_min; + + if (var->yres > var->yres_virtual) + var->yres = var->yres_virtual; + } + + if (line_size * var->yres_virtual > max_frame_size) { + DBG("can't fit FB into memory, reducing x\n"); + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + return -EINVAL; + + var->xres_virtual = max_frame_size / var->yres_virtual / + bytespp; + + if (var->xres_virtual < xres_min) + var->xres_virtual = xres_min; + + if (var->xres > var->xres_virtual) + var->xres = var->xres_virtual; + + line_size = var->xres_virtual * bytespp; + } + + if (line_size * var->yres_virtual > max_frame_size) { + DBG("cannot fit FB to memory\n"); + return -EINVAL; + } + + if (var->xres + var->xoffset > var->xres_virtual) + var->xoffset = var->xres_virtual - var->xres; + if (var->yres + var->yoffset > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + + DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", + var->xres, var->yres, + var->xres_virtual, var->yres_virtual); + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + if (display && display->get_timings) { + struct omap_video_timings timings; + display->get_timings(display, &timings); + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = timings.pixel_clock != 0 ? + KHZ2PICOS(timings.pixel_clock) : + 0; + var->left_margin = timings.hfp; + var->right_margin = timings.hbp; + var->upper_margin = timings.vfp; + var->lower_margin = timings.vbp; + var->hsync_len = timings.hsw; + var->vsync_len = timings.vsw; + } else { + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + } + + /* TODO: get these from panel->config */ + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + +/* + * --------------------------------------------------------------------------- + * fbdev framework callbacks + * --------------------------------------------------------------------------- + */ +static int omapfb_open(struct fb_info *fbi, int user) +{ + return 0; +} + +static int omapfb_release(struct fb_info *fbi, int user) +{ +#if 0 + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + + DBG("Closing fb with plane index %d\n", ofbi->id); + + omapfb_lock(fbdev); + + if (display && display->get_update_mode && display->update) { + /* XXX this update should be removed, I think. But it's + * good for debugging */ + if (display->get_update_mode(display) == + OMAP_DSS_UPDATE_MANUAL) { + u16 w, h; + + if (display->sync) + display->sync(display); + + display->get_resolution(display, &w, &h); + display->update(display, 0, 0, w, h); + } + } + + if (display && display->sync) + display->sync(display); + + omapfb_unlock(fbdev); +#endif + return 0; +} + +/* setup overlay according to the fb */ +static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, + u16 posx, u16 posy, u16 outw, u16 outh) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + enum omap_color_mode mode = 0; + int offset; + u32 data_start_p; + void __iomem *data_start_v; + struct omap_overlay_info info; + int xres, yres; + int screen_width; + int mirror; + int rotation = var->rotate; + int i; + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ovl != ofbi->overlays[i]) + continue; + + rotation = (rotation + ofbi->rotation[i]) % 4; + break; + } + + DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id, + posx, posy, outw, outh); + + if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { + xres = var->yres; + yres = var->xres; + } else { + xres = var->xres; + yres = var->yres; + } + + offset = ((var->yoffset * var->xres_virtual + + var->xoffset) * var->bits_per_pixel) >> 3; + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); + data_start_v = NULL; + } else { + data_start_p = omapfb_get_region_paddr(ofbi); + data_start_v = omapfb_get_region_vaddr(ofbi); + } + + data_start_p += offset; + data_start_v += offset; + + mode = fb_mode_to_dss_mode(var); + + if (mode == -EINVAL) { + DBG("fb_mode_to_dss_mode failed"); + r = -EINVAL; + goto err; + } + + switch (var->nonstd) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUY422: + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + screen_width = fix->line_length + / (var->bits_per_pixel >> 2); + break; + } + default: + screen_width = fix->line_length / (var->bits_per_pixel >> 3); + break; + } + + ovl->get_overlay_info(ovl, &info); + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + mirror = 0; + else + mirror = ofbi->mirror; + + info.paddr = data_start_p; + info.vaddr = data_start_v; + info.screen_width = screen_width; + info.width = xres; + info.height = yres; + info.color_mode = mode; + info.rotation_type = ofbi->rotation_type; + info.rotation = rotation; + info.mirror = mirror; + + info.pos_x = posx; + info.pos_y = posy; + info.out_width = outw; + info.out_height = outh; + + r = ovl->set_overlay_info(ovl, &info); + if (r) { + DBG("ovl->setup_overlay_info failed\n"); + goto err; + } + + return 0; + +err: + DBG("setup_overlay failed\n"); + return r; +} + +/* apply var to the overlay */ +int omapfb_apply_changes(struct fb_info *fbi, int init) +{ + int r = 0; + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_var_screeninfo *var = &fbi->var; + struct omap_overlay *ovl; + u16 posx, posy; + u16 outw, outh; + int i; + +#ifdef DEBUG + if (omapfb_test_pattern) + fill_fb(fbi); +#endif + + for (i = 0; i < ofbi->num_overlays; i++) { + ovl = ofbi->overlays[i]; + + DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); + + if (ofbi->region.size == 0) { + /* the fb is not available. disable the overlay */ + omapfb_overlay_enable(ovl, 0); + if (!init && ovl->manager) + ovl->manager->apply(ovl->manager); + continue; + } + + if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { + int rotation = (var->rotate + ofbi->rotation[i]) % 4; + if (rotation == FB_ROTATE_CW || + rotation == FB_ROTATE_CCW) { + outw = var->yres; + outh = var->xres; + } else { + outw = var->xres; + outh = var->yres; + } + } else { + outw = ovl->info.out_width; + outh = ovl->info.out_height; + } + + if (init) { + posx = 0; + posy = 0; + } else { + posx = ovl->info.pos_x; + posy = ovl->info.pos_y; + } + + r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); + if (r) + goto err; + + if (!init && ovl->manager) + ovl->manager->apply(ovl->manager); + } + return 0; +err: + DBG("apply_changes failed\n"); + return r; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + int r; + + DBG("check_var(%d)\n", FB2OFB(fbi)->id); + + r = check_fb_var(fbi, var); + + return r; +} + +/* set the video mode according to info->var */ +static int omapfb_set_par(struct fb_info *fbi) +{ + int r; + + DBG("set_par(%d)\n", FB2OFB(fbi)->id); + + set_fb_fix(fbi); + r = omapfb_apply_changes(fbi, 0); + + return r; +} + +static int omapfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + int r = 0; + + DBG("pan_display(%d)\n", ofbi->id); + + if (var->xoffset != fbi->var.xoffset || + var->yoffset != fbi->var.yoffset) { + struct fb_var_screeninfo new_var; + + new_var = fbi->var; + new_var.xoffset = var->xoffset; + new_var.yoffset = var->yoffset; + + r = check_fb_var(fbi, &new_var); + + if (r == 0) { + fbi->var = new_var; + set_fb_fix(fbi); + r = omapfb_apply_changes(fbi, 0); + } + } + + return r; +} + +static void mmap_user_open(struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; + + atomic_inc(&ofbi->map_count); +} + +static void mmap_user_close(struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; + + atomic_dec(&ofbi->map_count); +} + +static struct vm_operations_struct mmap_user_ops = { + .open = mmap_user_open, + .close = mmap_user_close, +}; + +static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct fb_fix_screeninfo *fix = &fbi->fix; + unsigned long off; + unsigned long start; + u32 len; + + if (vma->vm_end - vma->vm_start == 0) + return 0; + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + off = vma->vm_pgoff << PAGE_SHIFT; + + start = omapfb_get_region_paddr(ofbi); + len = fix->smem_len; + if (off >= len) + return -EINVAL; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + + off += start; + + DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off); + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO | VM_RESERVED; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &mmap_user_ops; + vma->vm_private_data = ofbi; + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -EAGAIN; + /* vm_ops.open won't be called for mmap itself. */ + atomic_inc(&ofbi->map_count); + return 0; +} + +/* Store a single color palette entry into a pseudo palette or the hardware + * palette if one is available. For now we support only 16bpp and thus store + * the entry only to the pseudo palette. + */ +static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, + u_int blue, u_int transp, int update_hw_pal) +{ + /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ + /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ + struct fb_var_screeninfo *var = &fbi->var; + int r = 0; + + enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ + + /*switch (plane->color_mode) {*/ + switch (mode) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUV420: + case OMAPFB_COLOR_YUY422: + r = -EINVAL; + break; + case OMAPFB_COLOR_CLUT_8BPP: + case OMAPFB_COLOR_CLUT_4BPP: + case OMAPFB_COLOR_CLUT_2BPP: + case OMAPFB_COLOR_CLUT_1BPP: + /* + if (fbdev->ctrl->setcolreg) + r = fbdev->ctrl->setcolreg(regno, red, green, blue, + transp, update_hw_pal); + */ + /* Fallthrough */ + r = -EINVAL; + break; + case OMAPFB_COLOR_RGB565: + case OMAPFB_COLOR_RGB444: + case OMAPFB_COLOR_RGB24P: + case OMAPFB_COLOR_RGB24U: + if (r != 0) + break; + + if (regno < 0) { + r = -EINVAL; + break; + } + + if (regno < 16) { + u16 pal; + pal = ((red >> (16 - var->red.length)) << + var->red.offset) | + ((green >> (16 - var->green.length)) << + var->green.offset) | + (blue >> (16 - var->blue.length)); + ((u32 *)(fbi->pseudo_palette))[regno] = pal; + } + break; + default: + BUG(); + } + return r; +} + +static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + DBG("setcolreg\n"); + + return _setcolreg(info, regno, red, green, blue, transp, 1); +} + +static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + int count, index, r; + u16 *red, *green, *blue, *transp; + u16 trans = 0xffff; + + DBG("setcmap\n"); + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + index = cmap->start; + + for (count = 0; count < cmap->len; count++) { + if (transp) + trans = *transp++; + r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, + count == cmap->len - 1); + if (r != 0) + return r; + } + + return 0; +} + +static void omapfb_vrfb_suspend_all(struct omapfb2_device *fbdev) +{ + int i; + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + if (ofbi->region.vrfb.vaddr[0]) + omap_vrfb_suspend_ctx(&ofbi->region.vrfb); + } +} + +static void omapfb_vrfb_resume_all(struct omapfb2_device *fbdev) +{ + int i; + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + if (ofbi->region.vrfb.vaddr[0]) + omap_vrfb_resume_ctx(&ofbi->region.vrfb); + } +} + +static int omapfb_blank(int blank, struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + int do_update = 0; + int r = 0; + + omapfb_lock(fbdev); + + switch (blank) { + case FB_BLANK_UNBLANK: + if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) + goto exit; + + omapfb_vrfb_resume_all(fbdev); + + if (display->resume) + r = display->resume(display); + + if (r == 0 && display->get_update_mode && + display->get_update_mode(display) == + OMAP_DSS_UPDATE_MANUAL) + do_update = 1; + + break; + + case FB_BLANK_NORMAL: + /* FB_BLANK_NORMAL could be implemented. + * Needs DSS additions. */ + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + if (display->state != OMAP_DSS_DISPLAY_ACTIVE) + goto exit; + + if (display->suspend) + r = display->suspend(display); + + omapfb_vrfb_suspend_all(fbdev); + + break; + + default: + r = -EINVAL; + } + +exit: + omapfb_unlock(fbdev); + + if (r == 0 && do_update && display->update) { + u16 w, h; + display->get_resolution(display, &w, &h); + + r = display->update(display, 0, 0, w, h); + } + + return r; +} + +#if 0 +/* XXX fb_read and fb_write are needed for VRFB */ +ssize_t omapfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos); + /* XXX needed for VRFB */ + return count; +} +#endif + +static struct fb_ops omapfb_ops = { + .owner = THIS_MODULE, + .fb_open = omapfb_open, + .fb_release = omapfb_release, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = omapfb_blank, + .fb_ioctl = omapfb_ioctl, + .fb_check_var = omapfb_check_var, + .fb_set_par = omapfb_set_par, + .fb_pan_display = omapfb_pan_display, + .fb_mmap = omapfb_mmap, + .fb_setcolreg = omapfb_setcolreg, + .fb_setcmap = omapfb_setcmap, + /*.fb_write = omapfb_write,*/ +}; + +static void omapfb_free_fbmem(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb2_mem_region *rg; + + rg = &ofbi->region; + + if (rg->paddr) + if (omap_vram_free(rg->paddr, rg->size)) + dev_err(fbdev->dev, "VRAM FREE failed\n"); + + if (rg->vaddr) + iounmap(rg->vaddr); + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + /* unmap the 0 angle rotation */ + if (rg->vrfb.vaddr[0]) { + iounmap(rg->vrfb.vaddr[0]); + omap_vrfb_release_ctx(&rg->vrfb); + } + } + + rg->vaddr = NULL; + rg->paddr = 0; + rg->alloc = 0; + rg->size = 0; +} + +static void clear_fb_info(struct fb_info *fbi) +{ + memset(&fbi->var, 0, sizeof(fbi->var)); + memset(&fbi->fix, 0, sizeof(fbi->fix)); + strlcpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id)); +} + +static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free all fbmem\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + struct fb_info *fbi = fbdev->fbs[i]; + omapfb_free_fbmem(fbi); + clear_fb_info(fbi); + } + + return 0; +} + +static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, + unsigned long paddr) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omapfb2_mem_region *rg; + void __iomem *vaddr; + int r; + + rg = &ofbi->region; + memset(rg, 0, sizeof(*rg)); + + size = PAGE_ALIGN(size); + + if (!paddr) { + DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); + r = omap_vram_alloc(OMAPFB_MEMTYPE_SDRAM, size, &paddr); + } else { + DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, + ofbi->id); + r = omap_vram_reserve(paddr, size); + } + + if (r) { + dev_err(fbdev->dev, "failed to allocate framebuffer\n"); + return -ENOMEM; + } + + if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) { + vaddr = ioremap_wc(paddr, size); + + if (!vaddr) { + dev_err(fbdev->dev, "failed to ioremap framebuffer\n"); + omap_vram_free(paddr, size); + return -ENOMEM; + } + + /* DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr); */ + } else { + void __iomem *va; + + r = omap_vrfb_request_ctx(&rg->vrfb); + if (r) { + dev_err(fbdev->dev, "vrfb create ctx failed\n"); + return r; + } + + /* only ioremap the 0 angle view */ + va = ioremap_wc(rg->vrfb.paddr[0], size); + + if (!va) { + printk(KERN_ERR "vrfb: ioremap failed\n"); + omap_vrfb_release_ctx(&rg->vrfb); + return -ENOMEM; + } + + DBG("ioremapped vrfb area 0 to %p\n", va); + + rg->vrfb.vaddr[0] = va; + + vaddr = NULL; + } + + rg->paddr = paddr; + rg->vaddr = vaddr; + rg->size = size; + rg->alloc = 1; + + return 0; +} + +/* allocate fbmem using display resolution as reference */ +static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, + unsigned long paddr) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omap_dss_device *display; + int bytespp; + + display = fb2display(fbi); + + if (!display) + return 0; + + switch (display->get_recommended_bpp(display)) { + case 16: + bytespp = 2; + break; + case 24: + bytespp = 4; + break; + default: + bytespp = 4; + break; + } + + if (!size) { + u16 w, h; + + display->get_resolution(display, &w, &h); + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { +#ifdef DEBUG + int oldw = w, oldh = h; +#endif + + omap_vrfb_adjust_size(&w, &h, bytespp); + + /* Because we change the resolution of the 0 degree + * view, we need to alloc max(w, h) for height */ + h = max(w, h); + w = OMAP_VRFB_LINE_LEN; + + DBG("adjusting fb mem size for VRFB, %dx%d -> %dx%d\n", + oldw, oldh, w, h); + } + + size = w * h * bytespp; + } + + if (!size) + return 0; + + return omapfb_alloc_fbmem(fbi, size, paddr); +} + +static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt) +{ + enum omap_color_mode mode; + + switch (fmt) { + case OMAPFB_COLOR_RGB565: + mode = OMAP_DSS_COLOR_RGB16; + break; + case OMAPFB_COLOR_YUV422: + mode = OMAP_DSS_COLOR_YUV2; + break; + case OMAPFB_COLOR_CLUT_8BPP: + mode = OMAP_DSS_COLOR_CLUT8; + break; + case OMAPFB_COLOR_CLUT_4BPP: + mode = OMAP_DSS_COLOR_CLUT4; + break; + case OMAPFB_COLOR_CLUT_2BPP: + mode = OMAP_DSS_COLOR_CLUT2; + break; + case OMAPFB_COLOR_CLUT_1BPP: + mode = OMAP_DSS_COLOR_CLUT1; + break; + case OMAPFB_COLOR_RGB444: + mode = OMAP_DSS_COLOR_RGB12U; + break; + case OMAPFB_COLOR_YUY422: + mode = OMAP_DSS_COLOR_UYVY; + break; + case OMAPFB_COLOR_ARGB16: + mode = OMAP_DSS_COLOR_ARGB16; + break; + case OMAPFB_COLOR_RGB24U: + mode = OMAP_DSS_COLOR_RGB24U; + break; + case OMAPFB_COLOR_RGB24P: + mode = OMAP_DSS_COLOR_RGB24P; + break; + case OMAPFB_COLOR_ARGB32: + mode = OMAP_DSS_COLOR_ARGB32; + break; + case OMAPFB_COLOR_RGBA32: + mode = OMAP_DSS_COLOR_RGBA32; + break; + case OMAPFB_COLOR_RGBX32: + mode = OMAP_DSS_COLOR_RGBX32; + break; + default: + mode = -EINVAL; + } + + return mode; +} + +static int omapfb_parse_vram_param(const char *param, int max_entries, + unsigned long *sizes, unsigned long *paddrs) +{ + int fbnum; + unsigned long size; + unsigned long paddr = 0; + char *p, *start; + + start = (char *)param; + + while (1) { + p = start; + + fbnum = simple_strtoul(p, &p, 10); + + if (p == param) + return -EINVAL; + + if (*p != ':') + return -EINVAL; + + if (fbnum >= max_entries) + return -EINVAL; + + size = memparse(p + 1, &p); + + if (!size) + return -EINVAL; + + paddr = 0; + + if (*p == '@') { + paddr = simple_strtoul(p + 1, &p, 16); + + if (!paddr) + return -EINVAL; + + } + + paddrs[fbnum] = paddr; + sizes[fbnum] = size; + + if (*p == 0) + break; + + if (*p != ',') + return -EINVAL; + + ++p; + + start = p; + } + + return 0; +} + +static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) +{ + int i, r; + unsigned long vram_sizes[10]; + unsigned long vram_paddrs[10]; + + memset(&vram_sizes, 0, sizeof(vram_sizes)); + memset(&vram_paddrs, 0, sizeof(vram_paddrs)); + + if (def_vram && omapfb_parse_vram_param(def_vram, 10, + vram_sizes, vram_paddrs)) { + dev_err(fbdev->dev, "failed to parse vram parameter\n"); + + memset(&vram_sizes, 0, sizeof(vram_sizes)); + memset(&vram_paddrs, 0, sizeof(vram_paddrs)); + } + + if (fbdev->dev->platform_data) { + struct omapfb_platform_data *opd; + opd = fbdev->dev->platform_data; + for (i = 0; i < opd->mem_desc.region_cnt; ++i) { + if (!vram_sizes[i]) { + unsigned long size; + unsigned long paddr; + + size = opd->mem_desc.region[i].size; + paddr = opd->mem_desc.region[i].paddr; + + vram_sizes[i] = size; + vram_paddrs[i] = paddr; + } + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + /* allocate memory automatically only for fb0, or if + * excplicitly defined with vram or plat data option */ + if (i == 0 || vram_sizes[i] != 0) { + r = omapfb_alloc_fbmem_display(fbdev->fbs[i], + vram_sizes[i], vram_paddrs[i]); + + if (r) + return r; + } + } + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + struct omapfb2_mem_region *rg; + rg = &ofbi->region; + + DBG("region%d phys %08x virt %p size=%lu\n", + i, + rg->paddr, + rg->vaddr, + rg->size); + } + + return 0; +} + +int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_dss_device *display = fb2display(fbi); + struct omapfb2_mem_region *rg = &ofbi->region; + unsigned long old_size = rg->size; + unsigned long old_paddr = rg->paddr; + int old_type = rg->type; + int r; + + if (type > OMAPFB_MEMTYPE_MAX) + return -EINVAL; + + size = PAGE_ALIGN(size); + + if (old_size == size && old_type == type) + return 0; + + if (display && display->sync) + display->sync(display); + + omapfb_free_fbmem(fbi); + + if (size == 0) { + clear_fb_info(fbi); + return 0; + } + + r = omapfb_alloc_fbmem(fbi, size, 0); + + if (r) { + if (old_size) + omapfb_alloc_fbmem(fbi, old_size, old_paddr); + + if (rg->size == 0) + clear_fb_info(fbi); + + return r; + } + + if (old_size == size) + return 0; + + if (old_size == 0) { + DBG("initializing fb %d\n", ofbi->id); + r = omapfb_fb_init(fbdev, fbi); + if (r) { + DBG("omapfb_fb_init failed\n"); + goto err; + } + r = omapfb_apply_changes(fbi, 1); + if (r) { + DBG("omapfb_apply_changes failed\n"); + goto err; + } + } else { + struct fb_var_screeninfo new_var; + memcpy(&new_var, &fbi->var, sizeof(new_var)); + r = check_fb_var(fbi, &new_var); + if (r) + goto err; + memcpy(&fbi->var, &new_var, sizeof(fbi->var)); + set_fb_fix(fbi); + } + + return 0; +err: + omapfb_free_fbmem(fbi); + clear_fb_info(fbi); + return r; +} + +/* initialize fb_info, var, fix to something sane based on the display */ +int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + struct fb_var_screeninfo *var = &fbi->var; + struct omap_dss_device *display = fb2display(fbi); + struct omapfb_info *ofbi = FB2OFB(fbi); + int r = 0; + + fbi->fbops = &omapfb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = fbdev->pseudo_palette; + +/* strncpy(fix->id, MODULE_NAME, sizeof(fix->id)); */ + + if (ofbi->region.size == 0) { + memset(&fbi->fix, 0, sizeof(fbi->fix)); + memset(&fbi->var, 0, sizeof(fbi->var)); + clear_fb_info(fbi); + return 0; + } + + var->nonstd = 0; + var->bits_per_pixel = 0; + + var->rotate = def_rotate; + + /* + * Check if there is a default color format set in the board file, + * and use this format instead the default deducted from the + * display bpp. + */ + if (fbdev->dev->platform_data) { + struct omapfb_platform_data *opd; + int id = ofbi->id; + + opd = fbdev->dev->platform_data; + if (opd->mem_desc.region[id].format_used) { + enum omap_color_mode mode; + enum omapfb_color_format format; + + format = opd->mem_desc.region[id].format; + mode = fb_format_to_dss_mode(format); + if (mode < 0) { + r = mode; + goto err; + } + r = dss_mode_to_fb_mode(mode, var); + if (r < 0) + goto err; + } + } + + if (display) { + u16 w, h; + int rotation = (var->rotate + ofbi->rotation[0]) % 4; + + display->get_resolution(display, &w, &h); + + if (rotation == FB_ROTATE_CW || + rotation == FB_ROTATE_CCW) { + var->xres = h; + var->yres = w; + } else { + var->xres = w; + var->yres = h; + } + + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + + if (!var->bits_per_pixel) { + switch (display->get_recommended_bpp(display)) { + case 16: + var->bits_per_pixel = 16; + break; + case 24: + var->bits_per_pixel = 32; + break; + default: + dev_err(fbdev->dev, "illegal display " + "bpp\n"); + return -EINVAL; + } + } + } else { + /* if there's no display, let's just guess some basic values */ + var->xres = 320; + var->yres = 240; + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + if (!var->bits_per_pixel) + var->bits_per_pixel = 16; + } + + r = check_fb_var(fbi, var); + if (r) + goto err; + + set_fb_fix(fbi); + + r = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (r) + dev_err(fbdev->dev, "unable to allocate color map memory\n"); + +err: + return r; +} + +static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) +{ + fb_dealloc_cmap(&fbi->cmap); +} + + +static void omapfb_free_resources(struct omapfb2_device *fbdev) +{ + int i; + + DBG("free_resources\n"); + + if (fbdev == NULL) + return; + + for (i = 0; i < fbdev->num_fbs; i++) + unregister_framebuffer(fbdev->fbs[i]); + + /* free the reserved fbmem */ + omapfb_free_all_fbmem(fbdev); + + for (i = 0; i < fbdev->num_fbs; i++) { + fbinfo_cleanup(fbdev, fbdev->fbs[i]); + framebuffer_release(fbdev->fbs[i]); + } + + for (i = 0; i < fbdev->num_displays; i++) { + if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED) + fbdev->displays[i]->disable(fbdev->displays[i]); + + omap_dss_put_device(fbdev->displays[i]); + } + + dev_set_drvdata(fbdev->dev, NULL); + kfree(fbdev); +} + +static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) +{ + int r, i; + + fbdev->num_fbs = 0; + + /* DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS); */ + + /* allocate fb_infos */ + for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) { + struct fb_info *fbi; + struct omapfb_info *ofbi; + + fbi = framebuffer_alloc(sizeof(struct omapfb_info), + fbdev->dev); + + if (fbi == NULL) { + dev_err(fbdev->dev, + "unable to allocate memory for plane info\n"); + return -ENOMEM; + } + + clear_fb_info(fbi); + + fbdev->fbs[i] = fbi; + + ofbi = FB2OFB(fbi); + ofbi->fbdev = fbdev; + ofbi->id = i; + + /* assign these early, so that fb alloc can use them */ + ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : + OMAP_DSS_ROT_DMA; + ofbi->mirror = def_mirror; + + fbdev->num_fbs++; + } + + DBG("fb_infos allocated\n"); + + /* assign overlays for the fbs */ + for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + if (i == 0) + { + ofbi->overlays[0] = fbdev->overlays[1];/* yong 10/2 zebu test */ + } + else if(i == 1) + { + ofbi->overlays[0] = fbdev->overlays[0]; + } + else + { + ofbi->overlays[0] = fbdev->overlays[i]; + + } + ofbi->num_overlays = 1; + } + + /* allocate fb memories */ + r = omapfb_allocate_all_fbs(fbdev); + if (r) { + dev_err(fbdev->dev, "failed to allocate fbmem\n"); + return r; + } + + DBG("fbmems allocated\n"); + + /* setup fb_infos */ + for (i = 0; i < fbdev->num_fbs; i++) { + r = omapfb_fb_init(fbdev, fbdev->fbs[i]); + if (r) { + dev_err(fbdev->dev, "failed to setup fb_info\n"); + return r; + } + } + + DBG("fb_infos initialized\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = register_framebuffer(fbdev->fbs[i]); + if (r != 0) { + dev_err(fbdev->dev, + "registering framebuffer %d failed\n", i); + return r; + } + } + + DBG("framebuffers registered\n"); + + for (i = 0; i < fbdev->num_fbs; i++) { + r = omapfb_apply_changes(fbdev->fbs[i], 1); + if (r) { + dev_err(fbdev->dev, "failed to change mode\n"); + return r; + } + } + + DBG("create sysfs for fbs\n"); + r = omapfb_create_sysfs(fbdev); + if (r) { + dev_err(fbdev->dev, "failed to create sysfs entries\n"); + return r; + } + + /* Enable fb0 */ + if (fbdev->num_fbs > 0) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); + + if (ofbi->num_overlays > 0) { + struct omap_overlay *ovl = ofbi->overlays[0]; + + r = omapfb_overlay_enable(ovl, 1); + + if (r) { + dev_err(fbdev->dev, + "failed to enable overlay\n"); + return r; + } + } + } + + DBG("create_framebuffers done\n"); + + return 0; +} + +int omapfb_mode_to_timings(const char *mode_str, + struct omap_video_timings *timings, u8 *bpp) +{ + struct fb_info fbi; + struct fb_var_screeninfo var; + struct fb_ops fbops; + int r; + +#ifdef CONFIG_OMAP2_DSS_VENC + if (strcmp(mode_str, "pal") == 0) { + *timings = omap_dss_pal_timings; + *bpp = 0; + return 0; + } else if (strcmp(mode_str, "ntsc") == 0) { + *timings = omap_dss_ntsc_timings; + *bpp = 0; + return 0; + } +#endif + + /* this is quite a hack, but I wanted to use the modedb and for + * that we need fb_info and var, so we create dummy ones */ + + memset(&fbi, 0, sizeof(fbi)); + memset(&var, 0, sizeof(var)); + memset(&fbops, 0, sizeof(fbops)); + fbi.fbops = &fbops; + + r = fb_find_mode(&var, &fbi, mode_str, NULL, 0, NULL, 24); + + if (r != 0) { + timings->pixel_clock = PICOS2KHZ(var.pixclock); + timings->hfp = var.left_margin; + timings->hbp = var.right_margin; + timings->vfp = var.upper_margin; + timings->vbp = var.lower_margin; + timings->hsw = var.hsync_len; + timings->vsw = var.vsync_len; + timings->x_res = var.xres; + timings->y_res = var.yres; + + switch (var.bits_per_pixel) { + case 16: + *bpp = 16; + break; + case 24: + case 32: + default: + *bpp = 24; + break; + } + + return 0; + } else { + return -EINVAL; + } +} + +static int omapfb_set_def_mode(struct omap_dss_device *display, char *mode_str) +{ + int r; + u8 bpp; + struct omap_video_timings timings; + + r = omapfb_mode_to_timings(mode_str, &timings, &bpp); + if (r) + return r; + + display->panel.recommended_bpp = bpp; + + if (!display->check_timings || !display->set_timings) + return -EINVAL; + + r = display->check_timings(display, &timings); + if (r) + return r; + + display->set_timings(display, &timings); + + return 0; +} + +static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) +{ + char *str, *options, *this_opt; + int r = 0; + + str = kmalloc(strlen(def_mode) + 1, GFP_KERNEL); + strcpy(str, def_mode); + options = str; + + while (!r && (this_opt = strsep(&options, ",")) != NULL) { + char *p, *display_str, *mode_str; + struct omap_dss_device *display; + int i; + + p = strchr(this_opt, ':'); + if (!p) { + r = -EINVAL; + break; + } + + *p = 0; + display_str = this_opt; + mode_str = p + 1; + + display = NULL; + for (i = 0; i < fbdev->num_displays; ++i) { + if (strcmp(fbdev->displays[i]->name, + display_str) == 0) { + display = fbdev->displays[i]; + break; + } + } + + if (!display) { + r = -EINVAL; + break; + } + + r = omapfb_set_def_mode(display, mode_str); + if (r) + break; + } + + kfree(str); + + return r; +} + +static int omapfb_probe(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = NULL; + int r = 0; + int i; + struct omap_overlay *ovl; + struct omap_dss_device *def_display; + struct omap_dss_device *dssdev; + + DBG("omapfb_probe\n"); + + if (pdev->num_resources != 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + r = -ENODEV; + goto err0; + } + + fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL); + if (fbdev == NULL) { + r = -ENOMEM; + goto err0; + } + + mutex_init(&fbdev->mtx); + + fbdev->dev = &pdev->dev; + platform_set_drvdata(pdev, fbdev); + + fbdev->num_displays = 0; + dssdev = NULL; + for_each_dss_dev(dssdev) { + omap_dss_get_device(dssdev); + fbdev->displays[fbdev->num_displays++] = dssdev; + } + + if (fbdev->num_displays == 0) { + dev_err(&pdev->dev, "no displays\n"); + r = -EINVAL; + goto cleanup; + } + + fbdev->num_overlays = omap_dss_get_num_overlays(); + for (i = 0; i < fbdev->num_overlays; i++) + fbdev->overlays[i] = omap_dss_get_overlay(i); + + fbdev->num_managers = omap_dss_get_num_overlay_managers(); + for (i = 0; i < fbdev->num_managers; i++) + fbdev->managers[i] = omap_dss_get_overlay_manager(i); + + + /* gfx overlay should be the default one. find a display + * connected to that, and use it as default display */ + ovl = omap_dss_get_overlay(0); + if (ovl->manager && ovl->manager->device) { + def_display = ovl->manager->device; + } else { + dev_err(&pdev->dev, "cannot find default display\n"); + r = -EINVAL; + goto cleanup; + } + + if (def_mode && strlen(def_mode) > 0) { + if (omapfb_parse_def_modes(fbdev)) + dev_err(&pdev->dev, "cannot parse default modes\n"); + } + + r = omapfb_create_framebuffers(fbdev); + if (r) + goto cleanup; + + for (i = 0; i < fbdev->num_managers; i++) { + struct omap_overlay_manager *mgr; + mgr = fbdev->managers[i]; + r = mgr->apply(mgr); + if (r) { + dev_err(fbdev->dev, "failed to apply dispc config\n"); + goto cleanup; + } + } + + DBG("mgr->apply'ed\n"); + + r = def_display->enable(def_display); + if (r) { + dev_err(fbdev->dev, "Failed to enable display '%s'\n", + def_display->name); + goto cleanup; + } + + /* set the update mode */ + if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + if (def_display->enable_te) + def_display->enable_te(def_display, 1); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); +#else + if (def_display->enable_te) + def_display->enable_te(def_display, 0); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_MANUAL); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + } + + for (i = 0; i < fbdev->num_displays; i++) { + struct omap_dss_device *display = fbdev->displays[i]; + u16 w, h; + + if (!display->get_update_mode || !display->update) + continue; + + if (display->get_update_mode(display) == + OMAP_DSS_UPDATE_MANUAL) { + + display->get_resolution(display, &w, &h); + display->update(display, 0, 0, w, h); + } + } + + DBG("display->updated\n"); + + return 0; + +cleanup: + omapfb_free_resources(fbdev); +err0: + dev_err(&pdev->dev, "failed to setup omapfb\n"); + return r; +} + +static int omapfb_remove(struct platform_device *pdev) +{ + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); + + /* FIXME: wait till completion of pending events */ + + omapfb_remove_sysfs(fbdev); + + omapfb_free_resources(fbdev); + + return 0; +} + +static struct platform_driver omapfb_driver = { + .probe = omapfb_probe, + .remove = omapfb_remove, + .driver = { + .name = "omapfb", + .owner = THIS_MODULE, + }, +}; + +static int __init omapfb_init(void) +{ + DBG("omapfb_init\n"); + + if (platform_driver_register(&omapfb_driver)) { + printk(KERN_ERR "failed to register omapfb driver\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit omapfb_exit(void) +{ + DBG("omapfb_exit\n"); + platform_driver_unregister(&omapfb_driver); +} + +module_param_named(mode, def_mode, charp, 0); +module_param_named(vram, def_vram, charp, 0); +module_param_named(rotate, def_rotate, int, 0); +module_param_named(vrfb, def_vrfb, bool, 0); +module_param_named(mirror, def_mirror, bool, 0); + +/* late_initcall to let panel/ctrl drivers loaded first. + * I guess better option would be a more dynamic approach, + * so that omapfb reacts to new panels when they are loaded */ +late_initcall(omapfb_init); +/*module_init(omapfb_init);*/ +module_exit(omapfb_exit); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_DESCRIPTION("OMAP2/3 Framebuffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c new file mode 100644 index 000000000000..23cb986052bd --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -0,0 +1,506 @@ +/* + * linux/drivers/video/omap2/omapfb-sysfs.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/fb.h> +#include <linux/sysfs.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/omapfb.h> + +#include <mach/display.h> +#include <mach/vrfb.h> + +#include "omapfb.h" + +static ssize_t show_rotate_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type); +} + +static ssize_t store_rotate_type(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + enum omap_dss_rotation_type rot_type; + int r; + + rot_type = simple_strtoul(buf, NULL, 0); + + if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB) + return -EINVAL; + + lock_fb_info(fbi); + + r = 0; + if (rot_type == ofbi->rotation_type) + goto out; + + if (ofbi->region.size) { + r = -EBUSY; + goto out; + } + + ofbi->rotation_type = rot_type; + + /* + * Since the VRAM for this FB is not allocated at the moment we don't + * need to do any further parameter checking at this point. + */ +out: + unlock_fb_info(fbi); + + return r ? r : count; +} + + +static ssize_t show_mirror(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror); +} + +static ssize_t store_mirror(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + bool mirror; + int r; + struct fb_var_screeninfo new_var; + + mirror = simple_strtoul(buf, NULL, 0); + + if (mirror != 0 && mirror != 1) + return -EINVAL; + + lock_fb_info(fbi); + + ofbi->mirror = mirror; + + memcpy(&new_var, &fbi->var, sizeof(new_var)); + r = check_fb_var(fbi, &new_var); + if (r) + goto out; + memcpy(&fbi->var, &new_var, sizeof(fbi->var)); + + set_fb_fix(fbi); + + r = omapfb_apply_changes(fbi, 0); + if (r) + goto out; + + r = count; +out: + unlock_fb_info(fbi); + + return r; +} + +static ssize_t show_overlays(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + ssize_t l = 0; + int t; + + omapfb_lock(fbdev); + lock_fb_info(fbi); + + for (t = 0; t < ofbi->num_overlays; t++) { + struct omap_overlay *ovl = ofbi->overlays[t]; + int ovlnum; + + for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum) + if (ovl == fbdev->overlays[ovlnum]) + break; + + l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", + t == 0 ? "" : ",", ovlnum); + } + + l += snprintf(buf + l, PAGE_SIZE - l, "\n"); + + unlock_fb_info(fbi); + omapfb_unlock(fbdev); + + return l; +} + +static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev, + struct omap_overlay *ovl) +{ + int i, t; + + for (i = 0; i < fbdev->num_fbs; i++) { + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + + for (t = 0; t < ofbi->num_overlays; t++) { + if (ofbi->overlays[t] == ovl) + return ofbi; + } + } + + return NULL; +} + +static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB]; + struct omap_overlay *ovl; + int num_ovls, r, i; + int len; + bool added = false; + + num_ovls = 0; + + len = strlen(buf); + if (buf[len - 1] == '\n') + len = len - 1; + + omapfb_lock(fbdev); + lock_fb_info(fbi); + + if (len > 0) { + char *p = (char *)buf; + int ovlnum; + + while (p < buf + len) { + int found; + if (num_ovls == OMAPFB_MAX_OVL_PER_FB) { + r = -EINVAL; + goto out; + } + + ovlnum = simple_strtoul(p, &p, 0); + if (ovlnum > fbdev->num_overlays) { + r = -EINVAL; + goto out; + } + + found = 0; + for (i = 0; i < num_ovls; ++i) { + if (ovls[i] == fbdev->overlays[ovlnum]) { + found = 1; + break; + } + } + + if (!found) + ovls[num_ovls++] = fbdev->overlays[ovlnum]; + + p++; + } + } + + for (i = 0; i < num_ovls; ++i) { + struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]); + if (ofbi2 && ofbi2 != ofbi) { + dev_err(fbdev->dev, "overlay already in use\n"); + r = -EINVAL; + goto out; + } + } + + /* detach unused overlays */ + for (i = 0; i < ofbi->num_overlays; ++i) { + int t, found; + + ovl = ofbi->overlays[i]; + + found = 0; + + for (t = 0; t < num_ovls; ++t) { + if (ovl == ovls[t]) { + found = 1; + break; + } + } + + if (found) + continue; + + DBG("detaching %d\n", ofbi->overlays[i]->id); + + omapfb_overlay_enable(ovl, 0); + + if (ovl->manager) + ovl->manager->apply(ovl->manager); + + for (t = i + 1; t < ofbi->num_overlays; t++) { + ofbi->rotation[t-1] = ofbi->rotation[t]; + ofbi->overlays[t-1] = ofbi->overlays[t]; + } + + ofbi->num_overlays--; + i--; + } + + for (i = 0; i < num_ovls; ++i) { + int t, found; + + ovl = ovls[i]; + + found = 0; + + for (t = 0; t < ofbi->num_overlays; ++t) { + if (ovl == ofbi->overlays[t]) { + found = 1; + break; + } + } + + if (found) + continue; + ofbi->rotation[ofbi->num_overlays] = 0; + ofbi->overlays[ofbi->num_overlays++] = ovl; + + added = true; + } + + if (added) { + r = omapfb_apply_changes(fbi, 0); + if (r) + goto out; + } + + r = count; +out: + unlock_fb_info(fbi); + omapfb_unlock(fbdev); + + return r; +} + +static ssize_t show_overlays_rotate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + ssize_t l = 0; + int t; + + lock_fb_info(fbi); + + for (t = 0; t < ofbi->num_overlays; t++) { + l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", + t == 0 ? "" : ",", ofbi->rotation[t]); + } + + l += snprintf(buf + l, PAGE_SIZE - l, "\n"); + + unlock_fb_info(fbi); + + return l; +} + +static ssize_t store_overlays_rotate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + int num_ovls = 0, r, i; + int len; + bool changed = false; + u8 rotation[OMAPFB_MAX_OVL_PER_FB]; + + len = strlen(buf); + if (buf[len - 1] == '\n') + len = len - 1; + + lock_fb_info(fbi); + + if (len > 0) { + char *p = (char *)buf; + + while (p < buf + len) { + int rot; + + if (num_ovls == ofbi->num_overlays) { + r = -EINVAL; + goto out; + } + + rot = simple_strtoul(p, &p, 0); + if (rot < 0 || rot > 3) { + r = -EINVAL; + goto out; + } + + if (ofbi->rotation[num_ovls] != rot) + changed = true; + + rotation[num_ovls++] = rot; + + p++; + } + } + + if (num_ovls != ofbi->num_overlays) { + r = -EINVAL; + goto out; + } + + if (changed) { + for (i = 0; i < num_ovls; ++i) + ofbi->rotation[i] = rotation[i]; + + r = omapfb_apply_changes(fbi, 0); + if (r) + goto out; + + /* FIXME error handling? */ + } + + r = count; +out: + unlock_fb_info(fbi); + + return r; +} + +static ssize_t show_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size); +} + +static ssize_t store_size(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + unsigned long size; + int r; + int i; + + size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0)); + + lock_fb_info(fbi); + + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->info.enabled) { + r = -EBUSY; + goto out; + } + } + + if (size != ofbi->region.size) { + r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type); + if (r) { + dev_err(dev, "realloc fbmem failed\n"); + goto out; + } + } + + r = count; +out: + unlock_fb_info(fbi); + + return r; +} + +static ssize_t show_phys(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr); +} + +static ssize_t show_virt(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct omapfb_info *ofbi = FB2OFB(fbi); + + return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr); +} + +static struct device_attribute omapfb_attrs[] = { + __ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type, store_rotate_type), + __ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror), + __ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size), + __ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays), + __ATTR(overlays_rotate, S_IRUGO | S_IWUSR, + show_overlays_rotate, store_overlays_rotate), + __ATTR(phys_addr, S_IRUGO, show_phys, NULL), + __ATTR(virt_addr, S_IRUGO, show_virt, NULL), +}; + +int omapfb_create_sysfs(struct omapfb2_device *fbdev) +{ + int i; + int r; + + DBG("create sysfs for fbs\n"); + for (i = 0; i < fbdev->num_fbs; i++) { + int t; + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) { + r = device_create_file(fbdev->fbs[i]->dev, + &omapfb_attrs[t]); + + if (r) { + dev_err(fbdev->dev, "failed to create sysfs " + "file\n"); + return r; + } + } + } + + return 0; +} + +void omapfb_remove_sysfs(struct omapfb2_device *fbdev) +{ + int i, t; + + DBG("remove sysfs for fbs\n"); + for (i = 0; i < fbdev->num_fbs; i++) { + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) + device_remove_file(fbdev->fbs[i]->dev, + &omapfb_attrs[t]); + } +} + diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h new file mode 100644 index 000000000000..83be0a7d9be0 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -0,0 +1,153 @@ +/* + * linux/drivers/video/omap2/omapfb.h + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ +#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ + +#ifdef CONFIG_FB_OMAP2_DEBUG_SUPPORT +#define DEBUG +#endif + +#include <mach/display.h> + +#ifdef DEBUG +extern unsigned int omapfb_debug; +#define DBG(format, ...) \ + if (omapfb_debug) \ + printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define FB2OFB(fb_info) ((struct omapfb_info *)(fb_info->par)) + +/* max number of overlays to which a framebuffer data can be direct */ +#define OMAPFB_MAX_OVL_PER_FB 3 + +struct omapfb2_mem_region { + u32 paddr; + void __iomem *vaddr; + struct vrfb vrfb; + unsigned long size; + u8 type; /* OMAPFB_PLANE_MEM_* */ + bool alloc; /* allocated by the driver */ + bool map; /* kernel mapped by the driver */ +}; + +/* appended to fb_info */ +struct omapfb_info { + int id; + struct omapfb2_mem_region region; + atomic_t map_count; + int num_overlays; + struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB]; + struct omapfb2_device *fbdev; + enum omap_dss_rotation_type rotation_type; + u8 rotation[OMAPFB_MAX_OVL_PER_FB]; + bool mirror; +}; + +struct omapfb2_device { + struct device *dev; + struct mutex mtx; + + u32 pseudo_palette[17]; + + int state; + + unsigned num_fbs; + struct fb_info *fbs[10]; + + unsigned num_displays; + struct omap_dss_device *displays[10]; + unsigned num_overlays; + struct omap_overlay *overlays[10]; + unsigned num_managers; + struct omap_overlay_manager *managers[10]; +}; + +struct omapfb_colormode { + enum omap_color_mode dssmode; + u32 bits_per_pixel; + u32 nonstd; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +u32 omapfb_get_region_paddr(struct omapfb_info *ofbi); +void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi); + +void set_fb_fix(struct fb_info *fbi); +int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var); +int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type); +int omapfb_apply_changes(struct fb_info *fbi, int init); +int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi); + +int omapfb_create_sysfs(struct omapfb2_device *fbdev); +void omapfb_remove_sysfs(struct omapfb2_device *fbdev); + +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg); + +int omapfb_mode_to_timings(const char *mode_str, + struct omap_video_timings *timings, u8 *bpp); + +int dss_mode_to_fb_mode(enum omap_color_mode dssmode, + struct fb_var_screeninfo *var); + +/* find the display connected to this fb, if any */ +static inline struct omap_dss_device *fb2display(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + int i; + + /* XXX: returns the display connected to first attached overlay */ + for (i = 0; i < ofbi->num_overlays; i++) { + if (ofbi->overlays[i]->manager) + return ofbi->overlays[i]->manager->device; + } + + return NULL; +} + +static inline void omapfb_lock(struct omapfb2_device *fbdev) +{ + mutex_lock(&fbdev->mtx); +} + +static inline void omapfb_unlock(struct omapfb2_device *fbdev) +{ + mutex_unlock(&fbdev->mtx); +} + +static inline int omapfb_overlay_enable(struct omap_overlay *ovl, + int enable) +{ + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + info.enabled = enable; + return ovl->set_overlay_info(ovl, &info); +} + +#endif |