From f83e721d45493504195d54a977459d76e1d94224 Mon Sep 17 00:00:00 2001 From: RMS NOOR HPD Date: Fri, 14 May 2010 01:24:01 +0700 Subject: [PATCH] Full support for Ginger Console --- Documentation/arm/OMAP/DSS | 317 ++ arch/arm/configs/am3517_evm_defconfig | 1864 ++++++++++ arch/arm/configs/am3517_pm_defconfig | 1077 ++++++ arch/arm/configs/devkit8000_defconfig | 1826 ++++++++++ arch/arm/configs/ginger_defconfig | 1807 ++++++++++ arch/arm/mach-omap2/board-3430sdp-camera.c | 754 ++++ arch/arm/mach-omap2/board-am3517evm.c | 833 +++++ arch/arm/mach-omap2/board-ginger.c | 717 ++++ arch/arm/mach-omap2/board-ldp-camera.c | 210 ++ arch/arm/mach-omap2/board-omap35x-pmic.h | 227 ++ arch/arm/mach-omap2/board-omap3evm-camera.c | 515 +++ arch/arm/mach-omap2/board-omap3evm-camera.h | 43 + arch/arm/mach-omap2/board-zoom2-camera.c | 391 +++ arch/arm/mach-omap2/mmc-am3517evm.c | 265 ++ arch/arm/mach-omap2/mmc-am3517evm.h | 22 + arch/arm/mach-omap2/sleep3517.S | 147 + arch/arm/plat-omap/include/plat/am3517.h | 61 + arch/arm/plat-omap/include/plat/display.h | 575 +++ arch/arm/plat-omap/include/plat/isp_user.h | 688 ++++ arch/arm/plat-omap/include/plat/vram.h | 62 + arch/arm/plat-omap/include/plat/vrfb.h | 50 + arch/arm/plat-omap/psp-version.c | 87 + arch/arm/plat-omap/psp-version.h | 40 + drivers/input/touchscreen/tsc2004.c | 525 +++ drivers/media/video/dw9710.c | 609 ++++ drivers/media/video/imx046.c | 1829 ++++++++++ drivers/media/video/imx046_regs.h | 314 ++ drivers/media/video/isp/Kconfig | 18 + drivers/media/video/isp/Makefile | 15 + drivers/media/video/isp/bluegamma_table.h | 1040 ++++++ drivers/media/video/isp/cfa_coef_table.h | 602 ++++ drivers/media/video/isp/greengamma_table.h | 1040 ++++++ drivers/media/video/isp/isp.c | 2823 +++++++++++++++ drivers/media/video/isp/isp.h | 545 +++ drivers/media/video/isp/isp_af.c | 513 +++ drivers/media/video/isp/isp_af.h | 134 + drivers/media/video/isp/ispccdc.c | 1821 ++++++++++ drivers/media/video/isp/ispccdc.h | 286 ++ drivers/media/video/isp/ispcsi2.c | 2276 ++++++++++++ drivers/media/video/isp/ispcsi2.h | 240 ++ drivers/media/video/isp/isph3a.c | 618 ++++ drivers/media/video/isp/isph3a.h | 154 + drivers/media/video/isp/isphist.c | 577 +++ drivers/media/video/isp/isphist.h | 163 + drivers/media/video/isp/isppreview.c | 1963 +++++++++++ drivers/media/video/isp/isppreview.h | 426 +++ drivers/media/video/isp/ispreg.h | 1702 +++++++++ drivers/media/video/isp/ispresizer.c | 924 +++++ drivers/media/video/isp/ispresizer.h | 190 + drivers/media/video/isp/ispstat.c | 258 ++ drivers/media/video/isp/ispstat.h | 65 + drivers/media/video/isp/luma_enhance_table.h | 144 + drivers/media/video/isp/noise_filter_table.h | 79 + drivers/media/video/isp/omap_previewer.c | 1251 +++++++ drivers/media/video/isp/omap_previewer.h | 171 + drivers/media/video/isp/omap_resizer.c | 1886 ++++++++++ drivers/media/video/isp/redgamma_table.h | 1040 ++++++ drivers/media/video/lv8093.c | 604 ++++ drivers/media/video/lv8093_regs.h | 76 + drivers/media/video/mt9p012.c | 1888 ++++++++++ drivers/media/video/mt9p012_regs.h | 60 + drivers/media/video/omap34xxcam.c | 2286 ++++++++++++ drivers/media/video/omap34xxcam.h | 190 + drivers/media/video/ov3640.c | 2212 ++++++++++++ drivers/media/video/ov3640_regs.h | 582 +++ drivers/media/video/ti-media/Kconfig | 89 + drivers/media/video/ti-media/Makefile | 20 + drivers/media/video/ti-media/ccdc_hw_device.h | 114 + drivers/media/video/ti-media/dm355_ccdc.c | 978 ++++++ drivers/media/video/ti-media/dm355_ccdc_regs.h | 310 ++ drivers/media/video/ti-media/dm644x_ccdc.c | 978 ++++++ drivers/media/video/ti-media/dm644x_ccdc_regs.h | 153 + drivers/media/video/ti-media/omap_vout.c | 2625 ++++++++++++++ drivers/media/video/ti-media/omap_voutdef.h | 148 + drivers/media/video/ti-media/omap_voutlib.c | 319 ++ drivers/media/video/ti-media/omap_voutlib.h | 34 + drivers/media/video/ti-media/vpfe_capture.c | 2482 +++++++++++++ drivers/media/video/ti-media/vpif.c | 296 ++ drivers/media/video/ti-media/vpif.h | 642 ++++ drivers/media/video/ti-media/vpif_capture.c | 2168 ++++++++++++ drivers/media/video/ti-media/vpif_capture.h | 165 + drivers/media/video/ti-media/vpif_display.c | 1656 +++++++++ drivers/media/video/ti-media/vpif_display.h | 175 + drivers/media/video/ti-media/vpss.c | 301 ++ drivers/media/video/tps61059.c | 390 ++ drivers/media/video/tvp514x-int.c | 1589 +++++++++ drivers/net/can/ti_hecc.c | 1038 ++++++ drivers/usb/musb/am3517.c | 968 +++++ drivers/usb/musb/cppi41.c | 839 +++++ drivers/usb/musb/cppi41.h | 726 ++++ drivers/usb/musb/cppi41_dma.c | 1314 +++++++ drivers/usb/musb/cppi41_dma.h | 63 + drivers/usb/musb/musb_procfs.c | 840 +++++ drivers/video/omap/omapfb.h | 227 ++ drivers/video/omap2/Kconfig | 9 + drivers/video/omap2/Makefile | 6 + drivers/video/omap2/displays/Kconfig | 40 + drivers/video/omap2/displays/Makefile | 6 + drivers/video/omap2/displays/panel-generic.c | 104 + drivers/video/omap2/displays/panel-ginger.c | 116 + .../omap2/displays/panel-samsung-lte430wq-f0c.c | 113 + .../video/omap2/displays/panel-sharp-lq043t1dg01.c | 120 + .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 153 + drivers/video/omap2/displays/panel-taal.c | 1003 ++++++ drivers/video/omap2/dss/Kconfig | 104 + drivers/video/omap2/dss/Makefile | 6 + drivers/video/omap2/dss/core.c | 919 +++++ drivers/video/omap2/dss/dispc.c | 3087 ++++++++++++++++ drivers/video/omap2/dss/display.c | 671 ++++ drivers/video/omap2/dss/dpi.c | 399 +++ drivers/video/omap2/dss/dsi.c | 3708 ++++++++++++++++++++ drivers/video/omap2/dss/dss.c | 596 ++++ drivers/video/omap2/dss/dss.h | 370 ++ drivers/video/omap2/dss/manager.c | 1487 ++++++++ drivers/video/omap2/dss/overlay.c | 680 ++++ drivers/video/omap2/dss/rfbi.c | 1309 +++++++ drivers/video/omap2/dss/sdi.c | 277 ++ drivers/video/omap2/dss/venc.c | 797 +++++ drivers/video/omap2/omapfb/Kconfig | 37 + drivers/video/omap2/omapfb/Makefile | 2 + drivers/video/omap2/omapfb/omapfb-ioctl.c | 755 ++++ drivers/video/omap2/omapfb/omapfb-main.c | 2261 ++++++++++++ drivers/video/omap2/omapfb/omapfb-sysfs.c | 507 +++ drivers/video/omap2/omapfb/omapfb.h | 146 + drivers/video/omap2/vram.c | 655 ++++ drivers/video/omap2/vrfb.c | 315 ++ include/linux/can/platform/ti_hecc.h | 40 + include/linux/davinci_emac.h | 39 + include/linux/i2c/tsc2004.h | 17 + include/linux/omap_resizer.h | 137 + include/linux/omapfb.h | 251 ++ include/media/dw9710.h | 35 + include/media/imx046.h | 44 + include/media/lv8093.h | 30 + include/media/mt9p012.h | 36 + include/media/ov3640.h | 29 + include/media/ti-media/ccdc_types.h | 43 + include/media/ti-media/dm355_ccdc.h | 321 ++ include/media/ti-media/dm644x_ccdc.h | 184 + include/media/ti-media/vpfe_capture.h | 218 ++ include/media/ti-media/vpfe_types.h | 51 + include/media/ti-media/vpss.h | 69 + include/media/tps61059.h | 35 + include/media/tvp514x-int.h | 118 + sound/soc/omap/am3517evm.c | 202 ++ sound/soc/omap/omap3beagle.c | 2 +- 146 files changed, 91022 insertions(+), 1 deletion(-) create mode 100644 Documentation/arm/OMAP/DSS create mode 100644 arch/arm/configs/am3517_evm_defconfig create mode 100644 arch/arm/configs/am3517_pm_defconfig create mode 100644 arch/arm/configs/devkit8000_defconfig create mode 100644 arch/arm/configs/ginger_defconfig create mode 100644 arch/arm/mach-omap2/board-3430sdp-camera.c create mode 100644 arch/arm/mach-omap2/board-am3517evm.c create mode 100644 arch/arm/mach-omap2/board-ginger.c create mode 100644 arch/arm/mach-omap2/board-ldp-camera.c create mode 100644 arch/arm/mach-omap2/board-omap35x-pmic.h create mode 100644 arch/arm/mach-omap2/board-omap3evm-camera.c create mode 100644 arch/arm/mach-omap2/board-omap3evm-camera.h create mode 100644 arch/arm/mach-omap2/board-zoom2-camera.c create mode 100644 arch/arm/mach-omap2/mmc-am3517evm.c create mode 100644 arch/arm/mach-omap2/mmc-am3517evm.h create mode 100644 arch/arm/mach-omap2/sleep3517.S create mode 100644 arch/arm/plat-omap/include/plat/am3517.h create mode 100644 arch/arm/plat-omap/include/plat/display.h create mode 100644 arch/arm/plat-omap/include/plat/isp_user.h create mode 100644 arch/arm/plat-omap/include/plat/vram.h create mode 100644 arch/arm/plat-omap/include/plat/vrfb.h create mode 100644 arch/arm/plat-omap/psp-version.c create mode 100644 arch/arm/plat-omap/psp-version.h create mode 100644 drivers/input/touchscreen/tsc2004.c create mode 100644 drivers/media/video/dw9710.c create mode 100644 drivers/media/video/imx046.c create mode 100644 drivers/media/video/imx046_regs.h create mode 100644 drivers/media/video/isp/Kconfig create mode 100644 drivers/media/video/isp/Makefile create mode 100644 drivers/media/video/isp/bluegamma_table.h create mode 100644 drivers/media/video/isp/cfa_coef_table.h create mode 100644 drivers/media/video/isp/greengamma_table.h create mode 100644 drivers/media/video/isp/isp.c create mode 100644 drivers/media/video/isp/isp.h create mode 100644 drivers/media/video/isp/isp_af.c create mode 100644 drivers/media/video/isp/isp_af.h create mode 100644 drivers/media/video/isp/ispccdc.c create mode 100644 drivers/media/video/isp/ispccdc.h create mode 100644 drivers/media/video/isp/ispcsi2.c create mode 100644 drivers/media/video/isp/ispcsi2.h create mode 100644 drivers/media/video/isp/isph3a.c create mode 100644 drivers/media/video/isp/isph3a.h create mode 100644 drivers/media/video/isp/isphist.c create mode 100644 drivers/media/video/isp/isphist.h create mode 100644 drivers/media/video/isp/isppreview.c create mode 100644 drivers/media/video/isp/isppreview.h create mode 100644 drivers/media/video/isp/ispreg.h create mode 100644 drivers/media/video/isp/ispresizer.c create mode 100644 drivers/media/video/isp/ispresizer.h create mode 100644 drivers/media/video/isp/ispstat.c create mode 100644 drivers/media/video/isp/ispstat.h create mode 100644 drivers/media/video/isp/luma_enhance_table.h create mode 100644 drivers/media/video/isp/noise_filter_table.h create mode 100644 drivers/media/video/isp/omap_previewer.c create mode 100644 drivers/media/video/isp/omap_previewer.h create mode 100644 drivers/media/video/isp/omap_resizer.c create mode 100644 drivers/media/video/isp/redgamma_table.h create mode 100644 drivers/media/video/lv8093.c create mode 100644 drivers/media/video/lv8093_regs.h create mode 100644 drivers/media/video/mt9p012.c create mode 100644 drivers/media/video/mt9p012_regs.h create mode 100644 drivers/media/video/omap34xxcam.c create mode 100644 drivers/media/video/omap34xxcam.h create mode 100644 drivers/media/video/ov3640.c create mode 100644 drivers/media/video/ov3640_regs.h create mode 100644 drivers/media/video/ti-media/Kconfig create mode 100644 drivers/media/video/ti-media/Makefile create mode 100644 drivers/media/video/ti-media/ccdc_hw_device.h create mode 100644 drivers/media/video/ti-media/dm355_ccdc.c create mode 100644 drivers/media/video/ti-media/dm355_ccdc_regs.h create mode 100644 drivers/media/video/ti-media/dm644x_ccdc.c create mode 100644 drivers/media/video/ti-media/dm644x_ccdc_regs.h create mode 100644 drivers/media/video/ti-media/omap_vout.c create mode 100644 drivers/media/video/ti-media/omap_voutdef.h create mode 100644 drivers/media/video/ti-media/omap_voutlib.c create mode 100644 drivers/media/video/ti-media/omap_voutlib.h create mode 100644 drivers/media/video/ti-media/vpfe_capture.c create mode 100644 drivers/media/video/ti-media/vpif.c create mode 100644 drivers/media/video/ti-media/vpif.h create mode 100644 drivers/media/video/ti-media/vpif_capture.c create mode 100644 drivers/media/video/ti-media/vpif_capture.h create mode 100644 drivers/media/video/ti-media/vpif_display.c create mode 100644 drivers/media/video/ti-media/vpif_display.h create mode 100644 drivers/media/video/ti-media/vpss.c create mode 100644 drivers/media/video/tps61059.c create mode 100644 drivers/media/video/tvp514x-int.c create mode 100644 drivers/net/can/ti_hecc.c create mode 100644 drivers/usb/musb/am3517.c create mode 100644 drivers/usb/musb/cppi41.c create mode 100644 drivers/usb/musb/cppi41.h create mode 100644 drivers/usb/musb/cppi41_dma.c create mode 100644 drivers/usb/musb/cppi41_dma.h create mode 100644 drivers/usb/musb/musb_procfs.c create mode 100644 drivers/video/omap/omapfb.h create mode 100644 drivers/video/omap2/Kconfig create mode 100644 drivers/video/omap2/Makefile create mode 100644 drivers/video/omap2/displays/Kconfig create mode 100644 drivers/video/omap2/displays/Makefile create mode 100644 drivers/video/omap2/displays/panel-generic.c create mode 100644 drivers/video/omap2/displays/panel-ginger.c create mode 100644 drivers/video/omap2/displays/panel-samsung-lte430wq-f0c.c create mode 100644 drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c create mode 100644 drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c create mode 100644 drivers/video/omap2/displays/panel-taal.c create mode 100644 drivers/video/omap2/dss/Kconfig create mode 100644 drivers/video/omap2/dss/Makefile create mode 100644 drivers/video/omap2/dss/core.c create mode 100644 drivers/video/omap2/dss/dispc.c create mode 100644 drivers/video/omap2/dss/display.c create mode 100644 drivers/video/omap2/dss/dpi.c create mode 100644 drivers/video/omap2/dss/dsi.c create mode 100644 drivers/video/omap2/dss/dss.c create mode 100644 drivers/video/omap2/dss/dss.h create mode 100644 drivers/video/omap2/dss/manager.c create mode 100644 drivers/video/omap2/dss/overlay.c create mode 100644 drivers/video/omap2/dss/rfbi.c create mode 100644 drivers/video/omap2/dss/sdi.c create mode 100644 drivers/video/omap2/dss/venc.c create mode 100644 drivers/video/omap2/omapfb/Kconfig create mode 100644 drivers/video/omap2/omapfb/Makefile create mode 100644 drivers/video/omap2/omapfb/omapfb-ioctl.c create mode 100644 drivers/video/omap2/omapfb/omapfb-main.c create mode 100644 drivers/video/omap2/omapfb/omapfb-sysfs.c create mode 100644 drivers/video/omap2/omapfb/omapfb.h create mode 100644 drivers/video/omap2/vram.c create mode 100644 drivers/video/omap2/vrfb.c create mode 100644 include/linux/can/platform/ti_hecc.h create mode 100644 include/linux/davinci_emac.h create mode 100644 include/linux/i2c/tsc2004.h create mode 100644 include/linux/omap_resizer.h create mode 100644 include/linux/omapfb.h create mode 100644 include/media/dw9710.h create mode 100644 include/media/imx046.h create mode 100644 include/media/lv8093.h create mode 100644 include/media/mt9p012.h create mode 100644 include/media/ov3640.h create mode 100644 include/media/ti-media/ccdc_types.h create mode 100644 include/media/ti-media/dm355_ccdc.h create mode 100644 include/media/ti-media/dm644x_ccdc.h create mode 100644 include/media/ti-media/vpfe_capture.h create mode 100644 include/media/ti-media/vpfe_types.h create mode 100644 include/media/ti-media/vpss.h create mode 100644 include/media/tps61059.h create mode 100644 include/media/tvp514x-int.h create mode 100644 sound/soc/omap/am3517evm.c diff --git a/Documentation/arm/OMAP/DSS b/Documentation/arm/OMAP/DSS new file mode 100644 index 00000000000..0af0e9eed5d --- /dev/null +++ b/Documentation/arm/OMAP/DSS @@ -0,0 +1,317 @@ +OMAP2/3 Display Subsystem +------------------------- + +This is an almost total rewrite of the OMAP FB driver in drivers/video/omap +(let's call it DSS1). The main differences between DSS1 and DSS2 are DSI, +TV-out and multiple display support, but there are lots of small improvements +also. + +The DSS2 driver (omapdss module) is in arch/arm/plat-omap/dss/, and the FB, +panel and controller drivers are in drivers/video/omap2/. DSS1 and DSS2 live +currently side by side, you can choose which one to use. + +Features +-------- + +Working and tested features include: + +- MIPI DPI (parallel) output +- MIPI DSI output in command mode +- MIPI DBI (RFBI) output +- SDI output +- TV output +- All pieces can be compiled as a module or inside kernel +- Use DISPC to update any of the outputs +- Use CPU to update RFBI or DSI output +- OMAP DISPC planes +- RGB16, RGB24 packed, RGB24 unpacked +- YUV2, UYVY +- Scaling +- Adjusting DSS FCK to find a good pixel clock +- Use DSI DPLL to create DSS FCK + +Tested boards include: +- OMAP3 SDP board +- Beagle board +- N810 + +omapdss driver +-------------- + +The DSS driver does not itself have any support for Linux framebuffer, V4L or +such like the current ones, but it has an internal kernel API that upper level +drivers can use. + +The DSS driver models OMAP's overlays, overlay managers and displays in a +flexible way to enable non-common multi-display configuration. In addition to +modelling the hardware overlays, omapdss supports virtual overlays and overlay +managers. These can be used when updating a display with CPU or system DMA. + +Panel and controller drivers +---------------------------- + +The drivers implement panel or controller specific functionality and are not +usually visible to users except through omapfb driver. They register +themselves to the DSS driver. + +omapfb driver +------------- + +The omapfb driver implements arbitrary number of standard linux framebuffers. +These framebuffers can be routed flexibly to any overlays, thus allowing very +dynamic display architecture. + +The driver exports some omapfb specific ioctls, which are compatible with the +ioctls in the old driver. + +The rest of the non standard features are exported via sysfs. Whether the final +implementation will use sysfs, or ioctls, is still open. + +V4L2 drivers +------------ + +V4L2 is being implemented in TI. + +From omapdss point of view the V4L2 drivers should be similar to framebuffer +driver. + +Architecture +-------------------- + +Some clarification what the different components do: + + - Framebuffer is a memory area inside OMAP's SRAM/SDRAM that contains the + pixel data for the image. Framebuffer has width and height and color + depth. + - Overlay defines where the pixels are read from and where they go on the + screen. The overlay may be smaller than framebuffer, thus displaying only + part of the framebuffer. The position of the overlay may be changed if + the overlay is smaller than the display. + - Overlay manager combines the overlays in to one image and feeds them to + display. + - Display is the actual physical display device. + +A framebuffer can be connected to multiple overlays to show the same pixel data +on all of the overlays. Note that in this case the overlay input sizes must be +the same, but, in case of video overlays, the output size can be different. Any +framebuffer can be connected to any overlay. + +An overlay can be connected to one overlay manager. Also DISPC overlays can be +connected only to DISPC overlay managers, and virtual overlays can be only +connected to virtual overlays. + +An overlay manager can be connected to one display. There are certain +restrictions which kinds of displays an overlay manager can be connected: + + - DISPC TV overlay manager can be only connected to TV display. + - Virtual overlay managers can only be connected to DBI or DSI displays. + - DISPC LCD overlay manager can be connected to all displays, except TV + display. + +Sysfs +----- +The sysfs interface is mainly used for testing. I don't think sysfs +interface is the best for this in the final version, but I don't quite know +what would be the best interfaces for these things. + +The sysfs interface is divided to two parts: DSS and FB. + +/sys/class/graphics/fb? directory: +mirror 0=off, 1=on +rotate Rotation 0-3 for 0, 90, 180, 270 degrees +rotate_type 0 = DMA rotation, 1 = VRFB rotation +overlays List of overlay numbers to which framebuffer pixels go +phys_addr Physical address of the framebuffer +virt_addr Virtual address of the framebuffer +size Size of the framebuffer + +/sys/devices/platform/omapdss/overlay? directory: +enabled 0=off, 1=on +input_size width,height (ie. the framebuffer size) +manager Destination overlay manager name +name +output_size width,height +position x,y +screen_width width +global_alpha global alpha 0-255 0=transparent 255=opaque + +/sys/devices/platform/omapdss/manager? directory: +display Destination display +name +alpha_blending_enabled 0=off, 1=on +trans_key_enabled 0=off, 1=on +trans_key_type gfx-destination, video-source +trans_key_value transparency color key (RGB24) +default_color default background color (RGB24) + +/sys/devices/platform/omapdss/display? directory: +ctrl_name Controller name +mirror 0=off, 1=on +update_mode 0=off, 1=auto, 2=manual +enabled 0=off, 1=on +name +rotate Rotation 0-3 for 0, 90, 180, 270 degrees +timings Display timings (pixclock,xres/hfp/hbp/hsw,yres/vfp/vbp/vsw) + When writing, two special timings are accepted for tv-out: + "pal" and "ntsc" +panel_name +tear_elim Tearing elimination 0=off, 1=on + +There are also some debugfs files at /omapdss/ which show information +about clocks and registers. + +Examples +-------- + +The following definitions have been made for the examples below: + +ovl0=/sys/devices/platform/omapdss/overlay0 +ovl1=/sys/devices/platform/omapdss/overlay1 +ovl2=/sys/devices/platform/omapdss/overlay2 + +mgr0=/sys/devices/platform/omapdss/manager0 +mgr1=/sys/devices/platform/omapdss/manager1 + +lcd=/sys/devices/platform/omapdss/display0 +dvi=/sys/devices/platform/omapdss/display1 +tv=/sys/devices/platform/omapdss/display2 + +fb0=/sys/class/graphics/fb0 +fb1=/sys/class/graphics/fb1 +fb2=/sys/class/graphics/fb2 + +Default setup on OMAP3 SDP +-------------------------- + +Here's the default setup on OMAP3 SDP board. All planes go to LCD. DVI +and TV-out are not in use. The columns from left to right are: +framebuffers, overlays, overlay managers, displays. Framebuffers are +handled by omapfb, and the rest by the DSS. + +FB0 --- GFX -\ DVI +FB1 --- VID1 --+- LCD ---- LCD +FB2 --- VID2 -/ TV ----- TV + +Example: Switch from LCD to DVI +---------------------- + +w=`cat $dvi/timings | cut -d "," -f 2 | cut -d "/" -f 1` +h=`cat $dvi/timings | cut -d "," -f 3 | cut -d "/" -f 1` + +echo "0" > $lcd/enabled +echo "" > $mgr0/display +fbset -fb /dev/fb0 -xres $w -yres $h -vxres $w -vyres $h +# at this point you have to switch the dvi/lcd dip-switch from the omap board +echo "dvi" > $mgr0/display +echo "1" > $dvi/enabled + +After this the configuration looks like: + +FB0 --- GFX -\ -- DVI +FB1 --- VID1 --+- LCD -/ LCD +FB2 --- VID2 -/ TV ----- TV + +Example: Clone GFX overlay to LCD and TV +------------------------------- + +w=`cat $tv/timings | cut -d "," -f 2 | cut -d "/" -f 1` +h=`cat $tv/timings | cut -d "," -f 3 | cut -d "/" -f 1` + +echo "0" > $ovl0/enabled +echo "0" > $ovl1/enabled + +echo "" > $fb1/overlays +echo "0,1" > $fb0/overlays + +echo "$w,$h" > $ovl1/output_size +echo "tv" > $ovl1/manager + +echo "1" > $ovl0/enabled +echo "1" > $ovl1/enabled + +echo "1" > $tv/enabled + +After this the configuration looks like (only relevant parts shown): + +FB0 +-- GFX ---- LCD ---- LCD + \- VID1 ---- TV ---- TV + +Misc notes +---------- + +OMAP FB allocates the framebuffer memory using the OMAP VRAM allocator. + +Using DSI DPLL to generate pixel clock it is possible produce the pixel clock +of 86.5MHz (max possible), and with that you get 1280x1024@57 output from DVI. + +Rotation and mirroring currently only supports RGB565 and RGB8888 modes. VRFB +does not support mirroring. + +VRFB rotation requires much more memory than non-rotated framebuffer, so you +probably need to increase your vram setting before using VRFB rotation. Also, +many applications may not work with VRFB if they do not pay attention to all +framebuffer parameters. + +Kernel boot arguments +--------------------- + +vram= + - Amount of total VRAM to preallocate. For example, "10M". omapfb + allocates memory for framebuffers from VRAM. + +omapfb.mode=:[,...] + - Default video mode for specified displays. For example, + "dvi:800x400MR-24@60". See drivers/video/modedb.c. + There are also two special modes: "pal" and "ntsc" that + can be used to tv out. + +omapfb.vram=:[@][,...] + - VRAM allocated for a framebuffer. Normally omapfb allocates vram + depending on the display size. With this you can manually allocate + more or define the physical address of each framebuffer. For example, + "1:4M" to allocate 4M for fb1. + +omapfb.debug= + - Enable debug printing. You have to have OMAPFB debug support enabled + in kernel config. + +omapfb.test= + - Draw test pattern to framebuffer whenever framebuffer settings change. + You need to have OMAPFB debug support enabled in kernel config. + +omapfb.vrfb= + - Use VRFB rotation for all framebuffers. + +omapfb.rotate= + - Default rotation applied to all framebuffers. + 0 - 0 degree rotation + 1 - 90 degree rotation + 2 - 180 degree rotation + 3 - 270 degree rotation + +omapfb.mirror= + - Default mirror for all framebuffers. Only works with DMA rotation. + +omapdss.def_disp= + - Name of default display, to which all overlays will be connected. + Common examples are "lcd" or "tv". + +omapdss.debug= + - Enable debug printing. You have to have DSS debug support enabled in + kernel config. + +TODO +---- + +DSS locking + +Error checking +- Lots of checks are missing or implemented just as BUG() + +System DMA update for DSI +- Can be used for RGB16 and RGB24P modes. Probably not for RGB24U (how + to skip the empty byte?) + +OMAP1 support +- Not sure if needed + diff --git a/arch/arm/configs/am3517_evm_defconfig b/arch/arm/configs/am3517_evm_defconfig new file mode 100644 index 00000000000..d2a7728558c --- /dev/null +++ b/arch/arm/configs/am3517_evm_defconfig @@ -0,0 +1,1864 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.32-rc5 +# Fri Nov 27 16:43:13 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_GROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_USER_SCHED=y +# CONFIG_CGROUP_SCHED is not set +# CONFIG_CGROUPS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_BCMRING is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y +# CONFIG_ARCH_OMAP4 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_DEBUG_POWERDOMAIN is not set +# CONFIG_OMAP_DEBUG_CLOCKDOMAIN is not set +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_MCBSP=y +# CONFIG_OMAP_MBOX_FWK is not set +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +# CONFIG_OMAP3_DEBOBS is not set +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_DM_TIMER=y +# CONFIG_OMAP_LL_DEBUG_UART1 is not set +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +CONFIG_OMAP_LL_DEBUG_UART3=y +# CONFIG_OMAP_PM_NONE is not set +CONFIG_OMAP_PM_NOOP=y +# CONFIG_OMAP_PM_SRF is not set +CONFIG_ARCH_OMAP34XX=y +CONFIG_ARCH_OMAP3430=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP3_BEAGLE is not set +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3EVM is not set +CONFIG_MACH_OMAP3517EVM=y +CONFIG_PMIC_TPS65023=y +# CONFIG_MACH_OMAP3_PANDORA is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_ZOOM2 is not set +# CONFIG_MACH_CM_T35 is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_HAS_TLS_REG=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +CONFIG_COMMON_CLKDEV=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=128 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_HAVE_MLOCK=y +CONFIG_HAVE_MLOCKED_PAGE_BIT=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/nfs nfsroot=192.168.0.1:/home/user/buildroot ip=192.168.0.2:192.168.0.1:192.168.0.1:255.255.255.0:tgt:eth0:off rw console=ttyS2,115200n8" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +CONFIG_CAN=y +CONFIG_CAN_RAW=y +CONFIG_CAN_BCM=y + +# +# CAN Device Drivers +# +CONFIG_CAN_VCAN=y +CONFIG_CAN_DEV=y +CONFIG_CAN_CALC_BITTIMING=y +# CONFIG_CAN_SJA1000 is not set +# CONFIG_CAN_EMS_USB is not set +CONFIG_CAN_TI_HECC=y +CONFIG_CAN_DEBUG_DEVICES=y +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +# CONFIG_CFG80211 is not set +CONFIG_CFG80211_DEFAULT_PS_VALUE=0 +# CONFIG_WIRELESS_OLD_REGULATORY is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +# CONFIG_MTD_NAND_OMAP_PREFETCH is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=32768 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +CONFIG_TI_DAVINCI_EMAC=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_AX8817X is not set +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +CONFIG_TOUCHSCREEN_TSC2004=y +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_ALLOW_V4L1=y +CONFIG_VIDEO_V4L1_COMPAT=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5761=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEO_V4L1=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_SG=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders/decoders and other helper chips +# + +# +# Audio decoders +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TDA9875 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_MT9P012 is not set +# CONFIG_VIDEO_DW9710 is not set +# CONFIG_VIDEO_OV3640 is not set +# CONFIG_VIDEO_IMX046 is not set +# CONFIG_VIDEO_LV8093 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7191 is not set +CONFIG_VIDEO_TVP514X=y +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_ADV7343 is not set + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_VIVI is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_VIDEO_OMAP3 is not set +# CONFIG_VIDEO_OMAP34XX_ISP_PREVIEWER is not set +# CONFIG_VIDEO_OMAP34XX_ISP_RESIZER is not set +CONFIG_TI_MEDIA=y +CONFIG_VIDEO_VPSS_SYSTEM=y +CONFIG_VIDEO_VPFE_CAPTURE=y +CONFIG_VIDEO_DM6446_CCDC=y +CONFIG_VIDEO_OMAP3_OUT=y +# CONFIG_SOC_CAMERA is not set +CONFIG_V4L_USB_DRIVERS=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_GSPCA is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_CX231XX is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_USB_VICAM is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_QUICKCAM_MESSENGER is not set +# CONFIG_USB_ET61X251 is not set +# CONFIG_VIDEO_OVCAMCHIP is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_STV680 is not set +# CONFIG_USB_ZC0301 is not set +# CONFIG_USB_PWC is not set +# CONFIG_USB_PWC_INPUT_EVDEV is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +CONFIG_OMAP2_VRAM=y +CONFIG_OMAP2_VRFB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_VRAM_SIZE=4 +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y +# CONFIG_OMAP2_DSS_RFBI is not set +CONFIG_OMAP2_DSS_VENC=y +CONFIG_OMAP2_VENC_OUT_TYPE_SVIDEO=y +# CONFIG_OMAP2_VENC_OUT_TYPE_COMPOSITE is not set +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP2_DSS_DSI is not set +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=4 +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +# CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE is not set +CONFIG_FB_OMAP2_NUM_FBS=1 + +# +# OMAP2/3 Display Device Drivers +# +CONFIG_PANEL_GENERIC=y +# CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C is not set +# CONFIG_PANEL_SHARP_LS037V7DW01 is not set +CONFIG_PANEL_SHARP_LQ043T1DG01=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=y +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_MCBSP=y +CONFIG_SND_OMAP_SOC_AM3517EVM=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_TLV320AIC23=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_ZEROPLUS is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_EHCI_TT_NEWSCHED=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SOC=y + +# +# OMAP 343x high speed USB support +# +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_PERIPHERAL is not set +CONFIG_USB_MUSB_OTG=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_MUSB_HDRC_HCD=y +# CONFIG_MUSB_PIO_ONLY is not set +# CONFIG_USB_TI_CPPI_DMA is not set +CONFIG_USB_TI_CPPI41_DMA=y +CONFIG_USB_MUSB_DEBUG=y + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +CONFIG_USB_TEST=y +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_S3C_HSOTG is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_CI13XXX is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +CONFIG_USB_ETH=y +CONFIG_USB_ETH_RNDIS=y +# CONFIG_USB_ETH_EEM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_ISP1301_OMAP is not set +CONFIG_NOP_USB_XCEIV=y +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_OMAP is not set +CONFIG_MMC_OMAP_HS=y +# CONFIG_MMC_AT91 is not set +# CONFIG_MMC_ATMELMCI is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +CONFIG_RTC_DRV_S35390A=y +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# CBUS support +# +# CONFIG_CBUS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/am3517_pm_defconfig b/arch/arm/configs/am3517_pm_defconfig new file mode 100644 index 00000000000..931c3738d94 --- /dev/null +++ b/arch/arm/configs/am3517_pm_defconfig @@ -0,0 +1,1077 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.32-rc5 +# Mon Nov 9 13:16:59 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_GROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_USER_SCHED=y +# CONFIG_CGROUP_SCHED is not set +# CONFIG_CGROUPS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_BCMRING is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y +# CONFIG_ARCH_OMAP4 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_DEBUG_POWERDOMAIN is not set +# CONFIG_OMAP_DEBUG_CLOCKDOMAIN is not set +CONFIG_OMAP_RESET_CLOCKS=y +# CONFIG_OMAP_MUX is not set +CONFIG_OMAP_MCBSP=y +# CONFIG_OMAP_MBOX_FWK is not set +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +# CONFIG_OMAP3_DEBOBS is not set +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_DM_TIMER=y +# CONFIG_OMAP_LL_DEBUG_UART1 is not set +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +CONFIG_OMAP_LL_DEBUG_UART3=y +# CONFIG_OMAP_PM_NONE is not set +CONFIG_OMAP_PM_NOOP=y +# CONFIG_OMAP_PM_SRF is not set +CONFIG_ARCH_OMAP34XX=y +CONFIG_ARCH_OMAP3430=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP3_BEAGLE is not set +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3EVM is not set +CONFIG_MACH_OMAP3517EVM=y +# CONFIG_MACH_OMAP3_PANDORA is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_ZOOM2 is not set +# CONFIG_MACH_CM_T35 is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_HAS_TLS_REG=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +CONFIG_COMMON_CLKDEV=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=128 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_HAVE_MLOCK=y +CONFIG_HAVE_MLOCKED_PAGE_BIT=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/nfs nfsroot=192.168.0.1:/home/user/buildroot ip=192.168.0.2:192.168.0.1:192.168.0.1:255.255.255.0:tgt:eth0:off rw console=ttyS2,115200n8" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_VERBOSE is not set +CONFIG_CAN_PM_TRACE=y +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +# CONFIG_CFG80211 is not set +CONFIG_CFG80211_DEFAULT_PS_VALUE=0 +# CONFIG_WIRELESS_OLD_REGULATORY is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +# CONFIG_NETDEVICES is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_I2C is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_OMAP2_DSS is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# CBUS support +# +# CONFIG_CBUS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/devkit8000_defconfig b/arch/arm/configs/devkit8000_defconfig new file mode 100644 index 00000000000..4b02d75e6f6 --- /dev/null +++ b/arch/arm/configs/devkit8000_defconfig @@ -0,0 +1,1826 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.33-rc8 +# Thu Mar 18 07:42:46 2010 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_GROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_USER_SCHED=y +# CONFIG_CGROUP_SCHED is not set +# CONFIG_CGROUPS is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_U8500 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +# CONFIG_ARCH_OMAP1 is not set +CONFIG_ARCH_OMAP2PLUS=y +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y +# CONFIG_ARCH_OMAP4 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +# CONFIG_OMAP_MUX is not set +CONFIG_OMAP_MCBSP=y +# CONFIG_OMAP_MBOX_FWK is not set +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +# CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set +CONFIG_OMAP_DM_TIMER=y +# CONFIG_OMAP_PM_NONE is not set +CONFIG_OMAP_PM_NOOP=y +CONFIG_ARCH_OMAP3430=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP3_BEAGLE is not set +CONFIG_MACH_DEVKIT8000=y +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3EVM is not set +# CONFIG_MACH_OMAP3517EVM is not set +# CONFIG_MACH_OMAP3_PANDORA is not set +# CONFIG_MACH_OMAP3_TOUCHBOOK is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_ZOOM2 is not set +# CONFIG_MACH_OMAP_ZOOM3 is not set +# CONFIG_MACH_CM_T35 is not set +# CONFIG_MACH_IGEP0020 is not set +# CONFIG_MACH_OMAP_3630SDP is not set +# CONFIG_OMAP3_EMU is not set +# CONFIG_OMAP3_SDRC_AC_TIMING is not set + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_HAS_TLS_REG=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +CONFIG_COMMON_CLKDEV=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS2,115200n8 root=/dev/nfs nfsroot=192.168.1.1:home/nfsroot/current,home/nfsroot/current ip=dhcp rw noinitrd root delay=3" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_WIRELESS is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_RAM=y +CONFIG_MTD_ROM=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_OMAP_PREFETCH=y +# CONFIG_MTD_NAND_OMAP_PREFETCH_DMA is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=40960 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +CONFIG_DM9000=y +CONFIG_DM9000_DEBUGLEVEL=4 +CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL=y +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +CONFIG_KEYBOARD_MATRIX=y +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +CONFIG_KEYBOARD_TWL4030=y +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_RAW=y +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +CONFIG_RAW_DRIVER=y +CONFIG_MAX_RAW_DEVS=256 +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_OMAP24XX=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +CONFIG_GPIO_TWL4030=y +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +CONFIG_TWL4030_CORE=y +CONFIG_TWL4030_POWER=y +CONFIG_TWL4030_CODEC=y +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13783 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_88PM8607 is not set +# CONFIG_AB4500_CORE is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8660 is not set +CONFIG_REGULATOR_TWL4030=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +CONFIG_FB_FOREIGN_ENDIAN=y +CONFIG_FB_BOTH_ENDIAN=y +# CONFIG_FB_BIG_ENDIAN is not set +# CONFIG_FB_LITTLE_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_OMAP_BOOTLOADER_INIT=y +CONFIG_OMAP2_VRAM=y +CONFIG_OMAP2_VRFB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_VRAM_SIZE=0 +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y +# CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS is not set +# CONFIG_OMAP2_DSS_RFBI is not set +CONFIG_OMAP2_DSS_VENC=y +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP2_DSS_DSI is not set +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0 +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +# CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE is not set +CONFIG_FB_OMAP2_NUM_FBS=3 + +# +# OMAP2/3 Display Device Drivers +# +CONFIG_PANEL_GENERIC=y +# CONFIG_PANEL_SHARP_LS037V7DW01 is not set +CONFIG_PANEL_GINGER_LCD=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +CONFIG_DISPLAY_SUPPORT=y + +# +# Display hardware drivers +# + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_MCBSP=y +CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_TWL4030=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +CONFIG_HIDRAW=y + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +CONFIG_USB_HIDDEV=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_KYE is not set +CONFIG_HID_GYRATION=y +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +# CONFIG_HID_NTRIG is not set +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_ZEROPLUS is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SOC=y + +# +# OMAP 343x high speed USB support +# +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_PERIPHERAL is not set +CONFIG_USB_MUSB_OTG=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_MUSB_HDRC_HCD=y +# CONFIG_MUSB_PIO_ONLY is not set +CONFIG_USB_INVENTRA_DMA=y +# CONFIG_USB_TI_CPPI_DMA is not set +# CONFIG_USB_MUSB_DEBUG is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_S3C_HSOTG is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_CI13XXX is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_ETH_EEM=y +CONFIG_USB_GADGETFS=m +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set +CONFIG_USB_G_PRINTER=m +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_ISP1301_OMAP is not set +# CONFIG_USB_ULPI is not set +CONFIG_TWL4030_USB=y +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=m +# CONFIG_MMC_OMAP is not set +CONFIG_MMC_OMAP_HS=y +# CONFIG_MMC_AT91 is not set +# CONFIG_MMC_ATMELMCI is not set +CONFIG_MMC_SPI=m +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +CONFIG_RTC_DRV_TWL4030=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# CBUS support +# +# CONFIG_CBUS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=m +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/ginger_defconfig b/arch/arm/configs/ginger_defconfig new file mode 100644 index 00000000000..8ad8534e244 --- /dev/null +++ b/arch/arm/configs/ginger_defconfig @@ -0,0 +1,1807 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.32-rc5 +# Thu May 13 10:57:19 2010 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="-ginger1" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_GROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_USER_SCHED=y +# CONFIG_CGROUP_SCHED is not set +# CONFIG_CGROUPS is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_BCMRING is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y +# CONFIG_ARCH_OMAP4 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_DEBUG_POWERDOMAIN is not set +# CONFIG_OMAP_DEBUG_CLOCKDOMAIN is not set +# CONFIG_OMAP_SMARTREFLEX is not set +# CONFIG_OMAP_RESET_CLOCKS is not set +# CONFIG_OMAP_MUX is not set +CONFIG_OMAP_MCBSP=y +# CONFIG_OMAP_MBOX_FWK is not set +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +CONFIG_OMAP_DM_TIMER=y +# CONFIG_OMAP_LL_DEBUG_UART1 is not set +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +CONFIG_OMAP_LL_DEBUG_UART3=y +# CONFIG_OMAP_PM_NONE is not set +CONFIG_OMAP_PM_NOOP=y +# CONFIG_OMAP_PM_SRF is not set +CONFIG_ARCH_OMAP34XX=y +CONFIG_ARCH_OMAP3430=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP3_BEAGLE is not set +# CONFIG_MACH_DEVKIT8000 is not set +CONFIG_MACH_GINGER=y +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3EVM is not set +# CONFIG_MACH_OMAP3517EVM is not set +# CONFIG_MACH_OMAP3_PANDORA is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_ZOOM2 is not set +# CONFIG_MACH_CM_T35 is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_HAS_TLS_REG=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +CONFIG_COMMON_CLKDEV=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_HAVE_MLOCK=y +CONFIG_HAVE_MLOCKED_PAGE_BIT=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS2,115200n8 root=/dev/nfs nfsroot=192.168.1.1:home/nfsroot/current,home/nfsroot/current ip=dhcp rw noinitrd root delay=3" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_WIRELESS is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_RAM=y +CONFIG_MTD_ROM=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_OMAP_PREFETCH=y +# CONFIG_MTD_NAND_OMAP_PREFETCH_DMA is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=40960 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +# CONFIG_TI_DAVINCI_EMAC is not set +CONFIG_DM9000=y +CONFIG_DM9000_DEBUGLEVEL=4 +CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL=y +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +CONFIG_KEYBOARD_MATRIX=y +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +CONFIG_KEYBOARD_TWL4030=y +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_TSC2004 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_RAW=y +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +CONFIG_RAW_DRIVER=y +CONFIG_MAX_RAW_DEVS=256 +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_OMAP24XX=y + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +CONFIG_GPIO_TWL4030=y + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +CONFIG_TWL4030_CORE=y +CONFIG_TWL4030_POWER=y +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13783 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_EZX_PCAP is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +CONFIG_REGULATOR_TWL4030=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +CONFIG_FB_FOREIGN_ENDIAN=y +CONFIG_FB_BOTH_ENDIAN=y +# CONFIG_FB_BIG_ENDIAN is not set +# CONFIG_FB_LITTLE_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_OMAP_BOOTLOADER_INIT=y +CONFIG_OMAP2_VRAM=y +CONFIG_OMAP2_VRFB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_VRAM_SIZE=0 +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y +# CONFIG_OMAP2_DSS_RFBI is not set +CONFIG_OMAP2_DSS_VENC=y +CONFIG_OMAP2_VENC_OUT_TYPE_SVIDEO=y +# CONFIG_OMAP2_VENC_OUT_TYPE_COMPOSITE is not set +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP2_DSS_DSI is not set +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0 +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +# CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE is not set +CONFIG_FB_OMAP2_NUM_FBS=3 + +# +# OMAP2/3 Display Device Drivers +# +CONFIG_PANEL_GENERIC=y +# CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C is not set +# CONFIG_PANEL_SHARP_LS037V7DW01 is not set +# CONFIG_PANEL_SHARP_LQ043T1DG01 is not set +CONFIG_PANEL_GINGER=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +CONFIG_DISPLAY_SUPPORT=y + +# +# Display hardware drivers +# + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_MCBSP=y +CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE=y +CONFIG_SND_SOC_I2C_AND_SPI=y +CONFIG_SND_SOC_ALL_CODECS=y +CONFIG_SND_SOC_WM_HUBS=y +CONFIG_SND_SOC_AD1836=y +CONFIG_SND_SOC_AD1938=y +CONFIG_SND_SOC_AD73311=y +CONFIG_SND_SOC_AK4104=y +CONFIG_SND_SOC_AK4535=y +CONFIG_SND_SOC_AK4642=y +CONFIG_SND_SOC_CS4270=y +CONFIG_SND_SOC_L3=y +CONFIG_SND_SOC_PCM3008=y +CONFIG_SND_SOC_SPDIF=y +CONFIG_SND_SOC_SSM2602=y +CONFIG_SND_SOC_TLV320AIC23=y +CONFIG_SND_SOC_TLV320AIC26=y +CONFIG_SND_SOC_TLV320AIC3X=y +CONFIG_SND_SOC_TWL4030=y +CONFIG_SND_SOC_UDA134X=y +CONFIG_SND_SOC_UDA1380=y +CONFIG_SND_SOC_WM8510=y +CONFIG_SND_SOC_WM8523=y +CONFIG_SND_SOC_WM8580=y +CONFIG_SND_SOC_WM8728=y +CONFIG_SND_SOC_WM8731=y +CONFIG_SND_SOC_WM8750=y +CONFIG_SND_SOC_WM8753=y +CONFIG_SND_SOC_WM8776=y +CONFIG_SND_SOC_WM8900=y +CONFIG_SND_SOC_WM8903=y +CONFIG_SND_SOC_WM8940=y +CONFIG_SND_SOC_WM8960=y +CONFIG_SND_SOC_WM8961=y +CONFIG_SND_SOC_WM8971=y +CONFIG_SND_SOC_WM8974=y +CONFIG_SND_SOC_WM8988=y +CONFIG_SND_SOC_WM8990=y +CONFIG_SND_SOC_WM8993=y +CONFIG_SND_SOC_WM9081=y +CONFIG_SND_SOC_MAX9877=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +CONFIG_HIDRAW=y + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +CONFIG_USB_HIDDEV=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_KYE is not set +CONFIG_HID_GYRATION=y +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +# CONFIG_HID_NTRIG is not set +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_ZEROPLUS is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SOC=y + +# +# OMAP 343x high speed USB support +# +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_PERIPHERAL is not set +CONFIG_USB_MUSB_OTG=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_MUSB_HDRC_HCD=y +# CONFIG_MUSB_PIO_ONLY is not set +CONFIG_USB_INVENTRA_DMA=y +CONFIG_MUSB_USE_SYSTEM_DMA_RX=y +# CONFIG_USB_TI_CPPI_DMA is not set +# CONFIG_USB_TI_CPPI41_DMA is not set +# CONFIG_USB_MUSB_DEBUG is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_S3C_HSOTG is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_CI13XXX is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_ETH_EEM=y +CONFIG_USB_GADGETFS=m +# CONFIG_USB_FILE_STORAGE is not set +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set +CONFIG_USB_G_PRINTER=m +# CONFIG_USB_CDC_COMPOSITE is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_ISP1301_OMAP is not set +CONFIG_TWL4030_USB=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=m +# CONFIG_MMC_OMAP is not set +CONFIG_MMC_OMAP_HS=y +# CONFIG_MMC_AT91 is not set +# CONFIG_MMC_ATMELMCI is not set +CONFIG_MMC_SPI=m +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_BD2802 is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +CONFIG_RTC_DRV_TWL4030=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# CBUS support +# +# CONFIG_CBUS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=m +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/mach-omap2/board-3430sdp-camera.c b/arch/arm/mach-omap2/board-3430sdp-camera.c new file mode 100644 index 00000000000..5b768270692 --- /dev/null +++ b/arch/arm/mach-omap2/board-3430sdp-camera.c @@ -0,0 +1,754 @@ +/* + * linux/arch/arm/mach-omap2/board-3430sdp.c + * + * Copyright (C) 2007 Texas Instruments + * + * Modified from mach-omap2/board-generic.c + * + * Initial code: Syed Mohammed Khasim + * + * 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. + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#ifdef CONFIG_OMAP_PM_SRF +#include +#endif + +static int cam_inited; + +static struct device *camkit_dev; + +#include +#include <../drivers/media/video/omap34xxcam.h> +#include <../drivers/media/video/isp/ispreg.h> +#define DEBUG_BASE 0x08000000 + +#define REG_SDP3430_FPGA_GPIO_2 (0x50) +#define FPGA_SPR_GPIO1_3v3 (0x1 << 14) +#define FPGA_GPIO6_DIR_CTRL (0x1 << 6) + +#define CAMKITV3_USE_XCLKA 0 +#define CAMKITV3_USE_XCLKB 1 + +#define CAMKITV3_RESET_GPIO 98 + +/* Sensor specific GPIO signals */ +#define MT9P012_STANDBY_GPIO 58 +#define OV3640_STANDBY_GPIO 55 +#define TPS61059_TORCH_EN_GPIO 56 +#define TPS61059_FLASH_STROBE_GPIO 126 + +static struct regulator *sdp3430_mt9p012_reg; +static struct regulator *sdp3430_dw9710_reg; +static struct regulator *sdp3430_ov3640_reg; + +#if defined(CONFIG_VIDEO_MT9P012) || defined(CONFIG_VIDEO_MT9P012_MODULE) +#include +static enum v4l2_power mt9p012_previous_power = V4L2_POWER_OFF; + +#define MT9P012_BIGGEST_FRAME_BYTE_SIZE PAGE_ALIGN(2592 * 1944 * 2) + +#ifdef CONFIG_VIDEO_DW9710 +#include +#define is_dw9710_enabled() 1 +#else +#define is_dw9710_enabled() 0 +#endif /* CONFIG_VIDEO_DW9710 */ + +#if defined(CONFIG_VIDEO_TPS61059) || defined(CONFIG_VIDEO_TPS61059_MODULE) +#include +#define is_tps61059_enabled() 1 +#else +#define is_tps61059_enabled() 0 +#endif /* CONFIG_VIDEO_TPS61059 || CONFIG_VIDEO_TPS61059_MODULE */ + +#define is_mt9p012_enabled() 1 +#else /* CONFIG_VIDEO_MT9P012 || CONFIG_VIDEO_MT9P012_MODULE */ +#define is_mt9p012_enabled() 0 +#define is_dw9710_enabled() 0 +#define is_tps61059_enabled() 0 +#endif /* CONFIG_VIDEO_MT9P012 || CONFIG_VIDEO_MT9P012_MODULE */ + +#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE) +#include +#include <../drivers/media/video/isp/ispcsi2.h> +#define OV3640_CSI2_CLOCK_POLARITY 0 /* +/- pin order */ +#define OV3640_CSI2_DATA0_POLARITY 0 /* +/- pin order */ +#define OV3640_CSI2_DATA1_POLARITY 0 /* +/- pin order */ +#define OV3640_CSI2_CLOCK_LANE 1 /* Clock lane position: 1 */ +#define OV3640_CSI2_DATA0_LANE 2 /* Data0 lane position: 2 */ +#define OV3640_CSI2_DATA1_LANE 3 /* Data1 lane position: 3 */ +#define OV3640_CSI2_PHY_THS_TERM 4 +#define OV3640_CSI2_PHY_THS_SETTLE 14 +#define OV3640_CSI2_PHY_TCLK_TERM 0 +#define OV3640_CSI2_PHY_TCLK_MISS 1 +#define OV3640_CSI2_PHY_TCLK_SETTLE 14 + +#define OV3640_BIGGEST_FRAME_BYTE_SIZE PAGE_ALIGN(2048 * 1536 * 2) +#define is_ov3640_enabled() 1 +#else +#define is_ov3640_enabled() 0 +#endif /* CONFIG_VIDEO_OV3640 || CONFIG_VIDEO_OV3640_MODULE */ + +#if defined(CONFIG_VIDEO_MT9P012) || defined(CONFIG_VIDEO_MT9P012_MODULE) || \ + defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE) || \ + defined(CONFIG_VIDEO_DW9710) + +static void enable_fpga_vio_1v8(u8 enable) +{ + void __iomem *fpga_map_addr; + u16 reg_val; + + fpga_map_addr = ioremap(DEBUG_BASE, 4096); + reg_val = readw(fpga_map_addr + REG_SDP3430_FPGA_GPIO_2); + + /* Ensure that the SPR_GPIO1_3v3 is 0 - powered off.. 1 is on */ + if (reg_val & FPGA_SPR_GPIO1_3v3) { + reg_val |= FPGA_SPR_GPIO1_3v3; + reg_val |= FPGA_GPIO6_DIR_CTRL; /* output mode */ + writew(reg_val, fpga_map_addr + REG_SDP3430_FPGA_GPIO_2); + /* give a few milli sec to settle down + * Let the sensor also settle down.. if required.. + */ + if (enable) + mdelay(10); + } + + if (enable) { + reg_val |= FPGA_SPR_GPIO1_3v3 | FPGA_GPIO6_DIR_CTRL; + writew(reg_val, fpga_map_addr + REG_SDP3430_FPGA_GPIO_2); + } + iounmap(fpga_map_addr); + /* Vrise time for the voltage - should be less than 1 ms */ + mdelay(1); +} +#endif + +#if defined(CONFIG_VIDEO_MT9P012) || defined(CONFIG_VIDEO_MT9P012_MODULE) +#ifdef CONFIG_VIDEO_DW9710 +static int dw9710_lens_power_set(enum v4l2_power power) +{ + if (!cam_inited) { + printk(KERN_ERR "DW9710: Unable to control board GPIOs!\n"); + return -EFAULT; + } + + /* The power change depends on MT9P012 powerup GPIO, so if we request a + * power state different from sensor, we should return error + */ + if ((mt9p012_previous_power != V4L2_POWER_OFF) && + (power != mt9p012_previous_power)) + return -EIO; + /* + * Plug regulator consumer to respective VAUX supply + * if not done before. + */ + if (!sdp3430_dw9710_reg) { + sdp3430_dw9710_reg = regulator_get(camkit_dev, "vaux2_2"); + if (IS_ERR(sdp3430_dw9710_reg)) { + dev_err(camkit_dev, "vaux2_2 regulator missing\n"); + return PTR_ERR(sdp3430_dw9710_reg); + } + } + + if (!sdp3430_dw9710_reg) { + sdp3430_dw9710_reg = regulator_get(camkit_dev, "vaux2_2"); + if (IS_ERR(sdp3430_dw9710_reg)) { + dev_err(camkit_dev, "vaux2_2 regulator missing\n"); + return PTR_ERR(sdp3430_dw9710_reg); + } + } + + switch (power) { + case V4L2_POWER_OFF: + /* Power Down Sequence */ + if (regulator_is_enabled(sdp3430_dw9710_reg)) + regulator_disable(sdp3430_dw9710_reg); + enable_fpga_vio_1v8(0); + break; + case V4L2_POWER_ON: + /* STANDBY_GPIO is active HIGH for set LOW to release */ + gpio_set_value(MT9P012_STANDBY_GPIO, 1); + + /* nRESET is active LOW. set HIGH to release reset */ + gpio_set_value(CAMKITV3_RESET_GPIO, 1); + + /* turn on digital power */ + enable_fpga_vio_1v8(1); + + /* turn on analog power */ + regulator_enable(sdp3430_dw9710_reg); + + /* out of standby */ + gpio_set_value(MT9P012_STANDBY_GPIO, 0); + udelay(1000); + + /* have to put sensor to reset to guarantee detection */ + gpio_set_value(CAMKITV3_RESET_GPIO, 0); + + udelay(1500); + + /* nRESET is active LOW. set HIGH to release reset */ + gpio_set_value(CAMKITV3_RESET_GPIO, 1); + /* give sensor sometime to get out of the reset. + * Datasheet says 2400 xclks. At 6 MHz, 400 usec is + * enough + */ + udelay(300); + break; + case V4L2_POWER_STANDBY: + break; + } + return 0; +} + +static int dw9710_lens_set_prv_data(void *priv) +{ + struct omap34xxcam_hw_config *hwc = priv; + + hwc->dev_index = 0; + hwc->dev_minor = 0; + hwc->dev_type = OMAP34XXCAM_SLAVE_LENS; + + return 0; +} + +struct dw9710_platform_data sdp3430_dw9710_platform_data = { + .power_set = dw9710_lens_power_set, + .priv_data_set = dw9710_lens_set_prv_data, +}; +#endif + +#if defined(CONFIG_VIDEO_TPS61059) || defined(CONFIG_VIDEO_TPS61059_MODULE) +static void tps61059_flash_on(void) +{ + gpio_set_value(TPS61059_TORCH_EN_GPIO, 1); + gpio_set_value(TPS61059_FLASH_STROBE_GPIO, 1); +} + +static void tps61059_flash_off(void) +{ + gpio_set_value(TPS61059_FLASH_STROBE_GPIO, 0); + gpio_set_value(TPS61059_TORCH_EN_GPIO, 0); +} + +static void tps61059_s_torch_intensity(u32 value) +{ + if (value > 0) { + /* Torch mode, light immediately on, duration indefinite */ + gpio_set_value(TPS61059_TORCH_EN_GPIO, 1); + } else { + /* Torch mode off */ + gpio_set_value(TPS61059_TORCH_EN_GPIO, 0); + } +} + +static int tps61059_set_prv_data(void *priv) +{ + struct omap34xxcam_hw_config *hwc = priv; + + hwc->dev_index = 0; + hwc->dev_minor = 0; + hwc->dev_type = OMAP34XXCAM_SLAVE_FLASH; + + return 0; +} + +struct tps61059_platform_data sdp3430_tps61059_data = { + .flash_on = tps61059_flash_on, + .flash_off = tps61059_flash_off, + .s_torch_intensity = tps61059_s_torch_intensity, + .priv_data_set = tps61059_set_prv_data, +}; + +#endif + +static struct omap34xxcam_sensor_config cam_hwc = { + .sensor_isp = 0, + .capture_mem = MT9P012_BIGGEST_FRAME_BYTE_SIZE * 4, + .ival_default = { 1, 10 }, +}; + +static int mt9p012_sensor_set_prv_data(struct v4l2_int_device *s, void *priv) +{ + struct omap34xxcam_hw_config *hwc = priv; + + hwc->u.sensor.sensor_isp = cam_hwc.sensor_isp; + hwc->u.sensor.capture_mem = cam_hwc.capture_mem; + hwc->dev_index = 0; + hwc->dev_minor = 0; + hwc->dev_type = OMAP34XXCAM_SLAVE_SENSOR; + return 0; +} + +static struct isp_interface_config mt9p012_if_config = { + .ccdc_par_ser = ISP_PARLL, + .dataline_shift = 0x1, + .hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE, + .strobe = 0x0, + .prestrobe = 0x0, + .shutter = 0x0, + .prev_sph = 2, + .prev_slv = 0, + .wenlog = ISPCCDC_CFG_WENLOG_AND, + .wait_hs_vs = 2, + .u.par.par_bridge = 0x0, + .u.par.par_clk_pol = 0x0, +}; + +static int mt9p012_sensor_power_set(struct v4l2_int_device *s, + enum v4l2_power power) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + + if (!cam_inited) { + printk(KERN_ERR "MT9P012: Unable to control board GPIOs!\n"); + return -EFAULT; + } + + /* + * Plug regulator consumer to respective VAUX supply + * if not done before. + */ + if (!sdp3430_mt9p012_reg) { + sdp3430_mt9p012_reg = regulator_get(camkit_dev, "vaux2_1"); + if (IS_ERR(sdp3430_mt9p012_reg)) { + dev_err(camkit_dev, "vaux2_1 regulator missing\n"); + return PTR_ERR(sdp3430_mt9p012_reg); + } + } + + switch (power) { + case V4L2_POWER_OFF: + /* Power Down Sequence */ + if (regulator_is_enabled(sdp3430_mt9p012_reg)) + regulator_disable(sdp3430_mt9p012_reg); + enable_fpga_vio_1v8(0); + +#ifdef CONFIG_OMAP_PM_SRF + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 0); +#endif + break; + case V4L2_POWER_ON: + +#ifdef CONFIG_OMAP_PM_SRF + /* Through-put requirement: + * 2592 x 1944 x 2Bpp x 11fps x 3 memory ops = 324770 KByte/s + */ + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 324770); +#endif + + if (mt9p012_previous_power == V4L2_POWER_OFF) { + /* Power Up Sequence */ + isp_configure_interface(vdev->cam->isp, + &mt9p012_if_config); + + /* set to output mode */ + gpio_direction_output(MT9P012_STANDBY_GPIO, true); + /* set to output mode */ + gpio_direction_output(CAMKITV3_RESET_GPIO, true); + + /* STANDBY_GPIO is active HIGH for set LOW to release */ + gpio_set_value(MT9P012_STANDBY_GPIO, 1); + + /* nRESET is active LOW. set HIGH to release reset */ + gpio_set_value(CAMKITV3_RESET_GPIO, 1); + + /* turn on digital power */ + enable_fpga_vio_1v8(1); + + /* turn on analog power */ + regulator_enable(sdp3430_mt9p012_reg); + } + + /* out of standby */ + gpio_set_value(MT9P012_STANDBY_GPIO, 0); + udelay(1000); + + if (mt9p012_previous_power == V4L2_POWER_OFF) { + /* have to put sensor to reset to guarantee detection */ + gpio_set_value(CAMKITV3_RESET_GPIO, 0); + + udelay(1500); + + /* nRESET is active LOW. set HIGH to release reset */ + gpio_set_value(CAMKITV3_RESET_GPIO, 1); + /* give sensor sometime to get out of the reset. + * Datasheet says 2400 xclks. At 6 MHz, 400 usec is + * enough + */ + udelay(300); + } + break; + case V4L2_POWER_STANDBY: + /* stand by */ + gpio_set_value(MT9P012_STANDBY_GPIO, 1); +#ifdef CONFIG_OMAP_PM_SRF + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 0); +#endif + break; + } + /* Save powerstate to know what was before calling POWER_ON. */ + mt9p012_previous_power = power; + return 0; +} + +static u32 mt9p012_sensor_set_xclk(struct v4l2_int_device *s, u32 xclkfreq) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + + return isp_set_xclk(vdev->cam->isp, xclkfreq, CAMKITV3_USE_XCLKA); +} + +struct mt9p012_platform_data sdp3430_mt9p012_platform_data = { + .power_set = mt9p012_sensor_power_set, + .priv_data_set = mt9p012_sensor_set_prv_data, + .set_xclk = mt9p012_sensor_set_xclk, +}; + +#endif + +#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE) + +static struct omap34xxcam_sensor_config ov3640_hwc = { + .sensor_isp = 0, + .capture_mem = OV3640_BIGGEST_FRAME_BYTE_SIZE * 2, + .ival_default = { 1, 15 }, +}; + +static struct isp_interface_config ov3640_if_config = { + .ccdc_par_ser = ISP_CSIA, + .dataline_shift = 0x0, + .hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE, + .strobe = 0x0, + .prestrobe = 0x0, + .shutter = 0x0, + .prev_sph = 2, + .prev_slv = 0, + .wenlog = ISPCCDC_CFG_WENLOG_AND, + .wait_hs_vs = 2, + .u.csi.crc = 0x0, + .u.csi.mode = 0x0, + .u.csi.edge = 0x0, + .u.csi.signalling = 0x0, + .u.csi.strobe_clock_inv = 0x0, + .u.csi.vs_edge = 0x0, + .u.csi.channel = 0x1, + .u.csi.vpclk = 0x1, + .u.csi.data_start = 0x0, + .u.csi.data_size = 0x0, + .u.csi.format = V4L2_PIX_FMT_SGRBG10, +}; + +static int ov3640_sensor_set_prv_data(struct v4l2_int_device *s, void *priv) +{ + struct omap34xxcam_hw_config *hwc = priv; + + hwc->u.sensor.sensor_isp = ov3640_hwc.sensor_isp; + hwc->u.sensor.capture_mem = ov3640_hwc.capture_mem; + hwc->dev_index = 1; + hwc->dev_minor = 4; + hwc->dev_type = OMAP34XXCAM_SLAVE_SENSOR; + return 0; +} + +static int ov3640_sensor_power_set(struct v4l2_int_device *s, + enum v4l2_power power) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + struct isp_csi2_lanes_cfg lanecfg; + struct isp_csi2_phy_cfg phyconfig; + static enum v4l2_power previous_power = V4L2_POWER_OFF; + + if (!cam_inited) { + printk(KERN_ERR "OV3640: Unable to control board GPIOs!\n"); + return -EFAULT; + } + + /* + * Plug regulator consumer to respective VAUX supply + * if not done before. + */ + if (!sdp3430_ov3640_reg) { +#if defined(CONFIG_VIDEO_OV3640_CSI2) + sdp3430_ov3640_reg = regulator_get(camkit_dev, "vaux4_1"); + if (IS_ERR(sdp3430_ov3640_reg)) { + dev_err(camkit_dev, "vaux4_1 regulator missing\n"); + return PTR_ERR(sdp3430_ov3640_reg); + } +#else + sdp3430_ov3640_reg = regulator_get(camkit_dev, "vaux2_3"); + if (IS_ERR(sdp3430_ov3640_reg)) { + dev_err(camkit_dev, "vaux2_3 regulator missing\n"); + return PTR_ERR(sdp3430_ov3640_reg); + } +#endif + } + + switch (power) { + case V4L2_POWER_ON: +#ifdef CONFIG_OMAP_PM_SRF + /* Through-put requirement: + * 2048 x 1536 x 2Bpp x 7.5fps x 3 memory ops = 138240 KByte/s + */ + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 138240); +#endif + if (previous_power == V4L2_POWER_OFF) + isp_csi2_reset(); + + lanecfg.clk.pol = OV3640_CSI2_CLOCK_POLARITY; + lanecfg.clk.pos = OV3640_CSI2_CLOCK_LANE; + lanecfg.data[0].pol = OV3640_CSI2_DATA0_POLARITY; + lanecfg.data[0].pos = OV3640_CSI2_DATA0_LANE; + lanecfg.data[1].pol = OV3640_CSI2_DATA1_POLARITY; + lanecfg.data[1].pos = OV3640_CSI2_DATA1_LANE; + lanecfg.data[2].pol = 0; + lanecfg.data[2].pos = 0; + lanecfg.data[3].pol = 0; + lanecfg.data[3].pos = 0; + isp_csi2_complexio_lanes_config(&lanecfg); + isp_csi2_complexio_lanes_update(true); + + phyconfig.ths_term = OV3640_CSI2_PHY_THS_TERM; + phyconfig.ths_settle = OV3640_CSI2_PHY_THS_SETTLE; + phyconfig.tclk_term = OV3640_CSI2_PHY_TCLK_TERM; + phyconfig.tclk_miss = OV3640_CSI2_PHY_TCLK_MISS; + phyconfig.tclk_settle = OV3640_CSI2_PHY_TCLK_SETTLE; + isp_csi2_phy_config(&phyconfig); + isp_csi2_phy_update(true); + + isp_configure_interface(vdev->cam->isp, &ov3640_if_config); + + if (previous_power == V4L2_POWER_OFF) { + /* turn on analog power */ + regulator_enable(sdp3430_ov3640_reg); + udelay(100); + + /* Turn ON Omnivision sensor */ + gpio_set_value(CAMKITV3_RESET_GPIO, 1); + gpio_set_value(OV3640_STANDBY_GPIO, 0); + udelay(100); + + /* RESET Omnivision sensor */ + gpio_set_value(CAMKITV3_RESET_GPIO, 0); + udelay(100); + gpio_set_value(CAMKITV3_RESET_GPIO, 1); + + /* Wait 10 ms */ + mdelay(10); + enable_fpga_vio_1v8(1); + udelay(100); + } + break; + case V4L2_POWER_OFF: + /* Power Down Sequence */ + isp_csi2_complexio_power(ISP_CSI2_POWER_OFF); + if (regulator_is_enabled(sdp3430_ov3640_reg)) + regulator_disable(sdp3430_ov3640_reg); + enable_fpga_vio_1v8(0); +#ifdef CONFIG_OMAP_PM_SRF + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 0); +#endif + break; + case V4L2_POWER_STANDBY: +#ifdef CONFIG_OMAP_PM_SRF + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 0); +#endif + break; + } + previous_power = power; + return 0; +} + +static u32 ov3640_sensor_set_xclk(struct v4l2_int_device *s, u32 xclkfreq) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + + return isp_set_xclk(vdev->cam->isp, xclkfreq, CAMKITV3_USE_XCLKB); +} + +struct ov3640_platform_data sdp3430_ov3640_platform_data = { + .power_set = ov3640_sensor_power_set, + .priv_data_set = ov3640_sensor_set_prv_data, + .set_xclk = ov3640_sensor_set_xclk, +}; + +#endif + +static int sdp3430_camkit_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (!is_mt9p012_enabled() && !is_ov3640_enabled()) + return -ENODEV; + + /* Request and configure shared gpio pins for both cameras */ + if (is_mt9p012_enabled() || is_ov3640_enabled()) { + if (gpio_request(CAMKITV3_RESET_GPIO, + "camkitv3_reset_gpio") != 0) { + dev_err(&pdev->dev, "Could not request GPIO %d", + CAMKITV3_RESET_GPIO); + ret = -ENODEV; + goto err; + } + gpio_direction_output(CAMKITV3_RESET_GPIO, false); + } + + /* Request and configure shared gpio pins for primary camera */ + if (is_mt9p012_enabled()) { + if (gpio_request(MT9P012_STANDBY_GPIO, + "mt9p012_standby_gpio")) { + dev_err(&pdev->dev, + "Could not request GPIO %d for MT9P012\n", + MT9P012_STANDBY_GPIO); + ret = -ENODEV; + goto err_freegpio1; + } + + gpio_direction_output(MT9P012_STANDBY_GPIO, false); + + if (is_tps61059_enabled()) { + /* Configure pin MUX for GPIO 126 for TPS61059 flash */ + omap_cfg_reg(D25_34XX_GPIO126_OUT); + + if (gpio_request(TPS61059_TORCH_EN_GPIO, + "tps61059_torch_en_gpio")) { + dev_err(&pdev->dev, + "Could not request GPIO %d for" + " TPS61059\n", + TPS61059_TORCH_EN_GPIO); + ret = -ENODEV; + goto err_freegpio2; + } + + if (gpio_request(TPS61059_FLASH_STROBE_GPIO, + "tps61059_flash_strobe_gpio")) { + dev_err(&pdev->dev, + "Could not request GPIO %d for" + " TPS61059\n", + TPS61059_FLASH_STROBE_GPIO); + ret = -ENODEV; + goto err_freegpio3; + } + gpio_direction_output(TPS61059_TORCH_EN_GPIO, false); + gpio_direction_output(TPS61059_FLASH_STROBE_GPIO, + false); + } + } + + /* Request and configure shared gpio pins for secondary camera */ + if (is_ov3640_enabled()) { + if (gpio_request(OV3640_STANDBY_GPIO, + "ov3640_standby_gpio") != 0) { + dev_err(&pdev->dev, "Could not request GPIO %d", + OV3640_STANDBY_GPIO); + ret = -ENODEV; + goto err_freegpio4; + } + gpio_direction_output(OV3640_STANDBY_GPIO, false); + } + + cam_inited = 1; + camkit_dev = &pdev->dev; + return 0; + +err_freegpio4: + if (is_mt9p012_enabled() && is_tps61059_enabled()) + gpio_free(TPS61059_FLASH_STROBE_GPIO); +err_freegpio3: + if (is_mt9p012_enabled() && is_tps61059_enabled()) + gpio_free(TPS61059_TORCH_EN_GPIO); +err_freegpio2: + if (is_mt9p012_enabled()) + gpio_free(MT9P012_STANDBY_GPIO); +err_freegpio1: + if (is_mt9p012_enabled() || is_ov3640_enabled()) + gpio_free(CAMKITV3_RESET_GPIO); +err: + cam_inited = 0; + return ret; +} + +static int sdp3430_camkit_remove(struct platform_device *pdev) +{ + /* Primary camera resources */ + if (is_mt9p012_enabled()) { + /* Free Regulators */ + if (is_dw9710_enabled()) { + if (regulator_is_enabled(sdp3430_dw9710_reg)) + regulator_disable(sdp3430_dw9710_reg); + regulator_put(sdp3430_dw9710_reg); + } + + if (regulator_is_enabled(sdp3430_mt9p012_reg)) + regulator_disable(sdp3430_mt9p012_reg); + regulator_put(sdp3430_mt9p012_reg); + + /* Free GPIOs */ + if (is_tps61059_enabled()) { + gpio_free(TPS61059_FLASH_STROBE_GPIO); + gpio_free(TPS61059_TORCH_EN_GPIO); + } + gpio_free(MT9P012_STANDBY_GPIO); + } + + /* Secondary camera resources */ + if (is_ov3640_enabled()) { + /* Free Regulators */ + if (regulator_is_enabled(sdp3430_ov3640_reg)) + regulator_disable(sdp3430_ov3640_reg); + regulator_put(sdp3430_ov3640_reg); + + /* Free GPIOs */ + gpio_free(OV3640_STANDBY_GPIO); + } + + /* Shared resources to free */ + if (is_mt9p012_enabled() || is_ov3640_enabled()) + gpio_free(CAMKITV3_RESET_GPIO); + return 0; +} + +static int sdp3430_camkit_suspend(struct device *dev) +{ + return 0; +} + +static int sdp3430_camkit_resume(struct device *dev) +{ + return 0; +} + +static struct dev_pm_ops sdp3430_camkit_pm_ops = { + .suspend = sdp3430_camkit_suspend, + .resume = sdp3430_camkit_resume, +}; + +static struct platform_driver sdp3430_camkit_driver = { + .probe = sdp3430_camkit_probe, + .remove = sdp3430_camkit_remove, + .driver = { + .name = "sdp3430_camkit", + .pm = &sdp3430_camkit_pm_ops, + }, +}; + +void __init sdp3430_cam_init(void) +{ + cam_inited = 0; + platform_driver_register(&sdp3430_camkit_driver); +} + diff --git a/arch/arm/mach-omap2/board-am3517evm.c b/arch/arm/mach-omap2/board-am3517evm.c new file mode 100644 index 00000000000..d1d5ef36855 --- /dev/null +++ b/arch/arm/mach-omap2/board-am3517evm.c @@ -0,0 +1,833 @@ +/* + * linux/arch/arm/mach-omap2/board-am3517evm.c + * + * Copyright (C) 2009 Texas Instruments Incorporated + * Authot: Ranjith Lohithakshan + * + * Based on mach-omap2/board-omap3evm.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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include "mmc-am3517evm.h" +#include "board-omap35x-pmic.h" + +#define GPMC_CS0_BASE 0x60 +#define GPMC_CS_SIZE 0x30 + +#define NAND_BLOCK_SIZE SZ_128K + +static struct mtd_partition am3517evm_nand_partitions[] = { +/* All the partition sizes are listed in terms of NAND block size */ +{ + .name = "xloader-nand", + .offset = 0, + .size = 4*(SZ_128K), + .mask_flags = MTD_WRITEABLE +}, +{ + .name = "uboot-nand", + .offset = MTDPART_OFS_APPEND, + .size = 14*(SZ_128K), + .mask_flags = MTD_WRITEABLE +}, +{ + .name = "params-nand", + .offset = MTDPART_OFS_APPEND, + .size = 2*(SZ_128K) +}, +{ + .name = "linux-nand", + .offset = MTDPART_OFS_APPEND, + .size = 40*(SZ_128K) +}, +{ + .name = "jffs2-nand", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, +}, +}; + +static struct omap_nand_platform_data am3517evm_nand_data = { + .parts = am3517evm_nand_partitions, + .nr_parts = ARRAY_SIZE(am3517evm_nand_partitions), + .nand_setup = NULL, + .dma_channel = -1, /* disable DMA in OMAP NAND driver */ + .dev_ready = NULL, +}; + +static struct resource am3517evm_nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device am3517evm_nand_device = { + .name = "omap2-nand", + .id = 0, + .dev = { + .platform_data = &am3517evm_nand_data, + }, + .num_resources = 1, + .resource = &am3517evm_nand_resource, +}; + +void __init am3517evm_flash_init(void) +{ + u8 cs = 0; + u8 nandcs = GPMC_CS_NUM + 1; + u32 gpmc_base_add = OMAP34XX_GPMC_VIRT; + + while (cs < GPMC_CS_NUM) { + u32 ret = 0; + ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + + if ((ret & 0xC00) == 0x800) { + /* Found it!! */ + if (nandcs > GPMC_CS_NUM) + nandcs = cs; + } + cs++; + } + if (nandcs > GPMC_CS_NUM) { + printk(KERN_INFO "NAND: Unable to find configuration " + " in GPMC\n "); + return; + } + + if (nandcs < GPMC_CS_NUM) { + am3517evm_nand_data.cs = nandcs; + am3517evm_nand_data.gpmc_cs_baseaddr = (void *)(gpmc_base_add + + GPMC_CS0_BASE + nandcs*GPMC_CS_SIZE); + am3517evm_nand_data.gpmc_baseaddr = (void *) (gpmc_base_add); + + if (platform_device_register(&am3517evm_nand_device) < 0) + printk(KERN_ERR "Unable to register NAND device\n"); + + } +} + +#define AM3517_EVM_PHY_MASK (0xF) +#define AM3517_EVM_MDIO_FREQUENCY (1000000) /*PHY bus frequency */ + +static struct emac_platform_data am3517_evm_emac_pdata = { + .phy_mask = AM3517_EVM_PHY_MASK, + .mdio_max_freq = AM3517_EVM_MDIO_FREQUENCY, + .rmii_en = 1, +}; + +static int __init eth_addr_setup(char *str) +{ + int i; + + if(str == NULL) + return 0; + for(i = 0; i < ETH_ALEN; i++) + am3517_evm_emac_pdata.mac_addr[i] = simple_strtol(&str[i*3], + (char **)NULL, 16); + return 1; +} + +/* Get MAC address from kernel boot parameter eth=AA:BB:CC:DD:EE:FF */ +__setup("eth=", eth_addr_setup); + +static struct resource am3517_emac_resources[] = { + { + .start = AM3517_IPSS_EMAC_BASE, + .end = AM3517_IPSS_EMAC_BASE + 0x3FFFF, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_3517_EMAC_RXTHRESH_IRQ, + .end = INT_3517_EMAC_RXTHRESH_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_3517_EMAC_RX_IRQ, + .end = INT_3517_EMAC_RX_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_3517_EMAC_TX_IRQ, + .end = INT_3517_EMAC_TX_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_3517_EMAC_MISC_IRQ, + .end = INT_3517_EMAC_MISC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device am3517_emac_device = { + .name = "davinci_emac", + .id = 1, + .num_resources = ARRAY_SIZE(am3517_emac_resources), + .resource = am3517_emac_resources, +}; + +static void am3517_enable_ethernet_int(void) +{ + u32 regval; + + regval = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); + regval = (regval | AM3517_CPGMAC_RX_PULSE_CLR | + AM3517_CPGMAC_TX_PULSE_CLR | AM3517_CPGMAC_MISC_PULSE_CLR | + AM3517_CPGMAC_RX_THRESH_CLR); + omap_ctrl_writel(regval,OMAP3517_CONTROL_LVL_INTR_CLEAR); + regval = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); + +} + +static void am3517_disable_ethernet_int(void) +{ + u32 regval; + + regval = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); + regval = (regval | AM3517_CPGMAC_RX_PULSE_CLR | + AM3517_CPGMAC_TX_PULSE_CLR); + omap_ctrl_writel(regval,OMAP3517_CONTROL_LVL_INTR_CLEAR); + regval = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); + +} + +void am3517_evm_ethernet_init(struct emac_platform_data *pdata) + { + unsigned int regval; + + pdata->ctrl_reg_offset = AM3517_EMAC_CNTRL_OFFSET; + pdata->ctrl_mod_reg_offset = AM3517_EMAC_CNTRL_MOD_OFFSET; + pdata->ctrl_ram_offset = AM3517_EMAC_CNTRL_RAM_OFFSET; + pdata->mdio_reg_offset = AM3517_EMAC_MDIO_OFFSET; + pdata->ctrl_ram_size = AM3517_EMAC_CNTRL_RAM_SIZE; + pdata->version = EMAC_VERSION_2; + pdata->hw_ram_addr = AM3517_EMAC_HW_RAM_ADDR; + pdata->wrapper_interrupt_enable = am3517_enable_ethernet_int; + pdata->wrapper_interrupt_disable= am3517_disable_ethernet_int; + am3517_emac_device.dev.platform_data = pdata; + platform_device_register(&am3517_emac_device); + + regval = omap_ctrl_readl(OMAP3517_CONTROL_IP_SW_RESET); + regval = regval & (~(AM3517_CPGMAC_SW_RST)); + omap_ctrl_writel(regval,OMAP3517_CONTROL_IP_SW_RESET); + regval = omap_ctrl_readl(OMAP3517_CONTROL_IP_SW_RESET); + + regval = omap_ctrl_readl(OMAP3517_CONTROL_IP_CLK_CTRL); + regval = regval |(1 << OMAP3517_CPGMAC_VBUSP_CLK_SHIFT) | + (1 << OMAP3517_CPGMAC_FCLK_SHIFT); + omap_ctrl_writel(regval,OMAP3517_CONTROL_IP_CLK_CTRL); + regval = omap_ctrl_readl(OMAP3517_CONTROL_IP_CLK_CTRL); + return ; + } + + +/* + * TSC 2004 Support + */ +#define GPIO_TSC2004_IRQ 65 + +static int tsc2004_init_irq(void) +{ + int ret = 0; + + ret = gpio_request(GPIO_TSC2004_IRQ, "tsc2004-irq"); + if (ret < 0) { + printk(KERN_WARNING "failed to request GPIO#%d: %d\n", + GPIO_TSC2004_IRQ, ret); + return ret; + } + + if (gpio_direction_input(GPIO_TSC2004_IRQ)) { + printk(KERN_WARNING "GPIO#%d cannot be configured as " + "input\n", GPIO_TSC2004_IRQ); + return -ENXIO; + } + + omap_set_gpio_debounce(GPIO_TSC2004_IRQ, 1); + omap_set_gpio_debounce_time(GPIO_TSC2004_IRQ, 0xa); + return ret; +} + +static void tsc2004_exit_irq(void) +{ + gpio_free(GPIO_TSC2004_IRQ); +} + +static int tsc2004_get_irq_level(void) +{ + return gpio_get_value(GPIO_TSC2004_IRQ) ? 0 : 1; +} + +struct tsc2004_platform_data am3517evm_tsc2004data = { + .model = 2004, + .x_plate_ohms = 180, + .get_pendown_state = tsc2004_get_irq_level, + .init_platform_hw = tsc2004_init_irq, + .exit_platform_hw = tsc2004_exit_irq, +}; + +/* + * RTC - S35390A + */ +#define GPIO_RTCS35390A_IRQ 55 + +static struct i2c_board_info __initdata am3517evm_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("tsc2004", 0x4B), + .type = "tsc2004", + .platform_data = &am3517evm_tsc2004data, + }, + { + I2C_BOARD_INFO("s35390a", 0x30), + .type = "s35390a", + }, +}; + +/* + * VPFE - Video Decoder interface + */ +#define TVP514X_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* Inputs available at the TVP5146 */ +static struct v4l2_input tvp5146_inputs[] = { + { + .index = 0, + .name = "Composite", + .type = V4L2_INPUT_TYPE_CAMERA, + .std = TVP514X_STD_ALL, + }, + { + .index = 1, + .name = "S-Video", + .type = V4L2_INPUT_TYPE_CAMERA, + .std = TVP514X_STD_ALL, + }, +}; + +static struct vpfe_route tvp5146_routes[] = { + { + .input = INPUT_CVBS_VI1A, + .output = OUTPUT_10BIT_422_EMBEDDED_SYNC, + }, + { + .input = INPUT_SVIDEO_VI2C_VI1C, + .output = OUTPUT_10BIT_422_EMBEDDED_SYNC, + }, +}; + +static struct tvp514x_platform_data tvp5146_pdata = { + .clk_polarity = 0, + .hs_polarity = 1, + .vs_polarity = 1 +}; + +static struct vpfe_subdev_info vpfe_sub_devs[] = { + { + .module_name = TVP514X_MODULE_NAME, + .grp_id = VPFE_SUBDEV_TVP5146, + .num_inputs = ARRAY_SIZE(tvp5146_inputs), + .inputs = tvp5146_inputs, + .routes = tvp5146_routes, + .can_route = 1, + .ccdc_if_params = { + .if_type = VPFE_BT656_10BIT, + .hdpol = VPFE_PINPOL_POSITIVE, + .vdpol = VPFE_PINPOL_POSITIVE, + }, + .board_info = { + I2C_BOARD_INFO("tvp5146", 0x5C), + .platform_data = &tvp5146_pdata, + }, + }, +}; + +static void am3517_evm_clear_vpfe_intr(int vdint) +{ + unsigned int vpfe_int_clr; + + vpfe_int_clr = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); + + switch (vdint) { + /* VD0 interrrupt */ + case INT_3517_CCDC_VD0_IRQ: + vpfe_int_clr &= ~AM3517_VPFE_VD0_INT_CLR; + vpfe_int_clr |= AM3517_VPFE_VD0_INT_CLR; + break; + /* VD1 interrrupt */ + case INT_3517_CCDC_VD1_IRQ: + vpfe_int_clr &= ~AM3517_VPFE_VD1_INT_CLR; + vpfe_int_clr |= AM3517_VPFE_VD1_INT_CLR; + break; + /* VD2 interrrupt */ + case INT_3517_CCDC_VD2_IRQ: + vpfe_int_clr &= ~AM3517_VPFE_VD2_INT_CLR; + vpfe_int_clr |= AM3517_VPFE_VD2_INT_CLR; + break; + /* Clear all interrrupts */ + default: + vpfe_int_clr &= ~(AM3517_VPFE_VD0_INT_CLR | + AM3517_VPFE_VD1_INT_CLR | + AM3517_VPFE_VD2_INT_CLR); + vpfe_int_clr |= (AM3517_VPFE_VD0_INT_CLR | + AM3517_VPFE_VD1_INT_CLR | + AM3517_VPFE_VD2_INT_CLR); + break; + } + + omap_ctrl_writel(vpfe_int_clr, OMAP3517_CONTROL_LVL_INTR_CLEAR); + vpfe_int_clr = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); +} + +static struct resource vpfe_resources[] = { + { + .start = INT_3517_CCDC_VD0_IRQ, + .end = INT_3517_CCDC_VD0_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_3517_CCDC_VD1_IRQ, + .end = INT_3517_CCDC_VD1_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = AM3517_IPSS_VPFE_BASE, + .end = AM3517_IPSS_VPFE_BASE + 0xffff, + .flags = IORESOURCE_MEM, + }, + + { + .start = OMAP3517_CONTROL_LVL_INTR_CLEAR, + .end = OMAP3517_CONTROL_LVL_INTR_CLEAR + 0x4, + .flags = IORESOURCE_MEM, + }, +}; + +static struct vpfe_config vpfe_cfg = { + .num_subdevs = ARRAY_SIZE(vpfe_sub_devs), + .sub_devs = vpfe_sub_devs, + .card_name = "AM3517 EVM", + .ccdc = "DM6446 CCDC", + .clr_intr = am3517_evm_clear_vpfe_intr, + .num_clocks = 2, + .clocks = {"vpfe_ck", "vpfe_pck"}, + .i2c_adapter_id = 3, +}; + + +static u64 vpfe_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device vpfe_capture_dev = { + .name = CAPTURE_DRV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(vpfe_resources), + .resource = vpfe_resources, + .dev = { + .dma_mask = &vpfe_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &vpfe_cfg, + }, +}; + +#define LCD_PANEL_PWR 176 +#define LCD_PANEL_BKLIGHT_PWR 182 +#define LCD_PANEL_PWM 181 + +static int lcd_enabled; +static int dvi_enabled; + +static void __init am3517_evm_display_init(void) +{ + int r; + + /* + * Enable GPIO 182 = LCD Backlight Power + */ + r = gpio_request(LCD_PANEL_BKLIGHT_PWR, "lcd_backlight_pwr"); + if (r) { + printk(KERN_ERR "failed to get lcd_backlight_pwr\n"); + return; + } + gpio_direction_output(LCD_PANEL_BKLIGHT_PWR, 1); + /* + * Enable GPIO 181 = LCD Panel PWM + */ + r = gpio_request(LCD_PANEL_PWM, "lcd_pwm"); + if (r) { + printk(KERN_ERR "failed to get lcd_pwm\n"); + goto err_1; + } + gpio_direction_output(LCD_PANEL_PWM, 1); + /* + * Enable GPIO 176 = LCD Panel Power enable pin + */ + r = gpio_request(LCD_PANEL_PWR, "lcd_panel_pwr"); + if (r) { + printk(KERN_ERR "failed to get lcd_panel_pwr\n"); + goto err_2; + } + gpio_direction_output(LCD_PANEL_PWR, 1); + + printk(KERN_INFO "Display initialized successfully\n"); + return; + +err_2: + gpio_free(LCD_PANEL_PWM); +err_1: + gpio_free(LCD_PANEL_BKLIGHT_PWR); +} + +static int am3517_evm_panel_enable_lcd(struct omap_dss_device *dssdev) +{ + if (dvi_enabled) { + printk(KERN_ERR "cannot enable LCD, DVI is enabled\n"); + return -EINVAL; + } + gpio_set_value(LCD_PANEL_PWR, 1); + lcd_enabled = 1; + + return 0; +} + +static void am3517_evm_panel_disable_lcd(struct omap_dss_device *dssdev) +{ + gpio_set_value(LCD_PANEL_PWR, 0); + lcd_enabled = 0; +} + +static struct omap_dss_device am3517_evm_lcd_device = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "lcd", + .driver_name = "sharp_lq_panel", + .phy.dpi.data_lines = 16, + .platform_enable = am3517_evm_panel_enable_lcd, + .platform_disable = am3517_evm_panel_disable_lcd, +}; + +static int am3517_evm_panel_enable_tv(struct omap_dss_device *dssdev) +{ + return 0; +} + +static void am3517_evm_panel_disable_tv(struct omap_dss_device *dssdev) +{ +} + +static struct omap_dss_device am3517_evm_tv_device = { + .type = OMAP_DISPLAY_TYPE_VENC, + .name = "tv", + .driver_name = "venc", + .phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO, + .platform_enable = am3517_evm_panel_enable_tv, + .platform_disable = am3517_evm_panel_disable_tv, +}; + +static int am3517_evm_panel_enable_dvi(struct omap_dss_device *dssdev) +{ + if (lcd_enabled) { + printk(KERN_ERR "cannot enable DVI, LCD is enabled\n"); + return -EINVAL; + } + dvi_enabled = 1; + + return 0; +} + +static void am3517_evm_panel_disable_dvi(struct omap_dss_device *dssdev) +{ + dvi_enabled = 0; +} + +static struct omap_dss_device am3517_evm_dvi_device = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "dvi", + .driver_name = "generic_panel", + .phy.dpi.data_lines = 24, + .platform_enable = am3517_evm_panel_enable_dvi, + .platform_disable = am3517_evm_panel_disable_dvi, +}; + +static struct omap_dss_device *am3517_evm_dss_devices[] = { + &am3517_evm_lcd_device, + &am3517_evm_tv_device, + &am3517_evm_dvi_device, +}; + +static struct omap_dss_board_info am3517_evm_dss_data = { + .num_devices = ARRAY_SIZE(am3517_evm_dss_devices), + .devices = am3517_evm_dss_devices, + .default_device = &am3517_evm_lcd_device, +}; + +struct platform_device am3517_evm_dss_device = { + .name = "omapdss", + .id = -1, + .dev = { + .platform_data = &am3517_evm_dss_data, + }, +}; + +/* PMIC specific initialization */ +/* Consumers -> Supplies mapping */ +/* VDCDC1 -> VDD_CORE */ +REGULATOR_CONSUMER_SINGLE_SUPPLY(vdcdc1, vdd_core, NULL); +/* VDCDC2 -> VDDSHV */ +REGULATOR_CONSUMER_SINGLE_SUPPLY(vdcdc2, vddshv, NULL); +/* VDCDC2 |-> VDDS + |-> VDDS_SRAM_CORE_BG + |-> VDDS_SRAM_MPU */ +REGULATOR_COMSUMER_START(vdcdc3) = { + REGULATOR_COMSUMER_DEFINE(vdds, NULL), + REGULATOR_COMSUMER_DEFINE(vdds_sram_core_bg, NULL), + REGULATOR_COMSUMER_DEFINE(vdds_sram_mpu, NULL), +}; +/* LDO1 |-> VDDA1P8V_USBPHY + |-> VDDA_DAC */ +REGULATOR_COMSUMER_START(vldo1) = { + REGULATOR_COMSUMER_DEFINE(vdda1p8v_usbphy, NULL), + REGULATOR_COMSUMER_DEFINE(vdda_dac, NULL), +}; +/* LDO2 -> VDDA3P3V_USBPHY */ +REGULATOR_CONSUMER_SINGLE_SUPPLY(vldo2, vdda3p3v_usbphy, NULL); + +/* Regulator initialization data */ +REGULATOR_INIT_DATA_START(tps65023) = { + /* VDCDC1 */ + REGULATOR_INIT_DATA_DEFINE(vdcdc1, VDD_CORE, 1200000, 1200000, + REGULATOR_MODE_NORMAL, REGULATOR_CHANGE_STATUS, + true, false), + /* VDCDC2 */ + REGULATOR_INIT_DATA_DEFINE(vdcdc2, VDDSHV, 3300000, 3300000, + REGULATOR_MODE_NORMAL, REGULATOR_CHANGE_STATUS, + true, false), + /* VDCDC3 */ + REGULATOR_INIT_DATA_DEFINE(vdcdc3, VDDS, 1800000, 1800000, + REGULATOR_MODE_NORMAL, REGULATOR_CHANGE_STATUS, + true, false), + /* LDO1 */ + REGULATOR_INIT_DATA_DEFINE(vldo1, VDAC/VUSBPHY, 1800000, 1800000, + REGULATOR_MODE_NORMAL, REGULATOR_CHANGE_STATUS, + false, false), + /* LDO2 */ + REGULATOR_INIT_DATA_DEFINE(vldo2, VUSBPHY, 3300000, 3300000, + REGULATOR_MODE_NORMAL, REGULATOR_CHANGE_STATUS, + false, false), +}; + +/* I2C1 */ +static struct i2c_board_info __initdata am3517evm_i2c1_boardinfo[] = { + { + I2C_BOARD_INFO("tps65023", 0x48), + .flags = I2C_CLIENT_WAKE, + .platform_data = &tps65023_data[0], + }, +}; +/* I2C2 */ +static struct i2c_board_info __initdata am3517evm_i2c2_boardinfo[] = { + { + I2C_BOARD_INFO("tlv320aic23", 0x1A), + }, +}; + +static int __init am3517_evm_i2c_init(void) +{ + omap_register_i2c_bus(1, 400, am3517evm_i2c1_boardinfo, + ARRAY_SIZE(am3517evm_i2c1_boardinfo)); + omap_register_i2c_bus(2, 400, am3517evm_i2c2_boardinfo, + ARRAY_SIZE(am3517evm_i2c2_boardinfo)); + omap_register_i2c_bus(3, 400, NULL, 0); + + return 0; +} + +/* + * HECC information + */ + +static struct resource am3517_hecc_resources[] = { + { + .start = AM3517_IPSS_HECC_BASE, + .end = AM3517_IPSS_HECC_BASE + 0x3FFF, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_3517_HECC0_IRQ, + .end = INT_3517_HECC0_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device am3517_hecc_device = { + .name = "ti_hecc", + .id = 1, + .num_resources = ARRAY_SIZE(am3517_hecc_resources), + .resource = am3517_hecc_resources, +}; + +static struct ti_hecc_platform_data am3517_evm_hecc_pdata = { + .scc_hecc_offset = AM3517_HECC_SCC_HECC_OFFSET, + .scc_ram_offset = AM3517_HECC_SCC_RAM_OFFSET, + .hecc_ram_offset = AM3517_HECC_RAM_OFFSET, + .mbx_offset = AM3517_HECC_MBOX_OFFSET, + .int_line = AM3517_HECC_INT_LINE, + .version = AM3517_HECC_VERSION, +}; + +static void am3517_evm_hecc_init(struct ti_hecc_platform_data *pdata) +{ + am3517_hecc_device.dev.platform_data = pdata; + platform_device_register(&am3517_hecc_device); +} + + +/* + * Board initialization + */ +static struct omap_board_config_kernel am3517_evm_config[] __initdata = { +}; + +static struct platform_device *am3517_evm_devices[] __initdata = { + &am3517_evm_dss_device, + &vpfe_capture_dev, +}; + +static void __init am3517_evm_init_irq(void) +{ + omap_board_config = am3517_evm_config; + omap_board_config_size = ARRAY_SIZE(am3517_evm_config); + + omap2_init_common_hw(NULL, NULL, NULL, NULL, NULL); + omap_init_irq(); + omap_gpio_init(); +} + +static struct ehci_hcd_omap_platform_data ehci_pdata __initdata = { + + .port_mode[0] = EHCI_HCD_OMAP_MODE_PHY, +#if defined(CONFIG_PANEL_SHARP_LQ043T1DG01) || \ + defined(CONFIG_PANEL_SHARP_LQ043T1DG01_MODULE) + .port_mode[1] = EHCI_HCD_OMAP_MODE_UNKNOWN, +#else + .port_mode[1] = EHCI_HCD_OMAP_MODE_PHY, +#endif + .port_mode[2] = EHCI_HCD_OMAP_MODE_UNKNOWN, + + .phy_reset = true, + .reset_gpio_port[0] = 57, + .reset_gpio_port[1] = -EINVAL, + .reset_gpio_port[2] = -EINVAL +}; + +static struct am3517_hsmmc_info mmc[] = { + { + .mmc = 1, + .wires = 4, + /*TODO: Need to change*/ + .gpio_cd = 127, + .gpio_wp = 126, + }, + { + .mmc = 2, + .wires = 4, + /*TODO: Need to change*/ + .gpio_cd = 128, + .gpio_wp = 129, + }, + {} /* Terminator */ +}; + +static void __init am3517_evm_init(void) +{ + am3517_evm_i2c_init(); + + regulator_has_full_constraints(); + + platform_add_devices(am3517_evm_devices, + ARRAY_SIZE(am3517_evm_devices)); + omap_serial_init(); + am3517evm_flash_init(); + + am3517_evm_display_init(); + + usb_musb_init(); + /* Setup EHCI phy reset padconfig for port1 using GPIO57 */ + omap_cfg_reg(N5_3517_GPIO57_OUT); + usb_ehci_init(&ehci_pdata); + + /* MMC init function */ + am3517_mmc_init(mmc); + + am3517_evm_ethernet_init(&am3517_evm_emac_pdata); + am3517_evm_hecc_init(&am3517_evm_hecc_pdata); + + /* TSC 2004 */ + omap_cfg_reg(U1_34XX_GPIO65); + am3517evm_i2c_boardinfo[0].irq = gpio_to_irq(GPIO_TSC2004_IRQ); + /* RTC - S35390A */ + omap_cfg_reg(M2_34XX_GPIO55); + if (gpio_request(GPIO_RTCS35390A_IRQ, "rtcs35390a-irq") < 0) + printk(KERN_WARNING "failed to request GPIO#%d\n", + GPIO_RTCS35390A_IRQ); + if (gpio_direction_input(GPIO_RTCS35390A_IRQ)) + printk(KERN_WARNING "GPIO#%d cannot be configured as " + "input\n", GPIO_RTCS35390A_IRQ); + am3517evm_i2c_boardinfo[1].irq = gpio_to_irq(GPIO_RTCS35390A_IRQ); + + i2c_register_board_info(1, am3517evm_i2c_boardinfo, + ARRAY_SIZE(am3517evm_i2c_boardinfo)); + + +} + +static void __init am3517_evm_map_io(void) +{ + omap2_set_globals_343x(); + omap2_map_common_io(); +} + +MACHINE_START(OMAP3517EVM, "OMAP3517/AM3517 EVM") + .phys_io = 0x48000000, + .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc, + .boot_params = 0x80000100, + .map_io = am3517_evm_map_io, + .init_irq = am3517_evm_init_irq, + .init_machine = am3517_evm_init, + .timer = &omap_timer, +MACHINE_END diff --git a/arch/arm/mach-omap2/board-ginger.c b/arch/arm/mach-omap2/board-ginger.c new file mode 100644 index 00000000000..d0b4e591a0c --- /dev/null +++ b/arch/arm/mach-omap2/board-ginger.c @@ -0,0 +1,717 @@ +/* + * board-ginger - TimLL Devkit8000-based Ginger Console + * + * Copyright (C) 2009 Kim Botherway + * Copyright (C) 2010 Thomas Weber + * + * Modified from mach-omap2/board-omap3beagle.c + * + * Initial code: Syed Mohammed Khasim + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sdram-micron-mt46h32m32lf-6.h" +#include "mmc-twl4030.h" +#include "pm.h" +#include "omap3-opp.h" + +#include +#include + +#define GPMC_CS0_BASE 0x60 +#define GPMC_CS_SIZE 0x30 + +#define NAND_BLOCK_SIZE SZ_128K + +#define OMAP_DM9000_GPIO_IRQ 25 +#define OMAP3_EVM_TS_GPIO 27 + +static struct mtd_partition ginger_nand_partitions[] = { + /* All the partition sizes are listed in terms of NAND block size */ + { + .name = "X-Loader", + .offset = 0, + .size = 4 * NAND_BLOCK_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "U-Boot", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x80000 */ + .size = 15 * NAND_BLOCK_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "U-Boot Env", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x260000 */ + .size = 1 * NAND_BLOCK_SIZE, + }, + { + .name = "Kernel", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */ + .size = 32 * NAND_BLOCK_SIZE, + }, + { + .name = "File System", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x680000 */ + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct omap_nand_platform_data ginger_nand_data = { + .options = NAND_BUSWIDTH_16, + .parts = ginger_nand_partitions, + .nr_parts = ARRAY_SIZE(ginger_nand_partitions), + .dma_channel = -1, /* disable DMA in OMAP NAND driver */ + .nand_setup = NULL, + .dev_ready = NULL, +}; + +static struct resource ginger_nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device ginger_nand_device = { + .name = "omap2-nand", + .id = -1, + .dev = { + .platform_data = &ginger_nand_data, + }, + .num_resources = 1, + .resource = &ginger_nand_resource, +}; + +static struct twl4030_hsmmc_info mmc[] = { + { + .mmc = 1, + .wires = 8, + .gpio_wp = 29, + }, + {} /* Terminator */ +}; +static struct omap_board_config_kernel ginger_config[] __initdata = { +}; + +// static int ginger_panel_enable_lcd(struct omap_dss_device *dssdev) +// { +// twl_i2c_write_u8(TWL4030_MODULE_GPIO, 0x80, REG_GPIODATADIR1); +// twl_i2c_write_u8(TWL4030_MODULE_LED, 0x0, 0x0); +// +// return 0; +// } +// +// static void ginger_panel_disable_lcd(struct omap_dss_device *dssdev) +// { +// } +static int ginger_panel_enable_lcd(struct omap_dss_device *dssdev) +{ + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0x80, REG_GPIODATAOUT1); + twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x0, 0x0); + return 0; +} +static void ginger_panel_disable_lcd(struct omap_dss_device *dssdev) +{ + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0x80, REG_GPIODATAOUT1); + twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x0, 0x0); +} +static int ginger_panel_enable_dvi(struct omap_dss_device *dssdev) +{ + return 0; +} + +static void ginger_panel_disable_dvi(struct omap_dss_device *dssdev) +{ +} + +static int ginger_panel_enable_tv(struct omap_dss_device *dssdev) +{ + + return 0; +} + +static void ginger_panel_disable_tv(struct omap_dss_device *dssdev) +{ +} + + +static struct regulator_consumer_supply ginger_vmmc1_supply = { + .supply = "vmmc", +}; + +static struct regulator_consumer_supply ginger_vsim_supply = { + .supply = "vmmc_aux", +}; + +static struct regulator_consumer_supply ginger_vio_supplies[] = { + REGULATOR_SUPPLY("vcc", "spi2.0"), +}; + + +static struct omap_dss_device ginger_lcd_device = { + .name = "lcd", + .driver_name = "ginger_panel", + .type = OMAP_DISPLAY_TYPE_DPI, + .phy.dpi.data_lines = 24, + .platform_enable = ginger_panel_enable_lcd, + .platform_disable = ginger_panel_disable_lcd, +}; +static struct omap_dss_device ginger_dvi_device = { + .name = "dvi", + .driver_name = "generic_panel", + .type = OMAP_DISPLAY_TYPE_DPI, + .phy.dpi.data_lines = 24, + .platform_enable = ginger_panel_enable_dvi, + .platform_disable = ginger_panel_disable_dvi, +}; + +static struct omap_dss_device ginger_tv_device = { + .name = "tv", + .driver_name = "venc", + .type = OMAP_DISPLAY_TYPE_VENC, + .phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO, + .platform_enable = ginger_panel_enable_tv, + .platform_disable = ginger_panel_disable_tv, +}; + + +static struct omap_dss_device *ginger_dss_devices[] = { + &ginger_lcd_device, + &ginger_dvi_device, + &ginger_tv_device, +}; + +static struct omap_dss_board_info ginger_dss_data = { + .num_devices = ARRAY_SIZE(ginger_dss_devices), + .devices = ginger_dss_devices, + .default_device = &ginger_lcd_device, +}; + +static struct platform_device ginger_dss_device = { + .name = "omapdss", + .id = -1, + .dev = { + .platform_data = &ginger_dss_data, + }, +}; + +static struct regulator_consumer_supply ginger_vdda_dac_supply = { + .supply = "vdda_dac", + .dev = &ginger_dss_device.dev, +}; + +static int board_keymap[] = { + KEY(0, 0, KEY_1), + KEY(1, 0, KEY_2), + KEY(2, 0, KEY_3), + KEY(0, 1, KEY_4), + KEY(1, 1, KEY_5), + KEY(2, 1, KEY_6), + KEY(3, 1, KEY_F5), + KEY(0, 2, KEY_7), + KEY(1, 2, KEY_8), + KEY(2, 2, KEY_9), + KEY(3, 2, KEY_F6), + KEY(0, 3, KEY_F7), + KEY(1, 3, KEY_0), + KEY(2, 3, KEY_F8), + PERSISTENT_KEY(4, 5), + KEY(4, 4, KEY_VOLUMEUP), + KEY(5, 5, KEY_VOLUMEDOWN), + 0 +}; + +static struct matrix_keymap_data board_map_data = { + .keymap = board_keymap, + .keymap_size = ARRAY_SIZE(board_keymap), +}; + +static struct twl4030_keypad_data ginger_kp_data = { + .keymap_data = &board_map_data, + .rows = 6, + .cols = 6, + .rep = 1, +}; + +static struct gpio_led gpio_leds[]; + +static int ginger_twl_gpio_setup(struct device *dev, + unsigned gpio, unsigned ngpio) +{ + omap_cfg_reg(AH8_34XX_GPIO29); + /* gpio + 0 is "mmc0_cd" (input/IRQ) */ + mmc[0].gpio_cd = gpio + 0; + twl4030_mmc_init(mmc); + + /* link regulators to MMC adapters */ + ginger_vmmc1_supply.dev = mmc[0].dev; + ginger_vsim_supply.dev = mmc[0].dev; + + /* REVISIT: need ehci-omap hooks for external VBUS + * power switch and overcurrent detect + */ + + gpio_request(gpio + 1, "EHCI_nOC"); + gpio_direction_input(gpio + 1); + + /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */ + gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR"); + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1); + + /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ + gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; + + return 0; +} + +static struct twl4030_gpio_platform_data ginger_gpio_data = { + .gpio_base = OMAP_MAX_GPIO_LINES, + .irq_base = TWL4030_GPIO_IRQ_BASE, + .irq_end = TWL4030_GPIO_IRQ_END, + .use_leds = true, + .pullups = BIT(1), + .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) + | BIT(15) | BIT(16) | BIT(17), + .setup = ginger_twl_gpio_setup, +}; + +static struct regulator_consumer_supply ginger_vpll1_supplies[] = { + { + .supply = "vdvi", + .dev = &ginger_lcd_device.dev, + }, + { + .supply = "vdss_dsi", + .dev = &ginger_dss_device.dev, + } +}; + +/* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ +static struct regulator_init_data ginger_vmmc1 = { + .constraints = { + .min_uV = 1850000, + .max_uV = 3150000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &ginger_vmmc1_supply, +}; + +/* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ +static struct regulator_init_data ginger_vsim = { + .constraints = { + .min_uV = 1800000, + .max_uV = 3000000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &ginger_vsim_supply, +}; + +/* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ +static struct regulator_init_data ginger_vdac = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &ginger_vdda_dac_supply, +}; + +/* VPLL2 for digital video outputs */ +static struct regulator_init_data ginger_vpll1 = { + .constraints = { + .name = "VDVI", + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ginger_vpll1_supplies), + .consumer_supplies = ginger_vpll1_supplies, +}; + +/* VAUX4 for ads7846 and nubs */ +static struct regulator_init_data ginger_vio = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ginger_vio_supplies), + .consumer_supplies = ginger_vio_supplies, +}; + + +static struct twl4030_usb_data ginger_usb_data = { + .usb_mode = T2_USB_MODE_ULPI, +}; + +static struct twl4030_platform_data ginger_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + + /* platform_data for children goes here */ + .usb = &ginger_usb_data, + .gpio = &ginger_gpio_data, + .vmmc1 = &ginger_vmmc1, + .vsim = &ginger_vsim, + .vdac = &ginger_vdac, + .vpll1 = &ginger_vpll1, + .keypad = &ginger_kp_data, +}; + +static struct i2c_board_info __initdata ginger_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, + .irq = INT_34XX_SYS_NIRQ, + .platform_data = &ginger_twldata, + }, +}; + +static int __init ginger_i2c_init(void) +{ + omap_register_i2c_bus(1, 2600, ginger_i2c_boardinfo, + ARRAY_SIZE(ginger_i2c_boardinfo)); + /* Bus 3 is attached to the DVI port where devices like the pico DLP + * projector don't work reliably with 400kHz */ + omap_register_i2c_bus(3, 400, NULL, 0); + return 0; +} + +static struct gpio_led gpio_leds[] = { + { + .name = "led1", + .default_trigger = "heartbeat", + .gpio = 186, + .active_low = true, + }, + { + .name = "led2", + .default_trigger = "mmc0", + .gpio = 163, + .active_low = true, + }, + { + .name = "ledB", + .default_trigger = "none", + .gpio = 153, + .active_low = true, + }, + { + .name = "led3", + .default_trigger = "none", + .gpio = 164, + .active_low = true, + }, +}; + +static struct gpio_led_platform_data gpio_led_info = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct platform_device leds_gpio = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_led_info, + }, +}; + +static struct gpio_keys_button gpio_buttons[] = { + { + .code = BTN_EXTRA, + .gpio = 26, + .desc = "user", + .wakeup = 1, + }, +}; + +static struct gpio_keys_platform_data gpio_key_info = { + .buttons = gpio_buttons, + .nbuttons = ARRAY_SIZE(gpio_buttons), +}; + +static struct platform_device keys_gpio = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &gpio_key_info, + }, +}; + + + +#define OMAP_DM9000_BASE 0x2c000000 + +static struct resource omap_dm9000_resources[] = { + [0] = { + .start = OMAP_DM9000_BASE, + .end = (OMAP_DM9000_BASE + 0x4 - 1), + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = (OMAP_DM9000_BASE + 0x400), + .end = (OMAP_DM9000_BASE + 0x400 + 0x4 - 1), + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = OMAP_GPIO_IRQ(OMAP_DM9000_GPIO_IRQ), + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_LOW, + }, +}; + +static struct dm9000_plat_data omap_dm9000_platdata = { + .flags = DM9000_PLATF_16BITONLY, +}; + +static struct platform_device omap_dm9000_dev = { + .name = "dm9000", + .id = -1, + .num_resources = ARRAY_SIZE(omap_dm9000_resources), + .resource = omap_dm9000_resources, + .dev = { + .platform_data = &omap_dm9000_platdata, + }, +}; + +static void __init omap_dm9000_init(void) +{ + if (gpio_request(OMAP_DM9000_GPIO_IRQ, "dm9000 irq") < 0) { + printk(KERN_ERR "Failed to request GPIO%d for dm9000 IRQ\n", + OMAP_DM9000_GPIO_IRQ); + return; + } + + gpio_direction_input(OMAP_DM9000_GPIO_IRQ); +} + +static void __init ginger_init_irq(void) +{ + omap_board_config = ginger_config; + omap_board_config_size = ARRAY_SIZE(ginger_config); + + omap2_init_common_hw(mt46h32m32lf6_sdrc_params, mt46h32m32lf6_sdrc_params, + omap3_mpu_rate_table,omap3_dsp_rate_table, omap3_l3_rate_table); + + omap_init_irq(); +#ifdef CONFIG_OMAP_32K_TIMER + omap2_gp_clockevent_set_gptimer(12); +#endif + omap_gpio_init(); + omap_dm9000_init(); +} + +static struct platform_device *ginger_devices[] __initdata = { + &ginger_dss_device, + &leds_gpio, + &keys_gpio, + &omap_dm9000_dev, +}; + +static void __init ginger_flash_init(void) +{ + u8 cs = 0; + u8 nandcs = GPMC_CS_NUM + 1; + + u32 gpmc_base_add = OMAP34XX_GPMC_VIRT; + + /* find out the chip-select on which NAND exists */ + while (cs < GPMC_CS_NUM) { + u32 ret = 0; + ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + + if ((ret & 0xC00) == 0x800) { + printk(KERN_INFO "Found NAND on CS%d\n", cs); + if (nandcs > GPMC_CS_NUM) + nandcs = cs; + } + cs++; + } + + if (nandcs > GPMC_CS_NUM) { + printk(KERN_INFO "NAND: Unable to find configuration " + "in GPMC\n "); + return; + } + + if (nandcs < GPMC_CS_NUM) { + ginger_nand_data.cs = nandcs; + ginger_nand_data.gpmc_cs_baseaddr = (void *) + (gpmc_base_add + GPMC_CS0_BASE + nandcs * GPMC_CS_SIZE); + ginger_nand_data.gpmc_baseaddr = (void *) + (gpmc_base_add); + + printk(KERN_INFO "Registering NAND on CS%d\n", nandcs); + if (platform_device_register(&ginger_nand_device) < 0) + printk(KERN_ERR "Unable to register NAND device\n"); + } +} + +static struct ehci_hcd_omap_platform_data ehci_pdata __initconst = { + + .port_mode[0] = EHCI_HCD_OMAP_MODE_PHY, + .port_mode[1] = EHCI_HCD_OMAP_MODE_PHY, + .port_mode[2] = EHCI_HCD_OMAP_MODE_UNKNOWN, + + .phy_reset = true, + .reset_gpio_port[0] = -EINVAL, + .reset_gpio_port[1] = 147, + .reset_gpio_port[2] = -EINVAL +}; + +////// TOUCH +static void ads7846_dev_init(void) +{ + if (gpio_request(OMAP3_EVM_TS_GPIO, "ADS7846 pendown") < 0) + printk(KERN_ERR "can't get ads7846 pen down GPIO\n"); + + gpio_direction_input(OMAP3_EVM_TS_GPIO); + + omap_set_gpio_debounce(OMAP3_EVM_TS_GPIO, 1); + omap_set_gpio_debounce_time(OMAP3_EVM_TS_GPIO, 0xa); +} + +static int ads7846_get_pendown_state(void) +{ + return !gpio_get_value(OMAP3_EVM_TS_GPIO); +} + +struct ads7846_platform_data ads7846_config = { + .x_max = 0x0fff, + .y_max = 0x0fff, +// .x_plate_ohms = 180, +// .pressure_max = 255, + .debounce_max = 10, + .debounce_tol = 5, + .debounce_rep = 1, + .get_pendown_state = ads7846_get_pendown_state, + .keep_vref_on = 1, + .settle_delay_usecs = 150, +}; + +static struct omap2_mcspi_device_config ads7846_mcspi_config = { + .turbo_mode = 0, + .single_channel = 1, /* 0: slave, 1: master */ +}; + +struct spi_board_info omap3evm_spi_board_info[] = { + [0] = { + .modalias = "ads7846", + .bus_num = 2, + .chip_select = 0, + .max_speed_hz = 1500000, + .controller_data = &ads7846_mcspi_config, + .irq = OMAP_GPIO_IRQ(OMAP3_EVM_TS_GPIO), + .platform_data = &ads7846_config, + }, +}; + + +static void __init ginger_init(void) +{ + ginger_i2c_init(); + platform_add_devices(ginger_devices, + ARRAY_SIZE(ginger_devices)); + omap_board_config = ginger_config; + omap_board_config_size = ARRAY_SIZE(ginger_config); + + spi_register_board_info(omap3evm_spi_board_info, + ARRAY_SIZE(omap3evm_spi_board_info)); + omap_serial_init(); + + + + omap_cfg_reg(J25_34XX_GPIO170); + gpio_request(170, "DVI_nPD"); + /* REVISIT leave DVI powered down until it's needed ... */ + gpio_direction_output(170, true); + + usb_musb_init(); + usb_ehci_init(&ehci_pdata); + + ads7846_dev_init(); + ginger_flash_init(); + + /* Ensure SDRC pins are mux'd for self-refresh */ + omap_cfg_reg(H16_34XX_SDRC_CKE0); + omap_cfg_reg(H17_34XX_SDRC_CKE1); +} + +static void __init ginger_map_io(void) +{ + omap2_set_globals_343x(); + omap2_map_common_io(); +} + +MACHINE_START(GINGER, "GDi Ginger Console") + .phys_io = 0x48000000, + .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc, + .boot_params = 0x80000100, + .map_io = ginger_map_io, + .init_irq = ginger_init_irq, + .init_machine = ginger_init, + .timer = &omap_timer, +MACHINE_END diff --git a/arch/arm/mach-omap2/board-ldp-camera.c b/arch/arm/mach-omap2/board-ldp-camera.c new file mode 100644 index 00000000000..bc5400b884a --- /dev/null +++ b/arch/arm/mach-omap2/board-ldp-camera.c @@ -0,0 +1,210 @@ +/* + * linux/arch/arm/mach-omap2/board-ldp-camera.c + * + * Copyright (C) 2009 Texas Instruments Inc. + * Sergio Aguirre + * + * Modified from mach-omap2/board-ldp.c + * + * 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. + */ + +#ifdef CONFIG_TWL4030_CORE + +#include +#include +#include +#include + +#include + +#include + +#include + +static int cam_inited; +#include +#include <../drivers/media/video/omap34xxcam.h> +#include <../drivers/media/video/isp/ispreg.h> + +#define LDPCAM_USE_XCLKB 1 + +#define VAUX_1_8_V 0x05 +#define VAUX_DEV_GRP_P1 0x20 +#define VAUX_DEV_GRP_NONE 0x00 + +#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE) +#define OV3640_RESET_GPIO 98 +#define OV3640_STANDBY_GPIO 7 +#include +#include <../drivers/media/video/isp/ispcsi2.h> +#define OV3640_CSI2_CLOCK_POLARITY 0 /* +/- pin order */ +#define OV3640_CSI2_DATA0_POLARITY 0 /* +/- pin order */ +#define OV3640_CSI2_DATA1_POLARITY 0 /* +/- pin order */ +#define OV3640_CSI2_CLOCK_LANE 1 /* Clock lane position: 1 */ +#define OV3640_CSI2_DATA0_LANE 2 /* Data0 lane position: 2 */ +#define OV3640_CSI2_DATA1_LANE 3 /* Data1 lane position: 3 */ +#define OV3640_CSI2_PHY_THS_TERM 4 +#define OV3640_CSI2_PHY_THS_SETTLE 14 +#define OV3640_CSI2_PHY_TCLK_TERM 0 +#define OV3640_CSI2_PHY_TCLK_MISS 1 +#define OV3640_CSI2_PHY_TCLK_SETTLE 14 + +#define OV3640_BIGGEST_FRAME_BYTE_SIZE PAGE_ALIGN(2048 * 1536 * 2) + +static struct omap34xxcam_sensor_config ov3640_hwc = { + .sensor_isp = 0, + .capture_mem = OV3640_BIGGEST_FRAME_BYTE_SIZE * 2, + .ival_default = { 1, 15 }, +}; + +static struct isp_interface_config ov3640_if_config = { + .ccdc_par_ser = ISP_CSIA, + .dataline_shift = 0x0, + .hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE, + .strobe = 0x0, + .prestrobe = 0x0, + .shutter = 0x0, + .prev_sph = 2, + .prev_slv = 1, + .wenlog = ISPCCDC_CFG_WENLOG_AND, + .wait_hs_vs = 2, + .u.csi.crc = 0x0, + .u.csi.mode = 0x0, + .u.csi.edge = 0x0, + .u.csi.signalling = 0x0, + .u.csi.strobe_clock_inv = 0x0, + .u.csi.vs_edge = 0x0, + .u.csi.channel = 0x1, + .u.csi.vpclk = 0x1, + .u.csi.data_start = 0x0, + .u.csi.data_size = 0x0, + .u.csi.format = V4L2_PIX_FMT_SGRBG10, +}; + +static int ov3640_sensor_set_prv_data(struct v4l2_int_device *s, void *priv) +{ + struct omap34xxcam_hw_config *hwc = priv; + + hwc->u.sensor.sensor_isp = ov3640_hwc.sensor_isp; + hwc->dev_index = 1; + hwc->dev_minor = 4; + hwc->dev_type = OMAP34XXCAM_SLAVE_SENSOR; + return 0; +} + +static int ov3640_sensor_power_set(struct v4l2_int_device *s, + enum v4l2_power power) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + struct isp_csi2_lanes_cfg lanecfg; + struct isp_csi2_phy_cfg phyconfig; + static enum v4l2_power previous_power = V4L2_POWER_OFF; + + if (!cam_inited) { + printk(KERN_ERR "OV3640: Unable to control board GPIOs!\n"); + return -EFAULT; + } + + switch (power) { + case V4L2_POWER_ON: + if (previous_power == V4L2_POWER_OFF) + isp_csi2_reset(); + lanecfg.clk.pol = OV3640_CSI2_CLOCK_POLARITY; + lanecfg.clk.pos = OV3640_CSI2_CLOCK_LANE; + lanecfg.data[0].pol = OV3640_CSI2_DATA0_POLARITY; + lanecfg.data[0].pos = OV3640_CSI2_DATA0_LANE; + lanecfg.data[1].pol = OV3640_CSI2_DATA1_POLARITY; + lanecfg.data[1].pos = OV3640_CSI2_DATA1_LANE; + lanecfg.data[2].pol = 0; + lanecfg.data[2].pos = 0; + lanecfg.data[3].pol = 0; + lanecfg.data[3].pos = 0; + isp_csi2_complexio_lanes_config(&lanecfg); + isp_csi2_complexio_lanes_update(true); + + phyconfig.ths_term = OV3640_CSI2_PHY_THS_TERM; + phyconfig.ths_settle = OV3640_CSI2_PHY_THS_SETTLE; + phyconfig.tclk_term = OV3640_CSI2_PHY_TCLK_TERM; + phyconfig.tclk_miss = OV3640_CSI2_PHY_TCLK_MISS; + phyconfig.tclk_settle = OV3640_CSI2_PHY_TCLK_SETTLE; + isp_csi2_phy_config(&phyconfig); + isp_csi2_phy_update(true); + + isp_configure_interface(vdev->cam->isp, &ov3640_if_config); + + if (previous_power == V4L2_POWER_OFF) { + /* turn on analog power */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + VAUX_1_8_V, TWL4030_VAUX4_DEDICATED); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + VAUX_DEV_GRP_P1, TWL4030_VAUX4_DEV_GRP); + udelay(100); + /* Turn ON Omnivision sensor */ + gpio_set_value(OV3640_RESET_GPIO, 1); + gpio_set_value(OV3640_STANDBY_GPIO, 0); + udelay(100); + + /* RESET Omnivision sensor */ + gpio_set_value(OV3640_RESET_GPIO, 0); + udelay(100); + gpio_set_value(OV3640_RESET_GPIO, 1); + } + break; + case V4L2_POWER_OFF: + /* Power Down Sequence */ + isp_csi2_complexio_power(ISP_CSI2_POWER_OFF); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + VAUX_DEV_GRP_NONE, TWL4030_VAUX4_DEV_GRP); + break; + case V4L2_POWER_STANDBY: + break; + } + previous_power = power; + return 0; +} + +static u32 ov3640_sensor_set_xclk(struct v4l2_int_device *s, u32 xclkfreq) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + + return isp_set_xclk(vdev->cam->isp, xclkfreq, LDPCAM_USE_XCLKB); +} + +struct ov3640_platform_data ldp_ov3640_platform_data = { + .power_set = ov3640_sensor_power_set, + .priv_data_set = ov3640_sensor_set_prv_data, + .set_xclk = ov3640_sensor_set_xclk, +}; + +#endif + +void __init ldp_cam_init(void) +{ +#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE) + cam_inited = 0; + /* Request and configure gpio pins */ + if (gpio_request(OV3640_RESET_GPIO, "ov3640_reset_gpio") != 0) { + printk(KERN_ERR "Could not request GPIO %d", + OV3640_RESET_GPIO); + return; + } + if (gpio_request(OV3640_STANDBY_GPIO, "ov3640_standby_gpio") != 0) { + printk(KERN_ERR "Could not request GPIO %d", + OV3640_STANDBY_GPIO); + gpio_free(OV3640_RESET_GPIO); + return; + } + /* set to output mode */ + gpio_direction_output(OV3640_RESET_GPIO, true); + gpio_direction_output(OV3640_STANDBY_GPIO, true); +#endif + cam_inited = 1; +} +#else +void __init ldp_cam_init(void) +{ +} +#endif diff --git a/arch/arm/mach-omap2/board-omap35x-pmic.h b/arch/arm/mach-omap2/board-omap35x-pmic.h new file mode 100644 index 00000000000..096ca531df1 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap35x-pmic.h @@ -0,0 +1,227 @@ +/* + * board-omap35x-pmic.h + * + * Macros to create regulator supplies and regulator init data, along with the + * default wrappers for various TI PMICs like TWL4030/TPS65950, TPS65023 etc. + * + * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include + +/* Create supplies for a specific regulator */ +#define REGULATOR_COMSUMER_START(regulator) \ + static struct regulator_consumer_supply regulator##_consumers[] +/* Add/define supplies to the specific regulator */ +#define REGULATOR_COMSUMER_DEFINE(s, device) \ + { \ + .supply = #s, \ + .dev = device, \ + } + +/* Define regulator with no supplies attached to it */ +#define REGULATOR_CONSUMER_NO_SUPPLY(regulator) \ + REGULATOR_COMSUMER_START(regulator) = {} + +/* Define regulator with a single supply attached to it */ +#define REGULATOR_CONSUMER_SINGLE_SUPPLY(regulator, s, device) \ + REGULATOR_COMSUMER_START(regulator) = { \ + REGULATOR_COMSUMER_DEFINE(s, device), \ + } + +/* Define regulator with multiple supplies attached to it like */ + /* + REGULATOR_COMSUMER_START(name) = { + REGULATOR_COMSUMER_DEFINE(supply, device), + REGULATOR_COMSUMER_DEFINE(supply, device), + REGULATOR_COMSUMER_DEFINE(supply, device), + } + */ + +/* Define constraints flags */ +#define REGULATOR_CONSTRAINTS_FLAGS(reg_always_on, reg_boot_on, reg_apply_uV) \ + .always_on = reg_always_on, \ + .boot_on = reg_boot_on, \ + .apply_uV = reg_apply_uV, + +/* Define regulation constraints */ +#define REGULATOR_CONSTRAINTS(n, min, max, modes, ops, reg_on, apply_uv) \ + { \ + .name = #n, \ + .min_uV = min, \ + .max_uV = max, \ + .valid_modes_mask = modes, \ + .valid_ops_mask = ops, \ + .always_on = reg_on, \ + .apply_uV = apply_uv, \ + }, + +/* Declare the regulator initialization data */ +#define REGULATOR_INIT_DATA_START(regulator) \ + static struct regulator_init_data regulator##_data[] + +/* Populate various fields in the regulator initialization data */ +#define REGULATOR_INIT_DATA_DEFINE(regulator, n, min, max, modes, ops, \ + reg_on, apply_uv) \ + { \ + .constraints = REGULATOR_CONSTRAINTS(n, min, max, modes, ops, \ + reg_on, apply_uv) \ + .num_consumer_supplies = ARRAY_SIZE(regulator##_consumers), \ + .consumer_supplies = regulator##_consumers, \ + } + +/* Define regulator initialization data */ +#define REGULATOR_INIT_DATA(regulator,n,min,max,modes,ops,reg_on,apply_uv) \ + REGULATOR_INIT_DATA_START(regulator) = { \ + REGULATOR_INIT_DATA_DEFINE(regulator, n, min, max, modes, ops, \ + reg_on, apply_uv), \ + } + +/* + * Default wrappers specific to TWL4030/TPS65950 PMIC + */ +#if defined(CONFIG_PMIC_TWL4030) || defined(CONFIG_TWL4030_CORE) +#define TWL_REGULATOR_MODES_DEFAULT (REGULATOR_MODE_NORMAL | \ + REGULATOR_MODE_STANDBY) +#define TWL_REGULATOR_OPS_DEFAULT (REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_STATUS) + +/* Default supplies for TWL4030 regulators */ +#define TWL_VAUX1_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vaux1, \ + vaux1, NULL) +#define TWL_VAUX2_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vaux2, \ + vaux2, NULL) +#define TWL_VAUX3_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vaux3, \ + vaux3, NULL) +#define TWL_VAUX4_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vaux4, \ + vaux4, NULL) + +#define TWL_VMMC1_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vmmc1, \ + vmmc, NULL) +#define TWL_VMMC2_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vmmc2, \ + vmmc, NULL) + +#define TWL_VPLL1_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vpll1, \ + vpll1, NULL) +#define TWL_VPLL2_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vpll2, \ + vdvi, NULL) + +#define TWL_VSIM_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vsim, \ + vmmc_aux, NULL) +#define TWL_VDAC_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vdac, \ + vdac, NULL) + +#define TWL_VUSB1V5_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vusb1v5, \ + vusb1v5, NULL) +#define TWL_VUSB1V8_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vusb1v8, \ + vusb1v8, NULL) +#define TWL_VUSB3V1_SUPPLY REGULATOR_CONSUMER_SINGLE_SUPPLY(vusb3v1, \ + vusb3v1, NULL) + +/* Default initialization data for TWL4030 regulators */ +/* VAUX1 */ +#define TWL_VAUX1_DATA REGULATOR_INIT_DATA(vaux1, VAUX1, 2800000, 2800000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VAUX2 */ +#define TWL_VAUX2_DATA REGULATOR_INIT_DATA(vaux2, VAUX2, 2800000, 2800000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VAUX3 */ +#define TWL_VAUX3_DATA REGULATOR_INIT_DATA(vaux3, VAUX3, 2800000, 2800000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VAUX4 */ +#define TWL_VAUX4_DATA REGULATOR_INIT_DATA(vaux4, VAUX4, 1800000, 1800000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VMMC1 */ +#define TWL_VMMC1_DATA REGULATOR_INIT_DATA(vmmc1, VMMC1, 1850000, 3150000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VMMC2 */ +#define TWL_VMMC2_DATA REGULATOR_INIT_DATA(vmmc2, VMMC2, 1850000, 1850000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VPLL1 */ +#define TWL_VPLL1_DATA REGULATOR_INIT_DATA(vpll1, VPLL1, 1300000, 1300000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VPLL2 */ +#define TWL_VPLL2_DATA REGULATOR_INIT_DATA(vpll2, VDVI, 1800000, 1800000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VSIM */ +#define TWL_VSIM_DATA REGULATOR_INIT_DATA(vsim, VSIM, 1800000, 3000000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VDAC */ +#define TWL_VDAC_DATA REGULATOR_INIT_DATA(vdac, VDAC, 1800000, 1800000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VUSB1V5 */ +#define TWL_VUSB1V5_DATA REGULATOR_INIT_DATA(vusb1v5, VUSB1V5, \ + 1500000, 1500000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VUSB1V8 */ +#define TWL_VUSB1V8_DATA REGULATOR_INIT_DATA(vusb1v8, VUSB1V8, \ + 1800000, 1800000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +/* VUSB3V1 */ +#define TWL_VUSB3V1_DATA REGULATOR_INIT_DATA(vusb3v1, VUSB3V1, \ + 3100000, 3100000, \ + TWL_REGULATOR_MODES_DEFAULT, \ + TWL_REGULATOR_OPS_DEFAULT, \ + false, true) + +#endif /* CONFIG_PMIC_TWL4030 || CONFIG_TWL4030_CORE */ + +/* + * Definitions specific to TPS65023 + */ +#if defined(CONFIG_PMIC_TPS65023) +#endif /* CONFIG_PMIC_TPS65023 */ + +/* + * Definitions specific to TPS65073 + */ +#if defined(CONFIG_PMIC_TPS65073) +#endif /* CONFIG_PMIC_TPS65073 */ diff --git a/arch/arm/mach-omap2/board-omap3evm-camera.c b/arch/arm/mach-omap2/board-omap3evm-camera.c new file mode 100644 index 00000000000..05d4837bc36 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3evm-camera.c @@ -0,0 +1,515 @@ +/* + * arch/arm/mach-omap2/board-omap3evm-dc-v4l.c + * + * Driver for OMAP3 EVM Mass Market Daughter Card + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Anuj Aggarwal + * Sivaraj R + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* Include V4L2 ISP-Camera driver related header file */ +#include <../drivers/media/video/omap34xxcam.h> +#include <../drivers/media/video/isp/ispreg.h> + +#include "board-omap3evm-camera.h" + +#define MODULE_NAME "omap3evmdc" + +#define TVP5146_I2C_BUSNUM 3 +/* Is decoder present on-board or Daughter card */ +static bool is_dec_onboard; + +/* GPIO pins Daughter Card */ +#define GPIO134_SEL_TVP_Y (134) +#define GPIO54_SEL_EXP_CAM (54) +#define GPIO136_SEL_CAM (136) +/* GPIO pins for GEN_2 EVM */ +#define GPIO98_VID_DEC_RES (98) +#define nCAM_VD_SEL (157) + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE) +#if defined(CONFIG_VIDEO_OMAP3) || defined(CONFIG_VIDEO_OMAP3_MODULE) +static struct omap34xxcam_hw_config decoder_hwc = { + .dev_index = 0, + .dev_minor = 0, + .dev_type = OMAP34XXCAM_SLAVE_SENSOR, + .u.sensor.sensor_isp = 1, + .u.sensor.capture_mem = PAGE_ALIGN(720*525*2*4), +}; + +static struct isp_interface_config tvp5146_if_config = { + .ccdc_par_ser = ISP_PARLL_YUV_BT, + .dataline_shift = 0x1, + .hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE, + .strobe = 0x0, + .prestrobe = 0x0, + .shutter = 0x0, + .wait_hs_vs = 2, + .u.par.par_bridge = 0x0, + .u.par.par_clk_pol = 0x0, +}; +#endif + +static struct v4l2_ifparm ifparm = { + .if_type = V4L2_IF_TYPE_BT656, + .u = { + .bt656 = { + .frame_start_on_rising_vs = 1, + .bt_sync_correct = 0, + .swap = 0, + .latch_clk_inv = 0, + .nobt_hs_inv = 0, /* active high */ + .nobt_vs_inv = 0, /* active high */ + .mode = V4L2_IF_TYPE_BT656_MODE_BT_8BIT, + .clock_min = TVP514X_XCLK_BT656, + .clock_max = TVP514X_XCLK_BT656, + }, + }, +}; + +/** + * @brief tvp5146_ifparm - Returns the TVP5146 decoder interface parameters + * + * @param p - pointer to v4l2_ifparm structure + * + * @return result of operation - 0 is success + */ +static int tvp5146_ifparm(struct v4l2_ifparm *p) +{ + if (p == NULL) + return -EINVAL; + + *p = ifparm; + return 0; +} + +/** + * @brief tvp5146_set_prv_data - Returns tvp5146 omap34xx driver private data + * + * @param priv - pointer to omap34xxcam_hw_config structure + * + * @return result of operation - 0 is success + */ +static int tvp5146_set_prv_data(struct v4l2_int_device *s, void *priv) +{ +#if defined(CONFIG_VIDEO_OMAP3) || defined(CONFIG_VIDEO_OMAP3_MODULE) + struct omap34xxcam_hw_config *hwc = priv; + + if (priv == NULL) + return -EINVAL; + + hwc->u.sensor.sensor_isp = decoder_hwc.u.sensor.sensor_isp; + hwc->u.sensor.capture_mem = decoder_hwc.u.sensor.capture_mem; + hwc->dev_index = decoder_hwc.dev_index; + hwc->dev_minor = decoder_hwc.dev_minor; + hwc->dev_type = decoder_hwc.dev_type; + return 0; +#else + return -EINVAL; +#endif +} + +/** + * @brief omap3evmdc_set_mux - Sets mux to enable/disable signal routing to + * different peripherals present in board + * IMPORTANT - This function will take care of writing appropriate values for + * active low signals as well + * + * @param mux_id - enum, mux id to enable/disable + * @param value - enum, ENABLE_MUX for enabling and DISABLE_MUX for disabling + * + * @return result of operation - 0 is success + */ +static int omap3evmdc_set_mux(enum omap3evmdc_mux mux_id, enum config_mux value) +{ + int err = 0; + + if (unlikely(mux_id >= NUM_MUX)) { + printk(KERN_ERR MODULE_NAME ": Invalid mux id\n"); + return -EPERM; + } + + switch (mux_id) { + case MUX_TVP5146: + if (ENABLE_MUX == value) { + /* Enable TVP5146 Video in (GPIO134 = 0) */ + gpio_set_value(GPIO134_SEL_TVP_Y, 0); + /* Disable Expansion Camera Video in (GPIO54 = 1) */ + gpio_set_value(GPIO54_SEL_EXP_CAM, 1); + /* Disable Camera Video in (GPIO136 = 1)*/ + gpio_set_value(GPIO136_SEL_CAM, 1); + } else { + /* Disable TVP5146 Video in (GPIO134 = 0) */ + gpio_set_value(GPIO134_SEL_TVP_Y, 1); + } + break; + + case MUX_CAMERA_SENSOR: + if (ENABLE_MUX == value) { + /* Disable TVP5146 Video in (GPIO134 = 0) */ + gpio_set_value(GPIO134_SEL_TVP_Y, 1); + /* Disable Exapansion Camera Video in (GPIO54 = 1) */ + gpio_set_value(GPIO54_SEL_EXP_CAM, 1); + /* Enable Camera Video in (GPIO136 = 1) */ + gpio_set_value(GPIO136_SEL_CAM, 0); + } else { + /* Disable Camera Video in (GPIO136 = 1) */ + gpio_set_value(GPIO136_SEL_CAM, 1); + } + break; + + case MUX_EXP_CAMERA_SENSOR: + if (ENABLE_MUX == value) { + /* Disable TVP5146 Video in (GPIO134 = 0) */ + gpio_set_value(GPIO134_SEL_TVP_Y, 1); + /* Enable Expansion Camera Video in (GPIO54 = 1) */ + gpio_set_value(GPIO54_SEL_EXP_CAM, 0); + /* Disable Camera Video in (GPIO136 = 1) */ + gpio_set_value(GPIO136_SEL_CAM, 1); + } else { + /* Disable Expansion Camera Video in (GPIO54 = 1) */ + gpio_set_value(GPIO54_SEL_EXP_CAM, 1); + } + break; + + case NUM_MUX: + default: + printk(KERN_ERR "Invalid mux id\n"); + err = -EPERM; + } + + return err; +} + +/** + * @brief omap3evm_set_mux - Sets mux to enable/disable signal routing to + * different peripherals present on new EVM board + * IMPORTANT - This function will take care of writing appropriate values for + * active low signals as well + * + * @param mux_id - enum, mux id to enable/disable + * @param value - enum, ENABLE_MUX for enabling and DISABLE_MUX for disabling + * + * @return result of operation - 0 is success + */ +static int omap3evm_set_mux(enum omap3evmdc_mux mux_id, enum config_mux value) +{ + static int is_init_done = 0; + unsigned char val; + int err = 0; + + if (unlikely(mux_id >= NUM_MUX)) { + printk(KERN_ERR MODULE_NAME ": Invalid mux id\n"); + return -EPERM; + } + + if (is_init_done == 0) { + /*FIXME: Need to follow standard GPIO API's to control + * TWL4030 GPIO. + */ + /* Enable TWL GPIO Module */ + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0x04, REG_GPIO_CTRL); + + /* First Level Enable GPIO */ + /* Configure GPIO2 as output */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, REG_GPIODATADIR1); + val |= 0x04; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, REG_GPIODATADIR1); + /* Set GPIO-2 pull-up */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, REG_GPIOPUPDCTR1); + val |= 0x20; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, REG_GPIOPUPDCTR1); + /* Set GPIO-2 = 0 */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, REG_GPIODATAOUT1); + val &= ~0x04; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, REG_GPIODATAOUT1); + + /* Configure GPIO8 as output*/ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, REG_GPIODATADIR2); + val |= 0x1; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, REG_GPIODATADIR2); + /* GPIO-8 pull down */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, REG_GPIOPUPDCTR3); + val |= 0x01; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, REG_GPIOPUPDCTR3); + + /* Assert the reset signal */ + gpio_set_value(GPIO98_VID_DEC_RES, 0); + mdelay(5); + gpio_set_value(GPIO98_VID_DEC_RES, 1); + } + switch (mux_id) { + case MUX_TVP5146: + if (ENABLE_MUX == value) { + /* Set GPIO8 = 0 */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, + REG_GPIODATAOUT2); + val &= ~0x1; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, + REG_GPIODATAOUT2); + + gpio_set_value(nCAM_VD_SEL, 1); + } else { + /* Set GPIO8 = 0 */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, + REG_GPIODATAOUT2); + val |= 0x1; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, + REG_GPIODATAOUT2); + } + break; + + case MUX_CAMERA_SENSOR: + if (ENABLE_MUX == value) { + /* Set GPIO8 = 0 */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, + REG_GPIODATAOUT2); + val &= ~0x1; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, + REG_GPIODATAOUT2); + + gpio_set_value(nCAM_VD_SEL, 0); + } else { + /* Set GPIO8 = 0 */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, + REG_GPIODATAOUT2); + val |= 0x1; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, + REG_GPIODATAOUT2); + } + break; + + case MUX_EXP_CAMERA_SENSOR: + if (ENABLE_MUX == value) { + /* Set GPIO8 = 1 */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, + REG_GPIODATAOUT2); + val |= 0x1; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, + REG_GPIODATAOUT2); + + } else { + /* Set GPIO8 = 0 */ + twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &val, + REG_GPIODATAOUT2); + val &= ~0x1; + twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, val, + REG_GPIODATAOUT2); + } + break; + + case NUM_MUX: + default: + printk(KERN_ERR "Invalid mux id\n"); + err = -EPERM; + } + + return err; +} +/** + * @brief tvp5146_power_set - Power-on or power-off TVP5146 device + * + * @param power - enum, Power on/off, resume/standby + * + * @return result of operation - 0 is success + */ +static int tvp5146_power_set(struct v4l2_int_device *s, enum v4l2_power power) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + + switch (power) { + case V4L2_POWER_OFF: + /* Disable mux for TVP5146 decoder data path */ + if (is_dec_onboard) { + if (omap3evm_set_mux(MUX_TVP5146, DISABLE_MUX)) + return -ENODEV; + } else { + if (omap3evmdc_set_mux(MUX_TVP5146, DISABLE_MUX)) + return -ENODEV; + } + break; + + case V4L2_POWER_STANDBY: + break; + + case V4L2_POWER_ON: + /* Enable mux for TVP5146 decoder data path */ + if (is_dec_onboard) { + if (omap3evm_set_mux(MUX_TVP5146, ENABLE_MUX)) + return -ENODEV; + } else { + if (omap3evmdc_set_mux(MUX_TVP5146, ENABLE_MUX)) + return -ENODEV; + } + +#if defined(CONFIG_VIDEO_OMAP3) || defined(CONFIG_VIDEO_OMAP3_MODULE) + isp_configure_interface(vdev->cam->isp, &tvp5146_if_config); +#endif + break; + + default: + return -ENODEV; + break; + } + return 0; +} + +static struct tvp514x_platform_data tvp5146_pdata = { + .master = "omap34xxcam", + .power_set = tvp5146_power_set, + .priv_data_set = tvp5146_set_prv_data, + .ifparm = tvp5146_ifparm, + /* Some interface dependent params */ + .clk_polarity = 0, /* data clocked out on falling edge */ + .hs_polarity = 1, /* 0 - Active low, 1- Active high */ + .vs_polarity = 1, /* 0 - Active low, 1- Active high */ +}; + +static struct i2c_board_info __initdata tvp5146_i2c_board_info = { + I2C_BOARD_INFO("tvp5146m2", 0), + .platform_data = &tvp5146_pdata, +}; + +#endif /* #ifdef CONFIG_VIDEO_TVP514X */ + +/** + * @brief omap3evmdc_mdc_config - GPIO configuration for + * GPIO 134, 54 and 136 + * + * @return result of operation - 0 is success + */ +static int omap3evmdc_mdc_config(void) +{ + if (is_dec_onboard) { + /* Enable Video Decoder */ + omap_cfg_reg(AA21_34XX_GPIO157); + if (gpio_request(nCAM_VD_SEL, "Vid-Dec Sel") < 0) { + printk(KERN_ERR "Failed to get GPIO 157\n"); + return -EINVAL; + } + gpio_direction_output(nCAM_VD_SEL, 1); + gpio_set_value(nCAM_VD_SEL, 1); + + omap_cfg_reg(C23_34XX_GPIO98); + if (gpio_request(GPIO98_VID_DEC_RES, "vid-dec reset") < 0) { + printk(KERN_ERR "failed to get GPIO98_VID_DEC_RES\n"); + return -EINVAL; + } + gpio_direction_output(GPIO98_VID_DEC_RES, 1); + } else { + + /* Setting the MUX configuration */ + omap_cfg_reg(AG4_34XX_GPIO134_OUT); + omap_cfg_reg(U8_34XX_GPIO54_OUT); + omap_cfg_reg(AE4_34XX_GPIO136_OUT); + + if (gpio_request(GPIO134_SEL_TVP_Y, "TVP5146 Vid-in") < 0) { + printk(KERN_ERR MODULE_NAME ": Can't get GPIO 134\n"); + return -EINVAL; + } + + if (gpio_request(GPIO54_SEL_EXP_CAM, "EXP_CAM Vid-in") < 0) { + printk(KERN_ERR MODULE_NAME ": Can't get GPIO 54\n"); + return -EINVAL; + } + + if (gpio_request(GPIO136_SEL_CAM, "CAM Vid-in") < 0) { + printk(KERN_ERR MODULE_NAME ": Can't get GPIO 136\n"); + return -EINVAL; + } + + /* Make GPIO as output */ + gpio_direction_output(GPIO134_SEL_TVP_Y, 0); + gpio_direction_output(GPIO54_SEL_EXP_CAM, 0); + gpio_direction_output(GPIO136_SEL_CAM, 0); + } + + return 0; +} + +/** + * @brief omap3evmdc_init - module init function. Should be called before any + * client driver init call + * + * @return result of operation - 0 is success + */ +int __init omap3evmdc_init(void) +{ + int err; + + /* Status of Video Decoder : On Board or DC */ + if (get_omap3_evm_rev() >= OMAP3EVM_BOARD_GEN_2) + is_dec_onboard = TRUE; + else + is_dec_onboard = FALSE; + + err = omap3evmdc_mdc_config(); + if (err) { + printk(KERN_ERR MODULE_NAME ": MDC configuration failed \n"); + return err; + } + + /* + * Register the I2C devices present in the board to the I2C + * framework. + * If more I2C devices are added, then each device information should + * be registered with I2C using i2c_register_board_info(). + */ +#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE) + if (get_omap3_evm_rev() >= OMAP3EVM_BOARD_GEN_2) + tvp5146_i2c_board_info.addr = 0x5C; + else + tvp5146_i2c_board_info.addr = 0x5D; + + err = i2c_register_board_info(TVP5146_I2C_BUSNUM, + &tvp5146_i2c_board_info, 1); + if (err) { + printk(KERN_ERR MODULE_NAME \ + ": TVP5146 I2C Board Registration failed \n"); + return err; + } +#endif + printk(KERN_INFO MODULE_NAME ": Driver registration complete \n"); + + return 0; +} +arch_initcall(omap3evmdc_init); diff --git a/arch/arm/mach-omap2/board-omap3evm-camera.h b/arch/arm/mach-omap2/board-omap3evm-camera.h new file mode 100644 index 00000000000..5eaffc59769 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3evm-camera.h @@ -0,0 +1,43 @@ +/* + * arch/arm/mach-omap2/board-omap3evm-dc.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Anuj Aggarwal + * Sivaraj R + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __BOARD_OMAP3EVM_DC_H_ +#define __BOARD_OMAP3EVM_DC_H_ + +/* mux id to enable/disable signal routing to different peripherals */ +enum omap3evmdc_mux { + MUX_TVP5146 = 0, + MUX_CAMERA_SENSOR, + MUX_EXP_CAMERA_SENSOR, + NUM_MUX +}; + +/* enum to enable or disable mux */ +enum config_mux { + DISABLE_MUX, + ENABLE_MUX +}; + +#endif /* __BOARD_OMAP3EVM_DC_H_ */ diff --git a/arch/arm/mach-omap2/board-zoom2-camera.c b/arch/arm/mach-omap2/board-zoom2-camera.c new file mode 100644 index 00000000000..1ba2982a10a --- /dev/null +++ b/arch/arm/mach-omap2/board-zoom2-camera.c @@ -0,0 +1,391 @@ +/* + * linux/arch/arm/mach-omap2/board-zoom2-camera.c + * + * Copyright (C) 2007 Texas Instruments + * + * Modified from mach-omap2/board-generic.c + * + * Initial code: Syed Mohammed Khasim + * + * 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. + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#ifdef CONFIG_OMAP_PM_SRF +#include +#endif + +static int cam_inited; + +static struct device *zoom2cam_dev; + +#include +#include <../drivers/media/video/omap34xxcam.h> +#include <../drivers/media/video/isp/ispreg.h> +#define DEBUG_BASE 0x08000000 + +#define REG_SDP3430_FPGA_GPIO_2 (0x50) +#define FPGA_SPR_GPIO1_3v3 (0x1 << 14) +#define FPGA_GPIO6_DIR_CTRL (0x1 << 6) + +#define CAMZOOM2_USE_XCLKB 1 + +/* Sensor specific GPIO signals */ +#define IMX046_RESET_GPIO 98 +#define IMX046_STANDBY_GPIO 58 +#define LV8093_PS_GPIO 7 + +static struct regulator *zoom2_imx046_reg1; +static struct regulator *zoom2_imx046_reg2; + +#if defined(CONFIG_VIDEO_IMX046) || defined(CONFIG_VIDEO_IMX046_MODULE) +#include +#include <../drivers/media/video/isp/ispcsi2.h> +#define IMX046_CSI2_CLOCK_POLARITY 0 /* +/- pin order */ +#define IMX046_CSI2_DATA0_POLARITY 0 /* +/- pin order */ +#define IMX046_CSI2_DATA1_POLARITY 0 /* +/- pin order */ +#define IMX046_CSI2_CLOCK_LANE 1 /* Clock lane position: 1 */ +#define IMX046_CSI2_DATA0_LANE 2 /* Data0 lane position: 2 */ +#define IMX046_CSI2_DATA1_LANE 3 /* Data1 lane position: 3 */ +#define IMX046_CSI2_PHY_THS_TERM 2 +#define IMX046_CSI2_PHY_THS_SETTLE 23 +#define IMX046_CSI2_PHY_TCLK_TERM 0 +#define IMX046_CSI2_PHY_TCLK_MISS 1 +#define IMX046_CSI2_PHY_TCLK_SETTLE 14 +#define IMX046_BIGGEST_FRAME_BYTE_SIZE PAGE_ALIGN(3280 * 2464 * 2) +#endif + +#ifdef CONFIG_VIDEO_LV8093 +#include +/* GPIO7 is connected to lens PS pin through inverter */ +#define LV8093_PWR_OFF 1 +#define LV8093_PWR_ON (!LV8093_PWR_OFF) +#endif + + +#ifdef CONFIG_VIDEO_LV8093 +static int lv8093_lens_power_set(enum v4l2_power power) +{ + static enum v4l2_power previous_pwr = V4L2_POWER_OFF; + + if (!cam_inited) { + printk(KERN_ERR "MT9P012: Unable to control board GPIOs!\n"); + return -EFAULT; + } + + switch (power) { + case V4L2_POWER_ON: + printk(KERN_DEBUG "lv8093_lens_power_set(ON)\n"); + if (previous_pwr == V4L2_POWER_OFF) + gpio_set_value(LV8093_PS_GPIO, LV8093_PWR_OFF); + gpio_set_value(LV8093_PS_GPIO, LV8093_PWR_ON); + break; + case V4L2_POWER_OFF: + printk(KERN_DEBUG "lv8093_lens_power_set(OFF)\n"); + break; + case V4L2_POWER_STANDBY: + printk(KERN_DEBUG "lv8093_lens_power_set(STANDBY)\n"); + gpio_set_value(LV8093_PS_GPIO, LV8093_PWR_OFF); + break; + } + previous_pwr = power; + return 0; +} + +static int lv8093_lens_set_prv_data(void *priv) +{ + struct omap34xxcam_hw_config *hwc = priv; + + hwc->dev_index = 2; + hwc->dev_minor = 5; + hwc->dev_type = OMAP34XXCAM_SLAVE_LENS; + return 0; +} + +struct lv8093_platform_data zoom2_lv8093_platform_data = { + .power_set = lv8093_lens_power_set, + .priv_data_set = lv8093_lens_set_prv_data, +}; +#endif + +#if defined(CONFIG_VIDEO_IMX046) || defined(CONFIG_VIDEO_IMX046_MODULE) + +static struct omap34xxcam_sensor_config imx046_hwc = { + .sensor_isp = 0, + .capture_mem = IMX046_BIGGEST_FRAME_BYTE_SIZE * 2, + .ival_default = { 1, 10 }, +}; + +static int imx046_sensor_set_prv_data(struct v4l2_int_device *s, void *priv) +{ + struct omap34xxcam_hw_config *hwc = priv; + + hwc->u.sensor.sensor_isp = imx046_hwc.sensor_isp; + hwc->dev_index = 2; + hwc->dev_minor = 5; + hwc->dev_type = OMAP34XXCAM_SLAVE_SENSOR; + + return 0; +} + +static struct isp_interface_config imx046_if_config = { + .ccdc_par_ser = ISP_CSIA, + .dataline_shift = 0x0, + .hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE, + .strobe = 0x0, + .prestrobe = 0x0, + .shutter = 0x0, + .wenlog = ISPCCDC_CFG_WENLOG_AND, + .wait_hs_vs = 2, + .u.csi.crc = 0x0, + .u.csi.mode = 0x0, + .u.csi.edge = 0x0, + .u.csi.signalling = 0x0, + .u.csi.strobe_clock_inv = 0x0, + .u.csi.vs_edge = 0x0, + .u.csi.channel = 0x0, + .u.csi.vpclk = 0x2, + .u.csi.data_start = 0x0, + .u.csi.data_size = 0x0, + .u.csi.format = V4L2_PIX_FMT_SGRBG10, +}; + + +static int imx046_sensor_power_set(struct v4l2_int_device *s, enum v4l2_power power) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + struct isp_csi2_lanes_cfg lanecfg; + struct isp_csi2_phy_cfg phyconfig; + static enum v4l2_power previous_power = V4L2_POWER_OFF; + int err = 0; + + if (!cam_inited) { + printk(KERN_ERR "MT9P012: Unable to control board GPIOs!\n"); + return -EFAULT; + } + + /* + * Plug regulator consumer to respective VAUX supply + * if not done before. + */ + if (!zoom2_imx046_reg1 && !zoom2_imx046_reg2) { + zoom2_imx046_reg1 = regulator_get(zoom2cam_dev, "vaux2_1"); + if (IS_ERR(zoom2_imx046_reg1)) { + dev_err(zoom2cam_dev, "vaux2_1 regulator missing\n"); + return PTR_ERR(zoom2_imx046_reg1); + } + zoom2_imx046_reg2 = regulator_get(zoom2cam_dev, "vaux4_1"); + if (IS_ERR(zoom2_imx046_reg2)) { + dev_err(zoom2cam_dev, "vaux4_1 regulator missing\n"); + regulator_put(zoom2_imx046_reg1); + return PTR_ERR(zoom2_imx046_reg2); + } + } + + switch (power) { + case V4L2_POWER_ON: + /* Power Up Sequence */ + printk(KERN_DEBUG "imx046_sensor_power_set(ON)\n"); + + /* Through-put requirement: + * 3280 x 2464 x 2Bpp x 7.5fps x 3 memory ops = 355163 KByte/s + */ +#ifdef CONFIG_OMAP_PM_SRF + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 355163); +#endif + + isp_csi2_reset(); + + lanecfg.clk.pol = IMX046_CSI2_CLOCK_POLARITY; + lanecfg.clk.pos = IMX046_CSI2_CLOCK_LANE; + lanecfg.data[0].pol = IMX046_CSI2_DATA0_POLARITY; + lanecfg.data[0].pos = IMX046_CSI2_DATA0_LANE; + lanecfg.data[1].pol = IMX046_CSI2_DATA1_POLARITY; + lanecfg.data[1].pos = IMX046_CSI2_DATA1_LANE; + lanecfg.data[2].pol = 0; + lanecfg.data[2].pos = 0; + lanecfg.data[3].pol = 0; + lanecfg.data[3].pos = 0; + isp_csi2_complexio_lanes_config(&lanecfg); + isp_csi2_complexio_lanes_update(true); + + isp_csi2_ctrl_config_ecc_enable(true); + + phyconfig.ths_term = IMX046_CSI2_PHY_THS_TERM; + phyconfig.ths_settle = IMX046_CSI2_PHY_THS_SETTLE; + phyconfig.tclk_term = IMX046_CSI2_PHY_TCLK_TERM; + phyconfig.tclk_miss = IMX046_CSI2_PHY_TCLK_MISS; + phyconfig.tclk_settle = IMX046_CSI2_PHY_TCLK_SETTLE; + isp_csi2_phy_config(&phyconfig); + isp_csi2_phy_update(true); + + isp_configure_interface(vdev->cam->isp, &imx046_if_config); + + if (previous_power == V4L2_POWER_OFF) { + /* nRESET is active LOW. set HIGH to release reset */ + gpio_set_value(IMX046_RESET_GPIO, 1); + + + /* turn on analog power */ + regulator_enable(zoom2_imx046_reg1); + regulator_enable(zoom2_imx046_reg2); + udelay(100); + + /* have to put sensor to reset to guarantee detection */ + gpio_set_value(IMX046_RESET_GPIO, 0); + udelay(1500); + + /* nRESET is active LOW. set HIGH to release reset */ + gpio_set_value(IMX046_RESET_GPIO, 1); + udelay(300); + } + break; + case V4L2_POWER_OFF: + printk(KERN_DEBUG "imx046_sensor_power_set(OFF)\n"); + /* Power Down Sequence */ + isp_csi2_complexio_power(ISP_CSI2_POWER_OFF); + + if (regulator_is_enabled(zoom2_imx046_reg1)) + regulator_disable(zoom2_imx046_reg1); + if (regulator_is_enabled(zoom2_imx046_reg2)) + regulator_disable(zoom2_imx046_reg2); + +#ifdef CONFIG_OMAP_PM_SRF + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 0); +#endif + break; + case V4L2_POWER_STANDBY: + printk(KERN_DEBUG "imx046_sensor_power_set(STANDBY)\n"); + isp_csi2_complexio_power(ISP_CSI2_POWER_OFF); + /*TODO*/ +#ifdef CONFIG_OMAP_PM_SRF + omap_pm_set_min_bus_tput(vdev->cam->isp, OCP_INITIATOR_AGENT, 0); +#endif + break; + } + + /* Save powerstate to know what was before calling POWER_ON. */ + previous_power = power; + return err; +} + +static u32 imx046_sensor_set_xclk(struct v4l2_int_device *s, u32 xclkfreq) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + + return isp_set_xclk(vdev->cam->isp, xclkfreq, CAMZOOM2_USE_XCLKB); +} + +struct imx046_platform_data zoom2_imx046_platform_data = { + .power_set = imx046_sensor_power_set, + .priv_data_set = imx046_sensor_set_prv_data, + .set_xclk = imx046_sensor_set_xclk, + .csi2_lane_count = isp_csi2_complexio_lanes_count, + .csi2_cfg_vp_out_ctrl = isp_csi2_ctrl_config_vp_out_ctrl, + .csi2_ctrl_update = isp_csi2_ctrl_update, + .csi2_cfg_virtual_id = isp_csi2_ctx_config_virtual_id, + .csi2_ctx_update = isp_csi2_ctx_update, + .csi2_calc_phy_cfg0 = isp_csi2_calc_phy_cfg0, +}; +#endif + +static int zoom2_cam_probe(struct platform_device *pdev) +{ + int ret = 0; + + /* Request and configure gpio pins */ + if (gpio_request(IMX046_STANDBY_GPIO, "imx046_standby_gpio") != 0) { + dev_err(&pdev->dev, "Could not request GPIO %d", + IMX046_STANDBY_GPIO); + ret = -ENODEV; + goto err; + } + + if (gpio_request(IMX046_RESET_GPIO, "imx046_rst") != 0) { + dev_err(&pdev->dev, "Could not request GPIO %d", + IMX046_RESET_GPIO); + ret = -ENODEV; + goto err_freegpio1; + } + + if (gpio_request(LV8093_PS_GPIO, "lv8093_ps") != 0) { + dev_err(&pdev->dev, "Could not request GPIO %d", + LV8093_PS_GPIO); + ret = -ENODEV; + goto err_freegpio2; + } + + /* set to output mode */ + gpio_direction_output(IMX046_STANDBY_GPIO, true); + gpio_direction_output(IMX046_RESET_GPIO, true); + gpio_direction_output(LV8093_PS_GPIO, true); + + cam_inited = 1; + zoom2cam_dev = &pdev->dev; + return 0; + +err_freegpio2: + gpio_free(IMX046_RESET_GPIO); +err_freegpio1: + gpio_free(IMX046_STANDBY_GPIO); +err: + cam_inited = 0; + return ret; +} + +static int zoom2_cam_remove(struct platform_device *pdev) +{ + if (regulator_is_enabled(zoom2_imx046_reg1)) + regulator_disable(zoom2_imx046_reg1); + regulator_put(zoom2_imx046_reg1); + if (regulator_is_enabled(zoom2_imx046_reg2)) + regulator_disable(zoom2_imx046_reg2); + regulator_put(zoom2_imx046_reg2); + + gpio_free(IMX046_STANDBY_GPIO); + gpio_free(IMX046_RESET_GPIO); + gpio_free(LV8093_PS_GPIO); + return 0; +} + +static int zoom2_cam_suspend(struct device *dev) +{ + return 0; +} + +static int zoom2_cam_resume(struct device *dev) +{ + return 0; +} + +static struct dev_pm_ops zoom2_cam_pm_ops = { + .suspend = zoom2_cam_suspend, + .resume = zoom2_cam_resume, +}; + +static struct platform_driver zoom2_cam_driver = { + .probe = zoom2_cam_probe, + .remove = zoom2_cam_remove, + .driver = { + .name = "zoom2_cam", + .pm = &zoom2_cam_pm_ops, + }, +}; + +void __init zoom2_cam_init(void) +{ + cam_inited = 0; + platform_driver_register(&zoom2_cam_driver); +} + diff --git a/arch/arm/mach-omap2/mmc-am3517evm.c b/arch/arm/mach-omap2/mmc-am3517evm.c new file mode 100644 index 00000000000..4124a0d533b --- /dev/null +++ b/arch/arm/mach-omap2/mmc-am3517evm.c @@ -0,0 +1,265 @@ +/* + * linux/arch/arm/mach-omap2/mmc-am3517evm.c + * + * Copyright (C) 2007-2008 Texas Instruments + * Copyright (C) 2008 Nokia Corporation + * Author: Texas Instruments + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "mmc-am3517evm.h" + +#define LDO_CLR 0x00 +#define VSEL_S2_CLR 0x40 + +#define VMMC1_DEV_GRP 0x27 +#define VMMC1_CLR 0x00 +#define VMMC1_315V 0x03 +#define VMMC1_300V 0x02 +#define VMMC1_285V 0x01 +#define VMMC1_185V 0x00 +#define VMMC1_DEDICATED 0x2A + +#define VMMC2_DEV_GRP 0x2B +#define VMMC2_CLR 0x40 +#define VMMC2_315V 0x0c +#define VMMC2_300V 0x0b +#define VMMC2_285V 0x0a +#define VMMC2_260V 0x08 +#define VMMC2_185V 0x06 +#define VMMC2_DEDICATED 0x2E + +#define VMMC_DEV_GRP_P1 0x20 + +#define HSMMC_NAME_LEN 9 + +#if defined(CONFIG_REGULATOR) || \ + (defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \ + defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)) + +/* + * MMC definitions + * + */ +static struct mmc_controller { + struct omap_mmc_platform_data *mmc; + u8 vmmc_dev_grp; + u8 vmmc_dedicated; + char name[HSMMC_NAME_LEN]; +} hsmmc[] = { + { + .vmmc_dev_grp = VMMC1_DEV_GRP, + .vmmc_dedicated = VMMC1_DEDICATED, + }, + { + .vmmc_dev_grp = VMMC2_DEV_GRP, + .vmmc_dedicated = VMMC2_DEDICATED, + }, +}; + +static int mmc_card_detect(int irq) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(hsmmc); i++) { + struct omap_mmc_platform_data *mmc; + + mmc = hsmmc[i].mmc; + if (!mmc) + continue; + if (irq != mmc->slots[0].card_detect_irq) + continue; + + /* NOTE: assumes card detect signal is active-low */ + return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); + } + return -ENOSYS; +} + +static int mmc_get_ro(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + /* NOTE: assumes write protect signal is active-high */ + return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); +} + +/* + * MMC Slot Initialization. + */ +static int mmc_late_init(struct device *dev) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + int ret = 0; + int i; + + ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd"); + if (ret) + goto done; + ret = gpio_direction_input(mmc->slots[0].switch_pin); + if (ret) + goto err; + + for (i = 0; i < ARRAY_SIZE(hsmmc); i++) { + if (hsmmc[i].name == mmc->slots[0].name) { + hsmmc[i].mmc = mmc; + break; + } + } + + return 0; + +err: + gpio_free(mmc->slots[0].switch_pin); +done: + mmc->slots[0].card_detect_irq = 0; + mmc->slots[0].card_detect = NULL; + + dev_err(dev, "err %d configuring card detect\n", ret); + return ret; +} + +static void mmc_cleanup(struct device *dev) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + gpio_free(mmc->slots[0].switch_pin); +} + +#ifdef CONFIG_PM + +static int mmc_suspend(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + disable_irq(mmc->slots[0].card_detect_irq); + return 0; +} + +static int mmc_resume(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + enable_irq(mmc->slots[0].card_detect_irq); + return 0; +} + +#else +#define mmc_suspend NULL +#define mmc_resume NULL +#endif + +/* + * the MMC power setting function + */ + +static int mmc1_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + return 0; +} + +static int mmc2_set_power(struct device *dev, int slot, int power_on, int vdd) +{ + return 0; +} + +static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata; + +void __init am3517_mmc_init(struct am3517_hsmmc_info *controllers) +{ + struct am3517_hsmmc_info *c; + int nr_hsmmc = ARRAY_SIZE(hsmmc_data); + + for (c = controllers; c->mmc; c++) { + struct mmc_controller *mmc_control = hsmmc + c->mmc - 1; + struct omap_mmc_platform_data *mmc = hsmmc_data[c->mmc - 1]; + + if (!c->mmc || c->mmc > nr_hsmmc) { + pr_debug("MMC%d: no such controller\n", c->mmc); + continue; + } + if (mmc) { + pr_debug("MMC%d: already configured\n", c->mmc); + continue; + } + + mmc = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL); + if (!mmc) { + pr_err("Cannot allocate memory for mmc device!\n"); + return; + } + + sprintf(mmc_control->name, "mmc%islot%i", c->mmc, 1); + mmc->slots[0].name = mmc_control->name; + mmc->nr_slots = 1; + mmc->slots[0].ocr_mask = MMC_VDD_165_195 | + MMC_VDD_26_27 | MMC_VDD_27_28 | + MMC_VDD_29_30 | + MMC_VDD_30_31 | MMC_VDD_31_32; + mmc->slots[0].wires = c->wires; + mmc->slots[0].internal_clock = !c->ext_clock; + mmc->dma_mask = 0xffffffff; + + if (1) { + mmc->init = mmc_late_init; + mmc->cleanup = mmc_cleanup; + mmc->suspend = mmc_suspend; + mmc->resume = mmc_resume; + + mmc->slots[0].switch_pin = c->gpio_cd; + mmc->slots[0].card_detect_irq = gpio_to_irq(c->gpio_cd); + mmc->slots[0].card_detect = mmc_card_detect; + } else + mmc->slots[0].switch_pin = -EINVAL; + + /* write protect normally uses an OMAP gpio */ + if (gpio_is_valid(c->gpio_wp)) { + gpio_request(c->gpio_wp, "mmc_wp"); + gpio_direction_input(c->gpio_wp); + + mmc->slots[0].gpio_wp = c->gpio_wp; + mmc->slots[0].get_ro = mmc_get_ro; + } else + mmc->slots[0].gpio_wp = -EINVAL; + + /* NOTE: we assume OMAP's MMC1 and MMC2 use + * the TWL4030's VMMC1 and VMMC2, respectively; + * and that OMAP's MMC3 isn't used. + */ + + switch (c->mmc) { + case 1: + mmc->slots[0].set_power = mmc1_set_power; + break; + case 2: + mmc->slots[0].set_power = mmc2_set_power; + break; + default: + pr_err("MMC%d configuration not supported!\n", c->mmc); + continue; + } + hsmmc_data[c->mmc - 1] = mmc; + } + + omap2_init_mmc(hsmmc_data, OMAP34XX_NR_MMC); +} +#else +inline void am3517_mmc_init(struct am3517_hsmmc_info *info) +{ +} +#endif diff --git a/arch/arm/mach-omap2/mmc-am3517evm.h b/arch/arm/mach-omap2/mmc-am3517evm.h new file mode 100644 index 00000000000..bf2d3392020 --- /dev/null +++ b/arch/arm/mach-omap2/mmc-am3517evm.h @@ -0,0 +1,22 @@ +/* + * MMC definitions for OMAP3517 / AM3517 + * + * 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. + */ + +struct am3517_hsmmc_info { + u8 mmc; /* controller 1/2/3 */ + u8 wires; /* 1/4/8 wires */ + bool transceiver; /* MMC-2 option */ + bool ext_clock; /* use external pin for input clock */ + bool cover_only; /* No card detect - just cover switch */ + int gpio_cd; /* or -EINVAL */ + int gpio_wp; /* or -EINVAL */ + char *name; /* or NULL for default */ + struct device *dev; /* returned: pointer to mmc adapter */ + int ocr_mask; /* temporary HACK */ +}; + +void am3517_mmc_init(struct am3517_hsmmc_info *); diff --git a/arch/arm/mach-omap2/sleep3517.S b/arch/arm/mach-omap2/sleep3517.S new file mode 100644 index 00000000000..4a785d8b687 --- /dev/null +++ b/arch/arm/mach-omap2/sleep3517.S @@ -0,0 +1,147 @@ +/* +/* linux/arch/arm/mach-omap2/sleep3517.S + * + * AM3505/3517 Sleep Code. + * Ranjith Lohithakshan + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "cm.h" + +#define CM_IDLEST1_CORE_V OMAP34XX_CM_REGADDR(CORE_MOD, CM_IDLEST1) +#define CM_CLKST_CORE_V OMAP34XX_CM_REGADDR(CORE_MOD, OMAP3430_CM_CLKSTST) +#define CM_ICLKEN1_CORE_V OMAP34XX_CM_REGADDR(CORE_MOD, CM_ICLKEN1) + +#define EMIF_PM_CTR_V OMAP2_L3_IO_ADDRESS(0x6D000038) +#define OMAP3517_CONF1_REG_V OMAP2_L4_IO_ADDRESS(0x48002584) + +/* + * Forces OMAP into idle state + * + * omap34xx_suspend() - This bit of code just executes the WFI + * for normal idles. + * + * Note: This code get's copied to internal SRAM at boot. When the OMAP + * wakes up it continues execution at the point it went to sleep. + */ +ENTRY(omap34xx_cpu_suspend) + stmfd sp!, {r0-r12, lr} @ save registers on stack +loop: + /*b loop*/ @Enable to debug by stepping through code + + /* Put EMIF in self-refresh */ + ldr r4, emif_pm_ctrl + ldr r5, [r4] + orr r5, r5, #0x200 + str r5, [r4] + + /* Disable SDRC and Control Module */ + ldr r4, cm_iclken1_core + ldr r5, clk_core_disable + str r5, [r4] +wait_sdrc_ok: + ldr r4, cm_idlest1_core + ldr r5, [r4] + and r5, r5, #0x2 + cmp r5, #0x2 + bne wait_sdrc_ok + + /* Gate DDR Phy clock */ + ldr r4, omap3517_conf1 + ldr r5, emif_phy_gate + str r5, [r4] + + /* Data memory barrier and Data sync barrier */ + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 + mcr p15, 0, r1, c7, c10, 5 + + wfi + + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + /* Enable SDRC and Control Module */ + ldr r4, cm_iclken1_core + ldr r5, iclk_core_enable + str r5, [r4] + + /* Enable DDR Phy Clock */ + ldr r4, omap3517_conf1 + ldr r5, emif_phy_enable + str r5, [r4] + + /* Take EMIF out of self-refresh */ + ldr r4, emif_pm_ctrl + ldr r5, [r4] + bic r5, r5, #0x200 + str r5, [r4] + + ldmfd sp!, {r0-r12, pc} @ restore regs and return + +clk_core_disable: + .word 0x0 +iclk_core_enable: + .word 0x42 +emif_phy_gate: + .word 0x2620 +emif_phy_enable: + .word 0x8620 +cm_idlest1_core: + .word CM_IDLEST1_CORE_V +cm_clkst_core: + .word CM_CLKST_CORE_V +emif_pm_ctrl: + .word EMIF_PM_CTR_V +cm_iclken1_core: + .word CM_ICLKEN1_CORE_V +omap3517_conf1: + .word OMAP3517_CONF1_REG_V +ENTRY(omap34xx_cpu_suspend_sz) + .word . - omap34xx_cpu_suspend + +/* Function to call rom code to save secure ram context */ +ENTRY(save_secure_ram_context) + stmfd sp!, {r1-r12, lr} @ save registers on stack +save_secure_ram_debug: + /* b save_secure_ram_debug */ @ enable to debug save code + ldmfd sp!, {r1-r12, pc} +ENTRY(save_secure_ram_context_sz) + .word . - save_secure_ram_context + +/* Function call to get the restore pointer for resume from OFF */ +ENTRY(get_restore_pointer) + stmfd sp!, {lr} @ save registers on stack + ldmfd sp!, {pc} @ restore regs and return +ENTRY(get_restore_pointer_sz) + .word . - get_restore_pointer + +/* Function call to get the restore pointer for resume from OFF */ +ENTRY(get_es3_restore_pointer) + stmfd sp!, {lr} @ save registers on stack + ldmfd sp!, {pc} @ restore regs and return +ENTRY(get_es3_restore_pointer_sz) + .word . - get_es3_restore_pointer + diff --git a/arch/arm/plat-omap/include/plat/am3517.h b/arch/arm/plat-omap/include/plat/am3517.h new file mode 100644 index 00000000000..3359ffad6ac --- /dev/null +++ b/arch/arm/plat-omap/include/plat/am3517.h @@ -0,0 +1,61 @@ +/*: + * Address mappings and base address for AM3517 specific interconnects + * and peripherals. + * + * Copyright (C) 2009 Texas Instruments + * + * Author: Sriramakrishnan + * + * 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. + */ +#ifndef __ASM_ARCH_AM3517_H +#define __ASM_ARCH_AM3517_H + +/* + * ---------------------------------------------------------------------------- + * Base addresses + * ---------------------------------------------------------------------------- + */ + +#define AM3517_IPSS_EMAC_BASE 0x5C000000 +#define AM3517_IPSS_USBOTGSS_BASE 0x5C040000 +#define AM3517_IPSS_HECC_BASE 0x5C050000 +#define AM3517_IPSS_VPFE_BASE 0x5C060000 + +/*AM3517 CONTROL_LVL_INTR_CLEAR bits*/ +#define AM3517_CPGMAC_MISC_PULSE_CLR BIT(0) +#define AM3517_CPGMAC_RX_PULSE_CLR BIT(1) +#define AM3517_CPGMAC_RX_THRESH_CLR BIT(2) +#define AM3517_CPGMAC_TX_PULSE_CLR BIT(3) +#define AM3517_USBOTG_INT_CLR BIT(4) +#define AM3517_VPFE_VD0_INT_CLR BIT(5) +#define AM3517_VPFE_VD1_INT_CLR BIT(6) +#define AM3517_VPFE_VD2_INT_CLR BIT(7) + +/*AM3517 CONTROL_IP_SW_RESET bits*/ +#define AM3517_USBOTG_SW_RST BIT(0) +#define AM3517_CPGMAC_SW_RST BIT(1) +#define AM3517_VPFE_VBUSP_SW_RST BIT(2) +#define AM3517_HECC_SW_RST BIT(3) +#define AM3517_VPFE_PCLK_SW_RST BIT(4) + +#define AM3517_EMAC_CNTRL_OFFSET (0x10000) +#define AM3517_EMAC_CNTRL_MOD_OFFSET (0x0) +#define AM3517_EMAC_CNTRL_RAM_OFFSET (0x20000) +#define AM3517_EMAC_MDIO_OFFSET (0x30000) +#define AM3517_EMAC_CNTRL_RAM_SIZE (0x2000) +#define AM3517_EMAC_RAM_ADDR (AM3517_EMAC_BASE + \ + AM3517_EMAC_CNTRL_RAM_OFFSET) +#define AM3517_EMAC_HW_RAM_ADDR (0x01E20000) + +#define AM3517_HECC_SCC_HECC_OFFSET (0x0) +#define AM3517_HECC_SCC_RAM_OFFSET (0x3000) +#define AM3517_HECC_RAM_OFFSET (0x3000) +#define AM3517_HECC_MBOX_OFFSET (0x2000) +#define AM3517_HECC_INT_LINE (0x0) +#define AM3517_HECC_VERSION (0x1) + + +#endif /* __ASM_ARCH_AM3517_H */ diff --git a/arch/arm/plat-omap/include/plat/display.h b/arch/arm/plat-omap/include/plat/display.h new file mode 100644 index 00000000000..c66e464732d --- /dev/null +++ b/arch/arm/plat-omap/include/plat/display.h @@ -0,0 +1,575 @@ +/* + * linux/include/asm-arm/arch-omap/display.h + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#ifndef __ASM_ARCH_OMAP_DISPLAY_H +#define __ASM_ARCH_OMAP_DISPLAY_H + +#include +#include +#include +#include + +#define DISPC_IRQ_FRAMEDONE (1 << 0) +#define DISPC_IRQ_VSYNC (1 << 1) +#define DISPC_IRQ_EVSYNC_EVEN (1 << 2) +#define DISPC_IRQ_EVSYNC_ODD (1 << 3) +#define DISPC_IRQ_ACBIAS_COUNT_STAT (1 << 4) +#define DISPC_IRQ_PROG_LINE_NUM (1 << 5) +#define DISPC_IRQ_GFX_FIFO_UNDERFLOW (1 << 6) +#define DISPC_IRQ_GFX_END_WIN (1 << 7) +#define DISPC_IRQ_PAL_GAMMA_MASK (1 << 8) +#define DISPC_IRQ_OCP_ERR (1 << 9) +#define DISPC_IRQ_VID1_FIFO_UNDERFLOW (1 << 10) +#define DISPC_IRQ_VID1_END_WIN (1 << 11) +#define DISPC_IRQ_VID2_FIFO_UNDERFLOW (1 << 12) +#define DISPC_IRQ_VID2_END_WIN (1 << 13) +#define DISPC_IRQ_SYNC_LOST (1 << 14) +#define DISPC_IRQ_SYNC_LOST_DIGIT (1 << 15) +#define DISPC_IRQ_WAKEUP (1 << 16) + +struct omap_dss_device; +struct omap_overlay_manager; + +enum omap_display_type { + OMAP_DISPLAY_TYPE_NONE = 0, + OMAP_DISPLAY_TYPE_DPI = 1 << 0, + OMAP_DISPLAY_TYPE_DBI = 1 << 1, + OMAP_DISPLAY_TYPE_SDI = 1 << 2, + OMAP_DISPLAY_TYPE_DSI = 1 << 3, + OMAP_DISPLAY_TYPE_VENC = 1 << 4, +}; + +enum omap_plane { + OMAP_DSS_GFX = 0, + OMAP_DSS_VIDEO1 = 1, + OMAP_DSS_VIDEO2 = 2 +}; + +enum omap_channel { + OMAP_DSS_CHANNEL_LCD = 0, + OMAP_DSS_CHANNEL_DIGIT = 1, +}; + +enum omap_color_mode { + OMAP_DSS_COLOR_CLUT1 = 1 << 0, /* BITMAP 1 */ + OMAP_DSS_COLOR_CLUT2 = 1 << 1, /* BITMAP 2 */ + OMAP_DSS_COLOR_CLUT4 = 1 << 2, /* BITMAP 4 */ + OMAP_DSS_COLOR_CLUT8 = 1 << 3, /* BITMAP 8 */ + OMAP_DSS_COLOR_RGB12U = 1 << 4, /* RGB12, 16-bit container */ + OMAP_DSS_COLOR_ARGB16 = 1 << 5, /* ARGB16 */ + OMAP_DSS_COLOR_RGB16 = 1 << 6, /* RGB16 */ + OMAP_DSS_COLOR_RGB24U = 1 << 7, /* RGB24, 32-bit container */ + OMAP_DSS_COLOR_RGB24P = 1 << 8, /* RGB24, 24-bit container */ + OMAP_DSS_COLOR_YUV2 = 1 << 9, /* YUV2 4:2:2 co-sited */ + OMAP_DSS_COLOR_UYVY = 1 << 10, /* UYVY 4:2:2 co-sited */ + OMAP_DSS_COLOR_ARGB32 = 1 << 11, /* ARGB32 */ + OMAP_DSS_COLOR_RGBA32 = 1 << 12, /* RGBA32 */ + OMAP_DSS_COLOR_RGBX32 = 1 << 13, /* RGBx32 */ + + OMAP_DSS_COLOR_GFX_OMAP2 = + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 | + OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P, + + OMAP_DSS_COLOR_VID_OMAP2 = + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | + OMAP_DSS_COLOR_UYVY, + + OMAP_DSS_COLOR_GFX_OMAP3 = + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, + + OMAP_DSS_COLOR_VID1_OMAP3 = + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 | + OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY, + + OMAP_DSS_COLOR_VID2_OMAP3 = + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | + OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, +}; + +enum omap_lcd_display_type { + OMAP_DSS_LCD_DISPLAY_STN, + OMAP_DSS_LCD_DISPLAY_TFT, +}; + +enum omap_dss_load_mode { + OMAP_DSS_LOAD_CLUT_AND_FRAME = 0, + OMAP_DSS_LOAD_CLUT_ONLY = 1, + OMAP_DSS_LOAD_FRAME_ONLY = 2, + OMAP_DSS_LOAD_CLUT_ONCE_FRAME = 3, +}; + +enum omap_dss_trans_key_type { + OMAP_DSS_COLOR_KEY_GFX_DST = 0, + OMAP_DSS_COLOR_KEY_VID_SRC = 1, +}; + +enum omap_rfbi_te_mode { + OMAP_DSS_RFBI_TE_MODE_1 = 1, + OMAP_DSS_RFBI_TE_MODE_2 = 2, +}; + +enum omap_panel_config { + OMAP_DSS_LCD_IVS = 1<<0, + OMAP_DSS_LCD_IHS = 1<<1, + OMAP_DSS_LCD_IPC = 1<<2, + OMAP_DSS_LCD_IEO = 1<<3, + OMAP_DSS_LCD_RF = 1<<4, + OMAP_DSS_LCD_ONOFF = 1<<5, + + OMAP_DSS_LCD_TFT = 1<<20, +}; + +enum omap_dss_venc_type { + OMAP_DSS_VENC_TYPE_COMPOSITE, + OMAP_DSS_VENC_TYPE_SVIDEO, +}; + +enum omap_display_caps { + OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE = 1 << 0, + OMAP_DSS_DISPLAY_CAP_TEAR_ELIM = 1 << 1, +}; + +enum omap_dss_update_mode { + OMAP_DSS_UPDATE_DISABLED = 0, + OMAP_DSS_UPDATE_AUTO, + OMAP_DSS_UPDATE_MANUAL, +}; + +enum omap_dss_display_state { + OMAP_DSS_DISPLAY_DISABLED = 0, + OMAP_DSS_DISPLAY_ACTIVE, + OMAP_DSS_DISPLAY_SUSPENDED, +}; + +/* XXX perhaps this should be removed */ +enum omap_dss_overlay_managers { + OMAP_DSS_OVL_MGR_LCD, + OMAP_DSS_OVL_MGR_TV, +}; + +enum omap_dss_rotation_type { + OMAP_DSS_ROT_DMA = 0, + OMAP_DSS_ROT_VRFB = 1, +}; + +/* clockwise rotation angle */ +enum omap_dss_rotation_angle { + OMAP_DSS_ROT_0 = 0, + OMAP_DSS_ROT_90 = 1, + OMAP_DSS_ROT_180 = 2, + OMAP_DSS_ROT_270 = 3, +}; + +enum omap_overlay_caps { + OMAP_DSS_OVL_CAP_SCALE = 1 << 0, + OMAP_DSS_OVL_CAP_DISPC = 1 << 1, +}; + +enum omap_overlay_manager_caps { + OMAP_DSS_OVL_MGR_CAP_DISPC = 1 << 0, +}; + +/* RFBI */ + +struct rfbi_timings { + int cs_on_time; + int cs_off_time; + int we_on_time; + int we_off_time; + int re_on_time; + int re_off_time; + int we_cycle_time; + int re_cycle_time; + int cs_pulse_width; + int access_time; + + int clk_div; + + u32 tim[5]; /* set by rfbi_convert_timings() */ + + int converted; +}; + +void omap_rfbi_write_command(const void *buf, u32 len); +void omap_rfbi_read_data(void *buf, u32 len); +void omap_rfbi_write_data(const void *buf, u32 len); +void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, + u16 x, u16 y, + u16 w, u16 h); +int omap_rfbi_enable_te(bool enable, unsigned line); +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); + +/* DSI */ +void dsi_bus_lock(void); +void dsi_bus_unlock(void); +int dsi_vc_dcs_write(int channel, u8 *data, int len); +int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len); +int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen); +int dsi_vc_set_max_rx_packet_size(int channel, u16 len); +int dsi_vc_send_null(int channel); +int dsi_vc_send_bta_sync(int channel); + +/* Board specific data */ +struct omap_dss_board_info { + int (*get_last_off_on_transaction_id)(struct device *dev); + int num_devices; + struct omap_dss_device **devices; + struct omap_dss_device *default_device; +}; + +struct omap_video_timings { + /* Unit: pixels */ + u16 x_res; + /* Unit: pixels */ + u16 y_res; + /* Unit: KHz */ + u32 pixel_clock; + /* Unit: pixel clocks */ + u16 hsw; /* Horizontal synchronization pulse width */ + /* Unit: pixel clocks */ + u16 hfp; /* Horizontal front porch */ + /* Unit: pixel clocks */ + u16 hbp; /* Horizontal back porch */ + /* Unit: line clocks */ + u16 vsw; /* Vertical synchronization pulse width */ + /* Unit: line clocks */ + u16 vfp; /* Vertical front porch */ + /* Unit: line clocks */ + u16 vbp; /* Vertical back porch */ +}; + +#ifdef CONFIG_OMAP2_DSS_VENC +/* Hardcoded timings for tv modes. Venc only uses these to + * identify the mode, and does not actually use the configs + * itself. However, the configs should be something that + * a normal monitor can also show */ +const extern struct omap_video_timings omap_dss_pal_timings; +const extern struct omap_video_timings omap_dss_ntsc_timings; +#endif + +struct omap_overlay_info { + 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; +}; + +struct omap_overlay { + struct kobject kobj; + struct list_head list; + + /* static fields */ + const char *name; + int id; + enum omap_color_mode supported_modes; + enum omap_overlay_caps caps; + + /* dynamic fields */ + struct omap_overlay_manager *manager; + struct omap_overlay_info info; + + /* if true, info has been changed, but not applied() yet */ + bool info_dirty; + + int (*set_manager)(struct omap_overlay *ovl, + struct omap_overlay_manager *mgr); + int (*unset_manager)(struct omap_overlay *ovl); + + int (*set_overlay_info)(struct omap_overlay *ovl, + struct omap_overlay_info *info); + void (*get_overlay_info)(struct omap_overlay *ovl, + struct omap_overlay_info *info); + + int (*wait_for_go)(struct omap_overlay *ovl); +}; + +struct omap_overlay_manager_info { + u32 default_color; + + enum omap_dss_trans_key_type trans_key_type; + u32 trans_key; + bool trans_enabled; + + bool alpha_enabled; +}; + +struct omap_overlay_manager { + struct kobject kobj; + struct list_head list; + + /* static fields */ + const char *name; + int id; + enum omap_overlay_manager_caps caps; + int num_overlays; + struct omap_overlay **overlays; + enum omap_display_type supported_displays; + + /* dynamic fields */ + struct omap_dss_device *device; + struct omap_overlay_manager_info info; + + bool device_changed; + /* if true, info has been changed but not applied() yet */ + bool info_dirty; + + int (*set_device)(struct omap_overlay_manager *mgr, + struct omap_dss_device *dssdev); + int (*unset_device)(struct omap_overlay_manager *mgr); + + int (*set_manager_info)(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info); + void (*get_manager_info)(struct omap_overlay_manager *mgr, + struct omap_overlay_manager_info *info); + + int (*apply)(struct omap_overlay_manager *mgr); + int (*wait_for_go)(struct omap_overlay_manager *mgr); +}; + +struct omap_dss_device { + struct device dev; + + enum omap_display_type type; + + union { + struct { + u8 data_lines; + } dpi; + + struct { + u8 channel; + u8 data_lines; + } rfbi; + + struct { + u8 datapairs; + } sdi; + + struct { + u8 clk_lane; + u8 clk_pol; + u8 data1_lane; + u8 data1_pol; + u8 data2_lane; + u8 data2_pol; + + struct { + u16 regn; + u16 regm; + u16 regm3; + u16 regm4; + + u16 lp_clk_div; + + u16 lck_div; + u16 pck_div; + } div; + + bool ext_te; + u8 ext_te_gpio; + } dsi; + + struct { + enum omap_dss_venc_type type; + bool invert_polarity; + } venc; + } phy; + + struct { + struct omap_video_timings timings; + + int acbi; /* ac-bias pin transitions per interrupt */ + /* Unit: line clocks */ + int acb; /* ac-bias pin frequency */ + + enum omap_panel_config config; + + u8 recommended_bpp; + + struct omap_dss_device *ctrl; + } panel; + + struct { + u8 pixel_size; + struct rfbi_timings rfbi_timings; + struct omap_dss_device *panel; + } ctrl; + + int reset_gpio; + + int max_backlight_level; + + const char *name; + + /* used to match device to driver */ + const char *driver_name; + + void *data; + + struct omap_dss_driver *driver; + + /* helper variable for driver suspend/resume */ + bool activate_after_resume; + + enum omap_display_caps caps; + + struct omap_overlay_manager *manager; + + enum omap_dss_display_state state; + + int (*enable)(struct omap_dss_device *dssdev); + void (*disable)(struct omap_dss_device *dssdev); + + int (*suspend)(struct omap_dss_device *dssdev); + int (*resume)(struct omap_dss_device *dssdev); + + void (*get_resolution)(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres); + int (*get_recommended_bpp)(struct omap_dss_device *dssdev); + + int (*check_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + void (*set_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + void (*get_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + int (*update)(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h); + int (*sync)(struct omap_dss_device *dssdev); + int (*wait_vsync)(struct omap_dss_device *dssdev); + + int (*set_update_mode)(struct omap_dss_device *dssdev, + enum omap_dss_update_mode); + enum omap_dss_update_mode (*get_update_mode) + (struct omap_dss_device *dssdev); + + int (*enable_te)(struct omap_dss_device *dssdev, bool enable); + int (*get_te)(struct omap_dss_device *dssdev); + + u8 (*get_rotate)(struct omap_dss_device *dssdev); + int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate); + + bool (*get_mirror)(struct omap_dss_device *dssdev); + int (*set_mirror)(struct omap_dss_device *dssdev, bool enable); + + int (*run_test)(struct omap_dss_device *dssdev, int test); + int (*memory_read)(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h); + + int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); + u32 (*get_wss)(struct omap_dss_device *dssdev); + + /* platform specific */ + int (*platform_enable)(struct omap_dss_device *dssdev); + void (*platform_disable)(struct omap_dss_device *dssdev); + int (*set_backlight)(struct omap_dss_device *dssdev, int level); + int (*get_backlight)(struct omap_dss_device *dssdev); +}; + +struct omap_dss_driver { + struct device_driver driver; + + int (*probe)(struct omap_dss_device *); + void (*remove)(struct omap_dss_device *); + + int (*enable)(struct omap_dss_device *display); + void (*disable)(struct omap_dss_device *display); + int (*suspend)(struct omap_dss_device *display); + int (*resume)(struct omap_dss_device *display); + int (*run_test)(struct omap_dss_device *display, int test); + + void (*setup_update)(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h); + + int (*enable_te)(struct omap_dss_device *dssdev, bool enable); + int (*wait_for_te)(struct omap_dss_device *dssdev); + + u8 (*get_rotate)(struct omap_dss_device *dssdev); + int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate); + + bool (*get_mirror)(struct omap_dss_device *dssdev); + int (*set_mirror)(struct omap_dss_device *dssdev, bool enable); + + int (*memory_read)(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h); +}; + +int omap_dss_register_driver(struct omap_dss_driver *); +void omap_dss_unregister_driver(struct omap_dss_driver *); + +int omap_dss_register_device(struct omap_dss_device *); +void omap_dss_unregister_device(struct omap_dss_device *); + +void omap_dss_get_device(struct omap_dss_device *dssdev); +void omap_dss_put_device(struct omap_dss_device *dssdev); +#define for_each_dss_dev(d) while ((d = omap_dss_get_next_device(d)) != NULL) +struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from); +struct omap_dss_device *omap_dss_find_device(void *data, + int (*match)(struct omap_dss_device *dssdev, void *data)); + +int omap_dss_start_device(struct omap_dss_device *dssdev); +void omap_dss_stop_device(struct omap_dss_device *dssdev); + +int omap_dss_get_num_overlay_managers(void); +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num); + +int omap_dss_get_num_overlays(void); +struct omap_overlay *omap_dss_get_overlay(int num); + +typedef void (*omap_dispc_isr_t) (void *arg, u32 mask); +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask); +int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask); + +int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout); +int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, + unsigned long timeout); + +#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver) +#define to_dss_device(x) container_of((x), struct omap_dss_device, dev) + +#endif diff --git a/arch/arm/plat-omap/include/plat/isp_user.h b/arch/arm/plat-omap/include/plat/isp_user.h new file mode 100644 index 00000000000..4be40742683 --- /dev/null +++ b/arch/arm/plat-omap/include/plat/isp_user.h @@ -0,0 +1,688 @@ +/* + * isp_user.h + * + * Include file for OMAP ISP module in TI's OMAP3. + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Mohit Jalori + * Sergio Aguirre + * + * 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 OMAP_ISP_USER_H +#define OMAP_ISP_USER_H + +#include +#include + +/* ISP Private IOCTLs */ +#define VIDIOC_PRIVATE_ISP_CCDC_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct ispccdc_update_config) +#define VIDIOC_PRIVATE_ISP_PRV_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct ispprv_update_config) +#define VIDIOC_PRIVATE_ISP_AEWB_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct isph3a_aewb_config) +#define VIDIOC_PRIVATE_ISP_AEWB_REQ \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct isph3a_aewb_data) +#define VIDIOC_PRIVATE_ISP_HIST_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct isp_hist_config) +#define VIDIOC_PRIVATE_ISP_HIST_REQ \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct isp_hist_data) +#define VIDIOC_PRIVATE_ISP_AF_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct af_configuration) +#define VIDIOC_PRIVATE_ISP_AF_REQ \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct isp_af_data) +#define VIDIOC_PRIVATE_OMAP34XXCAM_SENSOR_INFO \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct omap34xxcam_sensor_info) + +/* Structre for getting Sensor Information*/ +struct omap34xxcam_sensor_info { + __u32 current_xclk; + struct v4l2_pix_format active_size; + struct v4l2_pix_format full_size; +}; + +/* AE/AWB related structures and flags*/ + +/* Flags for update field */ +#define REQUEST_STATISTICS (1 << 0) +#define SET_COLOR_GAINS (1 << 1) +#define SET_DIGITAL_GAIN (1 << 2) +#define SET_EXPOSURE (1 << 3) +#define SET_ANALOG_GAIN (1 << 4) + +#define MAX_FRAME_COUNT 0x0FFF +#define MAX_FUTURE_FRAMES 10 + +#define MAX_SATURATION_LIM 1023 +#define MIN_WIN_H 2 +#define MAX_WIN_H 256 +#define MIN_WIN_W 6 +#define MAX_WIN_W 256 +#define MAX_WINVC 128 +#define MAX_WINHC 36 +#define MAX_WINSTART 4095 +#define MIN_SUB_INC 2 +#define MAX_SUB_INC 32 + +/* Range Constants */ +#define AF_IIRSH_MIN 0 +#define AF_IIRSH_MAX 4094 +#define AF_PAXEL_HORIZONTAL_COUNT_MIN 0 +#define AF_PAXEL_HORIZONTAL_COUNT_MAX 35 +#define AF_PAXEL_VERTICAL_COUNT_MIN 0 +#define AF_PAXEL_VERTICAL_COUNT_MAX 127 +#define AF_PAXEL_INCREMENT_MIN 0 +#define AF_PAXEL_INCREMENT_MAX 14 +#define AF_PAXEL_HEIGHT_MIN 0 +#define AF_PAXEL_HEIGHT_MAX 127 +#define AF_PAXEL_WIDTH_MIN 0 +#define AF_PAXEL_WIDTH_MAX 127 +#define AF_PAXEL_HZSTART_MIN 2 +#define AF_PAXEL_HZSTART_MAX 4094 + +#define AF_PAXEL_VTSTART_MIN 0 +#define AF_PAXEL_VTSTART_MAX 4095 +#define AF_THRESHOLD_MAX 255 +#define AF_COEF_MAX 4095 +#define AF_PAXEL_SIZE 48 + +/** + * struct isph3a_aewb_config - AE AWB configuration reset values. + * saturation_limit: Saturation limit. + * @win_height: Window Height. Range 2 - 256, even values only. + * @win_width: Window Width. Range 6 - 256, even values only. + * @ver_win_count: Vertical Window Count. Range 1 - 128. + * @hor_win_count: Horizontal Window Count. Range 1 - 36. + * @ver_win_start: Vertical Window Start. Range 0 - 4095. + * @hor_win_start: Horizontal Window Start. Range 0 - 4095. + * @blk_ver_win_start: Black Vertical Windows Start. Range 0 - 4095. + * @blk_win_height: Black Window Height. Range 2 - 256, even values only. + * @subsample_ver_inc: Subsample Vertical points increment Range 2 - 32, even + * values only. + * @subsample_hor_inc: Subsample Horizontal points increment Range 2 - 32, even + * values only. + * @alaw_enable: AEW ALAW EN flag. + * @aewb_enable: AE AWB stats generation EN flag. + */ +struct isph3a_aewb_config { + __u16 saturation_limit; + __u16 win_height; + __u16 win_width; + __u16 ver_win_count; + __u16 hor_win_count; + __u16 ver_win_start; + __u16 hor_win_start; + __u16 blk_ver_win_start; + __u16 blk_win_height; + __u16 subsample_ver_inc; + __u16 subsample_hor_inc; + __u8 alaw_enable; + __u8 aewb_enable; +}; + +/** + * struct isph3a_aewb_data - Structure of data sent to or received from user + * @h3a_aewb_statistics_buf: Pointer to pass to user. + * @shutter: Shutter speed. + * @gain: Sensor analog Gain. + * @shutter_cap: Shutter speed for capture. + * @gain_cap: Sensor Gain for capture. + * @dgain: White balance digital gain. + * @wb_gain_b: White balance color gain blue. + * @wb_gain_r: White balance color gain red. + * @wb_gain_gb: White balance color gain green blue. + * @wb_gain_gr: White balance color gain green red. + * @frame_number: Frame number of requested stats. + * @curr_frame: Current frame number being processed. + * @update: Bitwise flags to update parameters. + * @ts: Timestamp of returned framestats. + * @field_count: Sequence number of returned framestats. + */ +struct isph3a_aewb_data { + void *h3a_aewb_statistics_buf; + __u32 shutter; + __u16 gain; + __u32 shutter_cap; + __u16 gain_cap; + __u16 dgain; + __u16 wb_gain_b; + __u16 wb_gain_r; + __u16 wb_gain_gb; + __u16 wb_gain_gr; + __u16 frame_number; + __u16 curr_frame; + __u8 update; + struct timeval ts; + __u32 config_counter; + unsigned long field_count; +}; + + +/* Histogram related structs */ +/* Flags for number of bins */ +#define BINS_32 0x0 +#define BINS_64 0x1 +#define BINS_128 0x2 +#define BINS_256 0x3 + +struct isp_hist_config { + __u8 hist_source; /* CCDC or Memory */ + __u8 input_bit_width; /* Needed o know the size per pixel */ + __u8 hist_frames; /* Num of frames to be processed and + * accumulated + */ + __u8 hist_h_v_info; /* frame-input width and height if source is + * memory + */ + __u16 hist_radd; /* frame-input address in memory */ + __u16 hist_radd_off; /* line-offset for frame-input */ + __u16 hist_bins; /* number of bins: 32, 64, 128, or 256 */ + __u16 wb_gain_R; /* White Balance Field-to-Pattern Assignments */ + __u16 wb_gain_RG; /* White Balance Field-to-Pattern Assignments */ + __u16 wb_gain_B; /* White Balance Field-to-Pattern Assignments */ + __u16 wb_gain_BG; /* White Balance Field-to-Pattern Assignments */ + __u8 num_regions; /* number of regions to be configured */ + __u16 reg0_hor; /* Region 0 size and position */ + __u16 reg0_ver; /* Region 0 size and position */ + __u16 reg1_hor; /* Region 1 size and position */ + __u16 reg1_ver; /* Region 1 size and position */ + __u16 reg2_hor; /* Region 2 size and position */ + __u16 reg2_ver; /* Region 2 size and position */ + __u16 reg3_hor; /* Region 3 size and position */ + __u16 reg3_ver; /* Region 3 size and position */ +}; + +struct isp_hist_data { + __u32 *hist_statistics_buf; /* Pointer to pass to user */ +}; + +/* Auto Focus related structs */ + +#define AF_NUMBER_OF_COEF 11 + +/* Flags for update field */ +#define REQUEST_STATISTICS (1 << 0) +#define LENS_DESIRED_POSITION (1 << 1) +#define LENS_CURRENT_POSITION (1 << 2) + +/** + * struct isp_af_xtrastats - Extra statistics related to AF generated stats. + * @ts: Timestamp when the frame gets delivered to the user. + * @field_count: Field count of the frame delivered to the user. + * @lens_position: Lens position when the stats are being generated. + */ +struct isp_af_xtrastats { + struct timeval ts; + unsigned long field_count; + __u16 lens_position; /* deprecated */ +}; + +/** + * struct isp_af_data - AF statistics data to transfer between driver and user. + * @af_statistics_buf: Pointer to pass to user. + * @lens_current_position: Read value of lens absolute position. + * @desired_lens_direction: Lens desired location. + * @update: Bitwise flags to update parameters. + * @frame_number: Data for which frame is desired/given. + * @curr_frame: Current frame number being processed by AF module. + * @xtrastats: Extra statistics structure. + */ +struct isp_af_data { + void *af_statistics_buf; + __u16 lens_current_position; /* deprecated */ + __u16 desired_lens_direction; /* deprecated */ + __u16 update; + __u16 frame_number; + __u16 curr_frame; + __u32 config_counter; + struct isp_af_xtrastats xtrastats; +}; + +/* enum used for status of specific feature */ +enum af_alaw_enable { + H3A_AF_ALAW_DISABLE = 0, + H3A_AF_ALAW_ENABLE = 1 +}; + +enum af_hmf_enable { + H3A_AF_HMF_DISABLE = 0, + H3A_AF_HMF_ENABLE = 1 +}; + +enum af_config_flag { + H3A_AF_CFG_DISABLE = 0, + H3A_AF_CFG_ENABLE = 1 +}; + +enum af_mode { + ACCUMULATOR_SUMMED = 0, + ACCUMULATOR_PEAK = 1 +}; + +/* Red, Green, and blue pixel location in the AF windows */ +enum rgbpos { + GR_GB_BAYER = 0, /* GR and GB as Bayer pattern */ + RG_GB_BAYER = 1, /* RG and GB as Bayer pattern */ + GR_BG_BAYER = 2, /* GR and BG as Bayer pattern */ + RG_BG_BAYER = 3, /* RG and BG as Bayer pattern */ + GG_RB_CUSTOM = 4, /* GG and RB as custom pattern */ + RB_GG_CUSTOM = 5 /* RB and GG as custom pattern */ +}; + +/* Contains the information regarding the Horizontal Median Filter */ +struct af_hmf { + enum af_hmf_enable enable; /* Status of Horizontal Median Filter */ + unsigned int threshold; /* Threshhold Value for Horizontal Median + * Filter + */ +}; + +/* Contains the information regarding the IIR Filters */ +struct af_iir { + unsigned int hz_start_pos; /* IIR Start Register Value */ + int coeff_set0[AF_NUMBER_OF_COEF]; /* + * IIR Filter Coefficient for + * Set 0 + */ + int coeff_set1[AF_NUMBER_OF_COEF]; /* + * IIR Filter Coefficient for + * Set 1 + */ +}; + +/* Contains the information regarding the Paxels Structure in AF Engine */ +struct af_paxel { + unsigned int width; /* Width of the Paxel */ + unsigned int height; /* Height of the Paxel */ + unsigned int hz_start; /* Horizontal Start Position */ + unsigned int vt_start; /* Vertical Start Position */ + unsigned int hz_cnt; /* Horizontal Count */ + unsigned int vt_cnt; /* vertical Count */ + unsigned int line_incr; /* Line Increment */ +}; +/* Contains the parameters required for hardware set up of AF Engine */ +struct af_configuration { + enum af_alaw_enable alaw_enable; /*ALWAW status */ + struct af_hmf hmf_config; /*HMF configurations */ + enum rgbpos rgb_pos; /*RGB Positions */ + struct af_iir iir_config; /*IIR filter configurations */ + struct af_paxel paxel_config; /*Paxel parameters */ + enum af_mode mode; /*Accumulator mode */ + enum af_config_flag af_config; /*Flag indicates Engine is configured */ +}; + +/* ISP CCDC structs */ + +/* Abstraction layer CCDC configurations */ +#define ISP_ABS_CCDC_ALAW (1 << 0) +#define ISP_ABS_CCDC_LPF (1 << 1) +#define ISP_ABS_CCDC_BLCLAMP (1 << 2) +#define ISP_ABS_CCDC_BCOMP (1 << 3) +#define ISP_ABS_CCDC_FPC (1 << 4) +#define ISP_ABS_CCDC_CULL (1 << 5) +#define ISP_ABS_CCDC_COLPTN (1 << 6) +#define ISP_ABS_CCDC_CONFIG_LSC (1 << 7) +#define ISP_ABS_TBL_LSC (1 << 8) + +#define RGB_MAX 3 + +/* Enumeration constants for Alaw input width */ +enum alaw_ipwidth { + ALAW_BIT12_3 = 0x3, + ALAW_BIT11_2 = 0x4, + ALAW_BIT10_1 = 0x5, + ALAW_BIT9_0 = 0x6 +}; + +/* Enumeration constants for Video Port */ +enum vpin { + BIT12_3 = 3, + BIT11_2 = 4, + BIT10_1 = 5, + BIT9_0 = 6 +}; + +enum vpif_freq { + PIXCLKBY2, + PIXCLKBY3_5, + PIXCLKBY4_5, + PIXCLKBY5_5, + PIXCLKBY6_5 +}; + +/** + * struct ispccdc_lsc_config - Structure for LSC configuration. + * @offset: Table Offset of the gain table. + * @gain_mode_n: Vertical dimension of a paxel in LSC configuration. + * @gain_mode_m: Horizontal dimension of a paxel in LSC configuration. + * @gain_format: Gain table format. + * @fmtsph: Start pixel horizontal from start of the HS sync pulse. + * @fmtlnh: Number of pixels in horizontal direction to use for the data + * reformatter. + * @fmtslv: Start line from start of VS sync pulse for the data reformatter. + * @fmtlnv: Number of lines in vertical direction for the data reformatter. + * @initial_x: X position, in pixels, of the first active pixel in reference + * to the first active paxel. Must be an even number. + * @initial_y: Y position, in pixels, of the first active pixel in reference + * to the first active paxel. Must be an even number. + * @size: Size of LSC gain table. Filled when loaded from userspace. + */ +struct ispccdc_lsc_config { + __u16 offset; + __u8 gain_mode_n; + __u8 gain_mode_m; + __u8 gain_format; + __u16 fmtsph; + __u16 fmtlnh; + __u16 fmtslv; + __u16 fmtlnv; + __u8 initial_x; + __u8 initial_y; + __u32 size; +}; + +/** + * struct ispccdc_bclamp - Structure for Optical & Digital black clamp subtract + * @obgain: Optical black average gain. + * @obstpixel: Start Pixel w.r.t. HS pulse in Optical black sample. + * @oblines: Optical Black Sample lines. + * @oblen: Optical Black Sample Length. + * @dcsubval: Digital Black Clamp subtract value. + */ +struct ispccdc_bclamp { + __u8 obgain; + __u8 obstpixel; + __u8 oblines; + __u8 oblen; + __u16 dcsubval; +}; + +/** + * ispccdc_fpc - Structure for FPC + * @fpnum: Number of faulty pixels to be corrected in the frame. + * @fpcaddr: Memory address of the FPC Table + */ +struct ispccdc_fpc { + __u16 fpnum; + __u32 fpcaddr; +}; + +/** + * ispccdc_blcomp - Structure for Black Level Compensation parameters. + * @b_mg: B/Mg pixels. 2's complement. -128 to +127. + * @gb_g: Gb/G pixels. 2's complement. -128 to +127. + * @gr_cy: Gr/Cy pixels. 2's complement. -128 to +127. + * @r_ye: R/Ye pixels. 2's complement. -128 to +127. + */ +struct ispccdc_blcomp { + __u8 b_mg; + __u8 gb_g; + __u8 gr_cy; + __u8 r_ye; +}; + +/** + * struct ispccdc_vp - Structure for Video Port parameters + * @bitshift_sel: Video port input select. 3 - bits 12-3, 4 - bits 11-2, + * 5 - bits 10-1, 6 - bits 9-0. + * @freq_sel: Video port data ready frequency. 1 - 1/3.5, 2 - 1/4.5, + * 3 - 1/5.5, 4 - 1/6.5. + */ +struct ispccdc_vp { + enum vpin bitshift_sel; + enum vpif_freq freq_sel; +}; + +/** + * ispccdc_culling - Structure for Culling parameters. + * @v_pattern: Vertical culling pattern. + * @h_odd: Horizontal Culling pattern for odd lines. + * @h_even: Horizontal Culling pattern for even lines. + */ +struct ispccdc_culling { + __u8 v_pattern; + __u16 h_odd; + __u16 h_even; +}; + +/** + * ispccdc_update_config - Structure for CCDC configuration. + * @update: Specifies which CCDC registers should be updated. + * @flag: Specifies which CCDC functions should be enabled. + * @alawip: Enable/Disable A-Law compression. + * @bclamp: Black clamp control register. + * @blcomp: Black level compensation value for RGrGbB Pixels. 2's complement. + * @fpc: Number of faulty pixels corrected in the frame, address of FPC table. + * @cull: Cull control register. + * @colptn: Color pattern of the sensor. + * @lsc: Pointer to LSC gain table. + */ +struct ispccdc_update_config { + __u16 update; + __u16 flag; + enum alaw_ipwidth alawip; + struct ispccdc_bclamp *bclamp; + struct ispccdc_blcomp *blcomp; + struct ispccdc_fpc *fpc; + struct ispccdc_lsc_config *lsc_cfg; + struct ispccdc_culling *cull; + __u32 colptn; + __u8 *lsc; +}; + +/* Preview configuration */ + +/*Abstraction layer preview configurations*/ +#define ISP_ABS_PREV_LUMAENH (1 << 0) +#define ISP_ABS_PREV_INVALAW (1 << 1) +#define ISP_ABS_PREV_HRZ_MED (1 << 2) +#define ISP_ABS_PREV_CFA (1 << 3) +#define ISP_ABS_PREV_CHROMA_SUPP (1 << 4) +#define ISP_ABS_PREV_WB (1 << 5) +#define ISP_ABS_PREV_BLKADJ (1 << 6) +#define ISP_ABS_PREV_RGB2RGB (1 << 7) +#define ISP_ABS_PREV_COLOR_CONV (1 << 8) +#define ISP_ABS_PREV_YC_LIMIT (1 << 9) +#define ISP_ABS_PREV_DEFECT_COR (1 << 10) +#define ISP_ABS_PREV_GAMMABYPASS (1 << 11) +#define ISP_ABS_TBL_NF (1 << 12) +#define ISP_ABS_TBL_REDGAMMA (1 << 13) +#define ISP_ABS_TBL_GREENGAMMA (1 << 14) +#define ISP_ABS_TBL_BLUEGAMMA (1 << 15) + +#define ISPPRV_NF_TBL_SIZE 64 +#define ISPPRV_CFA_TBL_SIZE 576 +#define ISPPRV_GAMMA_TBL_SIZE 1024 +#define ISPPRV_YENH_TBL_SIZE 128 + +/** + * struct ispprev_hmed - Structure for Horizontal Median Filter. + * @odddist: Distance between consecutive pixels of same color in the odd line. + * @evendist: Distance between consecutive pixels of same color in the even + * line. + * @thres: Horizontal median filter threshold. + */ +struct ispprev_hmed { + __u8 odddist; + __u8 evendist; + __u8 thres; +}; + +/* + * Enumeration for CFA Formats supported by preview + */ +enum cfa_fmt { + CFAFMT_BAYER, CFAFMT_SONYVGA, CFAFMT_RGBFOVEON, + CFAFMT_DNSPL, CFAFMT_HONEYCOMB, CFAFMT_RRGGBBFOVEON +}; + +/** + * struct ispprev_cfa - Structure for CFA Inpterpolation. + * @cfafmt: CFA Format Enum value supported by preview. + * @cfa_gradthrs_vert: CFA Gradient Threshold - Vertical. + * @cfa_gradthrs_horz: CFA Gradient Threshold - Horizontal. + * @cfa_table: Pointer to the CFA table. + */ +struct ispprev_cfa { + enum cfa_fmt cfafmt; + __u8 cfa_gradthrs_vert; + __u8 cfa_gradthrs_horz; + __u32 *cfa_table; +}; + +/** + * struct ispprev_csup - Structure for Chrominance Suppression. + * @gain: Gain. + * @thres: Threshold. + * @hypf_en: Flag to enable/disable the High Pass Filter. + */ +struct ispprev_csup { + __u8 gain; + __u8 thres; + __u8 hypf_en; +}; + +/** + * struct ispprev_wbal - Structure for White Balance. + * @dgain: Digital gain (U10Q8). + * @coef3: White balance gain - COEF 3 (U8Q5). + * @coef2: White balance gain - COEF 2 (U8Q5). + * @coef1: White balance gain - COEF 1 (U8Q5). + * @coef0: White balance gain - COEF 0 (U8Q5). + */ +struct ispprev_wbal { + __u16 dgain; + __u8 coef3; + __u8 coef2; + __u8 coef1; + __u8 coef0; +}; + +/** + * struct ispprev_blkadj - Structure for Black Adjustment. + * @red: Black level offset adjustment for Red in 2's complement format + * @green: Black level offset adjustment for Green in 2's complement format + * @blue: Black level offset adjustment for Blue in 2's complement format + */ +struct ispprev_blkadj { + /*Black level offset adjustment for Red in 2's complement format */ + __u8 red; + /*Black level offset adjustment for Green in 2's complement format */ + __u8 green; + /* Black level offset adjustment for Blue in 2's complement format */ + __u8 blue; +}; + +/** + * struct ispprev_rgbtorgb - Structure for RGB to RGB Blending. + * @matrix: Blending values(S12Q8 format) + * [RR] [GR] [BR] + * [RG] [GG] [BG] + * [RB] [GB] [BB] + * @offset: Blending offset value for R,G,B in 2's complement integer format. + */ +struct ispprev_rgbtorgb { + __u16 matrix[3][3]; + __u16 offset[3]; +}; + +/** + * struct ispprev_csc - Structure for Color Space Conversion from RGB-YCbYCr + * @matrix: Color space conversion coefficients(S10Q8) + * [CSCRY] [CSCGY] [CSCBY] + * [CSCRCB] [CSCGCB] [CSCBCB] + * [CSCRCR] [CSCGCR] [CSCBCR] + * @offset: CSC offset values for Y offset, CB offset and CR offset respectively + */ +struct ispprev_csc { + __u16 matrix[RGB_MAX][RGB_MAX]; + __s16 offset[RGB_MAX]; +}; + +/** + * struct ispprev_yclimit - Structure for Y, C Value Limit. + * @minC: Minimum C value + * @maxC: Maximum C value + * @minY: Minimum Y value + * @maxY: Maximum Y value + */ +struct ispprev_yclimit { + __u8 minC; + __u8 maxC; + __u8 minY; + __u8 maxY; +}; + +/** + * struct ispprev_dcor - Structure for Defect correction. + * @couplet_mode_en: Flag to enable or disable the couplet dc Correction in NF + * @detect_correct: Thresholds for correction bit 0:10 detect 16:25 correct + */ +struct ispprev_dcor { + __u8 couplet_mode_en; + __u32 detect_correct[4]; +}; + +/** + * struct ispprev_nf - Structure for Noise Filter + * @spread: Spread value to be used in Noise Filter + * @table: Pointer to the Noise Filter table + */ +struct ispprev_nf { + __u8 spread; + __u32 table[ISPPRV_NF_TBL_SIZE]; +}; + +/** + * struct ispprv_update_config - Structure for Preview Configuration (user). + * @update: Specifies which ISP Preview registers should be updated. + * @flag: Specifies which ISP Preview functions should be enabled. + * @yen: Pointer to luma enhancement table. + * @shading_shift: 3bit value of shift used in shading compensation. + * @prev_hmed: Pointer to structure containing the odd and even distance. + * between the pixels in the image along with the filter threshold. + * @prev_cfa: Pointer to structure containing the CFA interpolation table, CFA. + * format in the image, vertical and horizontal gradient threshold. + * @csup: Pointer to Structure for Chrominance Suppression coefficients. + * @prev_wbal: Pointer to structure for White Balance. + * @prev_blkadj: Pointer to structure for Black Adjustment. + * @rgb2rgb: Pointer to structure for RGB to RGB Blending. + * @prev_csc: Pointer to structure for Color Space Conversion from RGB-YCbYCr. + * @yclimit: Pointer to structure for Y, C Value Limit. + * @prev_dcor: Pointer to structure for defect correction. + * @prev_nf: Pointer to structure for Noise Filter + * @red_gamma: Pointer to red gamma correction table. + * @green_gamma: Pointer to green gamma correction table. + * @blue_gamma: Pointer to blue gamma correction table. + */ +struct ispprv_update_config { + __u16 update; + __u16 flag; + void *yen; + __u32 shading_shift; + struct ispprev_hmed *prev_hmed; + struct ispprev_cfa *prev_cfa; + struct ispprev_csup *csup; + struct ispprev_wbal *prev_wbal; + struct ispprev_blkadj *prev_blkadj; + struct ispprev_rgbtorgb *rgb2rgb; + struct ispprev_csc *prev_csc; + struct ispprev_yclimit *yclimit; + struct ispprev_dcor *prev_dcor; + struct ispprev_nf *prev_nf; + __u32 *red_gamma; + __u32 *green_gamma; + __u32 *blue_gamma; +}; + +#endif /* OMAP_ISP_USER_H */ diff --git a/arch/arm/plat-omap/include/plat/vram.h b/arch/arm/plat-omap/include/plat/vram.h new file mode 100644 index 00000000000..edd4987758a --- /dev/null +++ b/arch/arm/plat-omap/include/plat/vram.h @@ -0,0 +1,62 @@ +/* + * VRAM manager for OMAP + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __OMAP_VRAM_H__ +#define __OMAP_VRAM_H__ + +#include + +#define OMAP_VRAM_MEMTYPE_SDRAM 0 +#define OMAP_VRAM_MEMTYPE_SRAM 1 +#define OMAP_VRAM_MEMTYPE_MAX 1 + +extern int omap_vram_add_region(unsigned long paddr, size_t size); +extern int omap_vram_free(unsigned long paddr, size_t size); +extern int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr); +extern int omap_vram_reserve(unsigned long paddr, size_t size); +extern void omap_vram_get_info(unsigned long *vram, unsigned long *free_vram, + unsigned long *largest_free_block); + +#ifdef CONFIG_OMAP2_VRAM +extern void omap_vram_set_sdram_vram(u32 size, u32 start); +extern void omap_vram_set_sram_vram(u32 size, u32 start); + +extern void omap_vram_reserve_sdram(void); +extern unsigned long omap_vram_reserve_sram(unsigned long sram_pstart, + unsigned long sram_vstart, + unsigned long sram_size, + unsigned long pstart_avail, + unsigned long size_avail); +#else +static inline void omap_vram_set_sdram_vram(u32 size, u32 start) { } +static inline void omap_vram_set_sram_vram(u32 size, u32 start) { } + +static inline void omap_vram_reserve_sdram(void) { } +static inline unsigned long omap_vram_reserve_sram(unsigned long sram_pstart, + unsigned long sram_vstart, + unsigned long sram_size, + unsigned long pstart_avail, + unsigned long size_avail) +{ + return 0; +} +#endif + +#endif diff --git a/arch/arm/plat-omap/include/plat/vrfb.h b/arch/arm/plat-omap/include/plat/vrfb.h new file mode 100644 index 00000000000..d8a03ced3b1 --- /dev/null +++ b/arch/arm/plat-omap/include/plat/vrfb.h @@ -0,0 +1,50 @@ +/* + * VRFB Rotation Engine + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __OMAP_VRFB_H__ +#define __OMAP_VRFB_H__ + +#define OMAP_VRFB_LINE_LEN 2048 + +struct vrfb { + u8 context; + void __iomem *vaddr[4]; + unsigned long paddr[4]; + u16 xres; + u16 yres; + u16 xoffset; + u16 yoffset; + u8 bytespp; + bool yuv_mode; +}; + +extern int omap_vrfb_request_ctx(struct vrfb *vrfb); +extern void omap_vrfb_release_ctx(struct vrfb *vrfb); +extern void omap_vrfb_adjust_size(u16 *width, u16 *height, + u8 bytespp); +extern u32 omap_vrfb_min_phys_size(u16 width, u16 height, u8 bytespp); +extern u16 omap_vrfb_max_height(u32 phys_size, u16 width, u8 bytespp); +extern void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, + u16 width, u16 height, + unsigned bytespp, bool yuv_mode); +extern int omap_vrfb_map_angle(struct vrfb *vrfb, u16 height, u8 rot); +extern void omap_vrfb_restore_context(void); + +#endif /* __VRFB_H */ diff --git a/arch/arm/plat-omap/psp-version.c b/arch/arm/plat-omap/psp-version.c new file mode 100644 index 00000000000..b13021a85e3 --- /dev/null +++ b/arch/arm/plat-omap/psp-version.c @@ -0,0 +1,87 @@ +/* + * linux/arch/arm/mach-omap2/ti-psp-version.c + * + * Create a proc entry for showing PSP version. + * + * Copyright (C) 2008 Texas Instruments Incorporated. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + +#include "psp-version.h" + +static int init_psp_module(void); +static void exit_psp_module(void); + + +static struct proc_dir_entry *vers_file; + +static int show_version(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + int len; + + len = sprintf(page, TI_PSP_DEVICE \ + " Linux PSP version " TI_PSP_VERSION \ + " (" TI_PSP_PLATFORM ")\n"); + + return len; +} + +static int __init init_psp_module(void) +{ + int result = 0; + + /* + * Create the file "/proc/ti-psp-version" + */ + vers_file = create_proc_read_entry( + TI_PSP_ENTRY, + 0444, + NULL, + show_version, + NULL); + + if (vers_file != NULL) { + + printk (KERN_INFO TI_PSP_DEVICE \ + " Linux PSP version " TI_PSP_VERSION \ + " (" TI_PSP_PLATFORM ")\n"); + } + else { + result = -ENOMEM; + } + + return result; +} + +static void __exit exit_psp_module(void) +{ + remove_proc_entry(TI_PSP_ENTRY, NULL); +} + +module_init(init_psp_module); +module_exit(exit_psp_module); + +MODULE_LICENSE("GPLv2"); +MODULE_AUTHOR("Texas Instruments Incorporated"); +MODULE_DESCRIPTION("Contains version of the Linux PSP release."); diff --git a/arch/arm/plat-omap/psp-version.h b/arch/arm/plat-omap/psp-version.h new file mode 100644 index 00000000000..8a5073ef85e --- /dev/null +++ b/arch/arm/plat-omap/psp-version.h @@ -0,0 +1,40 @@ +/* + * linux/arch/arm/mach-omap2/ti-psp-version.c + * + * Create a proc entry for showing PSP version. + * + * Copyright (C) 2008 Texas Instruments Incorporated. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ARCH_ARM_PLAT_PSP_VERSION_H +#define __ARCH_ARM_PLAT_PSP_VERSION_H + +#define TI_PSP_ENTRY "ti-psp-version" +#define TI_PSP_VERSION "03.00.00.03" + +#if defined (CONFIG_MACH_OMAP3EVM) +#define TI_PSP_DEVICE "OMAP35x" +#define TI_PSP_PLATFORM "OMAP3EVM" +#elif defined(CONFIG_MACH_OMAP3517EVM) +#define TI_PSP_DEVICE "AM3517" +#define TI_PSP_PLATFORM "AM3517EVM" +#else +#define TI_PSP_DEVICE "DEVICE" +#define TI_PSP_PLATFORM "PLATFORM" +#endif + +#endif + diff --git a/drivers/input/touchscreen/tsc2004.c b/drivers/input/touchscreen/tsc2004.c new file mode 100644 index 00000000000..0bba2e670cf --- /dev/null +++ b/drivers/input/touchscreen/tsc2004.c @@ -0,0 +1,525 @@ +/* + * drivers/input/touchscreen/tsc2004.c + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Using code from: + * - tsc2007.c + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include + + +#define TS_POLL_DELAY 1 /* ms delay between samples */ +#define TS_POLL_PERIOD 1 /* ms delay between samples */ + +/* Control byte 0 */ +#define TSC2004_CMD0(addr, pnd, rw) ((addr<<3)|(pnd<<1)|rw) +/* Control byte 1 */ +#define TSC2004_CMD1(cmd, mode, rst) ((1<<7)|(cmd<<4)|(mode<<2)|(rst<<1)) + +/* Command Bits */ +#define READ_REG 1 +#define WRITE_REG 0 +#define SWRST_TRUE 1 +#define SWRST_FALSE 0 +#define PND0_TRUE 1 +#define PND0_FALSE 0 + +/* Converter function mapping */ +enum convertor_function { + MEAS_X_Y_Z1_Z2, /* Measure X,Y,z1 and Z2: 0x0 */ + MEAS_X_Y, /* Measure X and Y only: 0x1 */ + MEAS_X, /* Measure X only: 0x2 */ + MEAS_Y, /* Measure Y only: 0x3 */ + MEAS_Z1_Z2, /* Measure Z1 and Z2 only: 0x4 */ + MEAS_AUX, /* Measure Auxillary input: 0x5 */ + MEAS_TEMP1, /* Measure Temparature1: 0x6 */ + MEAS_TEMP2, /* Measure Temparature2: 0x7 */ + MEAS_AUX_CONT, /* Continuously measure Auxillary input: 0x8 */ + X_DRV_TEST, /* X-Axis drivers tested 0x9 */ + Y_DRV_TEST, /* Y-Axis drivers tested 0xA */ + /*Command Reserved*/ + SHORT_CKT_TST = 0xC, /* Short circuit test: 0xC */ + XP_XN_DRV_STAT, /* X+,Y- drivers status: 0xD */ + YP_YN_DRV_STAT, /* X+,Y- drivers status: 0xE */ + YP_XN_DRV_STAT /* Y+,X- drivers status: 0xF */ +}; + +/* Register address mapping */ +enum register_address { + X_REG, /* X register: 0x0 */ + Y_REG, /* Y register: 0x1 */ + Z1_REG, /* Z1 register: 0x2 */ + Z2_REG, /* Z2 register: 0x3 */ + AUX_REG, /* AUX register: 0x4 */ + TEMP1_REG, /* Temp1 register: 0x5 */ + TEMP2_REG, /* Temp2 register: 0x6 */ + STAT_REG, /* Status Register: 0x7 */ + AUX_HGH_TH_REG, /* AUX high threshold register: 0x8 */ + AUX_LOW_TH_REG, /* AUX low threshold register: 0x9 */ + TMP_HGH_TH_REG, /* Temp high threshold register:0xA */ + TMP_LOW_TH_REG, /* Temp low threshold register: 0xB */ + CFR0_REG, /* Configuration register 0: 0xC */ + CFR1_REG, /* Configuration register 1: 0xD */ + CFR2_REG, /* Configuration register 2: 0xE */ + CONV_FN_SEL_STAT /* Convertor function select register: 0xF */ +}; + +/* Supported Resolution modes */ +enum resolution_mode { + MODE_10BIT, /* 10 bit resolution */ + MODE_12BIT /* 12 bit resolution */ +}; + +/* Configuraton register bit fields */ +/* CFR0 */ +#define PEN_STS_CTRL_MODE (1<<15) +#define ADC_STS (1<<14) +#define RES_CTRL (1<<13) +#define ADC_CLK_4MHZ (0<<11) +#define ADC_CLK_2MHZ (1<<11) +#define ADC_CLK_1MHZ (2<<11) +#define PANEL_VLTG_STB_TIME_0US (0<<8) +#define PANEL_VLTG_STB_TIME_100US (1<<8) +#define PANEL_VLTG_STB_TIME_500US (2<<8) +#define PANEL_VLTG_STB_TIME_1MS (3<<8) +#define PANEL_VLTG_STB_TIME_5MS (4<<8) +#define PANEL_VLTG_STB_TIME_10MS (5<<8) +#define PANEL_VLTG_STB_TIME_50MS (6<<8) +#define PANEL_VLTG_STB_TIME_100MS (7<<8) + +/* CFR2 */ +#define PINTS1 (1<<15) +#define PINTS0 (1<<14) +#define MEDIAN_VAL_FLTR_SIZE_1 (0<<12) +#define MEDIAN_VAL_FLTR_SIZE_3 (1<<12) +#define MEDIAN_VAL_FLTR_SIZE_7 (2<<12) +#define MEDIAN_VAL_FLTR_SIZE_15 (3<<12) +#define AVRG_VAL_FLTR_SIZE_1 (0<<10) +#define AVRG_VAL_FLTR_SIZE_3_4 (1<<10) +#define AVRG_VAL_FLTR_SIZE_7_8 (2<<10) +#define AVRG_VAL_FLTR_SIZE_16 (3<<10) +#define MAV_FLTR_EN_X (1<<4) +#define MAV_FLTR_EN_Y (1<<3) +#define MAV_FLTR_EN_Z (1<<2) + +#define MAX_12BIT ((1 << 12) - 1) +#define MEAS_MASK 0xFFF + +struct ts_event { + u16 x; + u16 y; + u16 z1, z2; +}; + +struct tsc2004 { + struct input_dev *input; + char phys[32]; + struct delayed_work work; + + struct i2c_client *client; + + u16 model; + u16 x_plate_ohms; + + bool pendown; + int irq; + + int (*get_pendown_state)(void); + void (*clear_penirq)(void); +}; + +static inline int tsc2004_read_word_data(struct tsc2004 *tsc, u8 cmd) +{ + s32 data; + u16 val; + + data = i2c_smbus_read_word_data(tsc->client, cmd); + if (data < 0) { + dev_err(&tsc->client->dev, "i2c io (read) error: %d\n", data); + return data; + } + + /* The protocol and raw data format from i2c interface: + * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. + */ + val = swab16(data) >> 4; + + dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); + + return val; +} + +static inline int tsc2004_write_word_data(struct tsc2004 *tsc, u8 cmd, u16 data) +{ + u16 val; + + val = swab16(data); + return i2c_smbus_write_word_data(tsc->client, cmd, val); +} + +static inline int tsc2004_write_cmd(struct tsc2004 *tsc, u8 value) +{ + return i2c_smbus_write_byte(tsc->client, value); +} + +static int tsc2004_prepare_for_reading(struct tsc2004 *ts) +{ + int err; + int cmd, data; + + /* Reset the TSC, configure for 12 bit */ + cmd = TSC2004_CMD1(MEAS_X_Y_Z1_Z2, MODE_12BIT, SWRST_TRUE); + err = tsc2004_write_cmd(ts, cmd); + if (err < 0) + return err; + + /* Enable interrupt for PENIRQ and DAV */ + cmd = TSC2004_CMD0(CFR2_REG, PND0_FALSE, WRITE_REG); + data = PINTS1 | PINTS0 | MEDIAN_VAL_FLTR_SIZE_15 | + AVRG_VAL_FLTR_SIZE_7_8 | MAV_FLTR_EN_X | MAV_FLTR_EN_Y | + MAV_FLTR_EN_Z; + err = tsc2004_write_word_data(ts, cmd, data); + if (err < 0) + return err; + + /* Configure the TSC in TSMode 1 */ + cmd = TSC2004_CMD0(CFR0_REG, PND0_FALSE, WRITE_REG); + data = PEN_STS_CTRL_MODE | ADC_CLK_2MHZ | PANEL_VLTG_STB_TIME_1MS; + err = tsc2004_write_word_data(ts, cmd, data); + if (err < 0) + return err; + + /* Enable x, y, z1 and z2 conversion functions */ + cmd = TSC2004_CMD1(MEAS_X_Y_Z1_Z2, MODE_12BIT, SWRST_FALSE); + err = tsc2004_write_cmd(ts, cmd); + if (err < 0) + return err; + + return 0; +} + +static void tsc2004_read_values(struct tsc2004 *tsc, struct ts_event *tc) +{ + int cmd; + + /* Read X Measurement */ + cmd = TSC2004_CMD0(X_REG, PND0_FALSE, READ_REG); + tc->x = tsc2004_read_word_data(tsc, cmd); + + /* Read Y Measurement */ + cmd = TSC2004_CMD0(Y_REG, PND0_FALSE, READ_REG); + tc->y = tsc2004_read_word_data(tsc, cmd); + + /* Read Z1 Measurement */ + cmd = TSC2004_CMD0(Z1_REG, PND0_FALSE, READ_REG); + tc->z1 = tsc2004_read_word_data(tsc, cmd); + + /* Read Z2 Measurement */ + cmd = TSC2004_CMD0(Z2_REG, PND0_FALSE, READ_REG); + tc->z2 = tsc2004_read_word_data(tsc, cmd); + + + tc->x &= MEAS_MASK; + tc->y &= MEAS_MASK; + tc->z1 &= MEAS_MASK; + tc->z2 &= MEAS_MASK; + + /* Prepare for touch readings */ + if (tsc2004_prepare_for_reading(tsc) < 0) + dev_dbg(&tsc->client->dev, "Failed to prepare TSC for next" + "reading\n"); +} + +static u32 tsc2004_calculate_pressure(struct tsc2004 *tsc, struct ts_event *tc) +{ + u32 rt = 0; + + /* range filtering */ + if (tc->x == MAX_12BIT) + tc->x = 0; + + if (likely(tc->x && tc->z1)) { + /* compute touch pressure resistance using equation #1 */ + rt = tc->z2 - tc->z1; + rt *= tc->x; + rt *= tsc->x_plate_ohms; + rt /= tc->z1; + rt = (rt + 2047) >> 12; + } + + return rt; +} + +static void tsc2004_send_up_event(struct tsc2004 *tsc) +{ + struct input_dev *input = tsc->input; + + dev_dbg(&tsc->client->dev, "UP\n"); + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); +} + +static void tsc2004_work(struct work_struct *work) +{ + struct tsc2004 *ts = + container_of(to_delayed_work(work), struct tsc2004, work); + struct ts_event tc; + u32 rt; + + /* + * NOTE: We can't rely on the pressure to determine the pen down + * state, even though this controller has a pressure sensor. + * The pressure value can fluctuate for quite a while after + * lifting the pen and in some cases may not even settle at the + * expected value. + * + * The only safe way to check for the pen up condition is in the + * work function by reading the pen signal state (it's a GPIO + * and IRQ). Unfortunately such callback is not always available, + * in that case we have rely on the pressure anyway. + */ + if (ts->get_pendown_state) { + if (unlikely(!ts->get_pendown_state())) { + tsc2004_send_up_event(ts); + ts->pendown = false; + goto out; + } + + dev_dbg(&ts->client->dev, "pen is still down\n"); + } + + tsc2004_read_values(ts, &tc); + + rt = tsc2004_calculate_pressure(ts, &tc); + if (rt > MAX_12BIT) { + /* + * Sample found inconsistent by debouncing or pressure is + * beyond the maximum. Don't report it to user space, + * repeat at least once more the measurement. + */ + dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); + goto out; + + } + + if (rt) { + struct input_dev *input = ts->input; + + if (!ts->pendown) { + dev_dbg(&ts->client->dev, "DOWN\n"); + + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = true; + } + + input_report_abs(input, ABS_X, tc.x); + input_report_abs(input, ABS_Y, tc.y); + input_report_abs(input, ABS_PRESSURE, rt); + + input_sync(input); + + dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", + tc.x, tc.y, rt); + + } else if (!ts->get_pendown_state && ts->pendown) { + /* + * We don't have callback to check pendown state, so we + * have to assume that since pressure reported is 0 the + * pen was lifted up. + */ + tsc2004_send_up_event(ts); + ts->pendown = false; + } + + out: + if (ts->pendown) + schedule_delayed_work(&ts->work, + msecs_to_jiffies(TS_POLL_PERIOD)); + else + enable_irq(ts->irq); +} + +static irqreturn_t tsc2004_irq(int irq, void *handle) +{ + struct tsc2004 *ts = handle; + + if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { + disable_irq_nosync(ts->irq); + schedule_delayed_work(&ts->work, + msecs_to_jiffies(TS_POLL_DELAY)); + } + + if (ts->clear_penirq) + ts->clear_penirq(); + + return IRQ_HANDLED; +} + +static void tsc2004_free_irq(struct tsc2004 *ts) +{ + free_irq(ts->irq, ts); + if (cancel_delayed_work_sync(&ts->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(ts->irq); + } +} + +static int __devinit tsc2004_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tsc2004 *ts; + struct tsc2004_platform_data *pdata = pdata = client->dev.platform_data; + struct input_dev *input_dev; + int err; + + if (!pdata) { + dev_err(&client->dev, "platform data is required!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct tsc2004), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->irq = client->irq; + ts->input = input_dev; + INIT_DELAYED_WORK(&ts->work, tsc2004_work); + + ts->model = pdata->model; + ts->x_plate_ohms = pdata->x_plate_ohms; + ts->get_pendown_state = pdata->get_pendown_state; + ts->clear_penirq = pdata->clear_penirq; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = "TSC2004 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + if (pdata->init_platform_hw) + pdata->init_platform_hw(); + + err = request_irq(ts->irq, tsc2004_irq, IRQF_TRIGGER_FALLING, + client->dev.driver->name, ts); + if (err < 0) { + dev_err(&client->dev, "irq %d busy?\n", ts->irq); + goto err_free_mem; + } + + /* Prepare for touch readings */ + err = tsc2004_prepare_for_reading(ts); + if (err < 0) + goto err_free_irq; + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + i2c_set_clientdata(client, ts); + + return 0; + + err_free_irq: + tsc2004_free_irq(ts); + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); + err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +static int __devexit tsc2004_remove(struct i2c_client *client) +{ + struct tsc2004 *ts = i2c_get_clientdata(client); + struct tsc2004_platform_data *pdata = client->dev.platform_data; + + tsc2004_free_irq(ts); + + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); + + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static struct i2c_device_id tsc2004_idtable[] = { + { "tsc2004", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tsc2004_idtable); + +static struct i2c_driver tsc2004_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tsc2004" + }, + .id_table = tsc2004_idtable, + .probe = tsc2004_probe, + .remove = __devexit_p(tsc2004_remove), +}; + +static int __init tsc2004_init(void) +{ + return i2c_add_driver(&tsc2004_driver); +} + +static void __exit tsc2004_exit(void) +{ + i2c_del_driver(&tsc2004_driver); +} + +module_init(tsc2004_init); +module_exit(tsc2004_exit); + +MODULE_AUTHOR("Vaibhav Hiremath "); +MODULE_DESCRIPTION("TSC2004 TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/dw9710.c b/drivers/media/video/dw9710.c new file mode 100644 index 00000000000..ab2966d67c3 --- /dev/null +++ b/drivers/media/video/dw9710.c @@ -0,0 +1,609 @@ +/* + * dw9710.c - DW9710 Coil Motor (LENS) driver + * + * Copyright (C) 2009 Texas Instruments. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * Mohit Jalori + * + * 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. + */ + +#include +#include +#include + +#include +#include + +#define DW9710_I2C_RETRY_COUNT 5 +#define DW9710_DISABLE 1 +#define DW9710_ENABLE 0 +#define DW9710_POWERDN(ARG) (((ARG) & 0x1) << 15) +#define DW9710_POWERDN_R(ARG) (((ARG) >> 15) & 0x1) +#define DW9710_DATA(ARG) (((ARG) & 0xFF) << 6) +#define DW9710_DATA_R(ARG) (((ARG) >> 6) & 0xFF) + +/* Focus control values */ +#define DW9710_DEF_LENS_POSN 0 /* 0x7F */ +#define DW9710_LENS_POSN_STEP 1 +#define DW9710_MAX_FOCUS_POS 0xFF + +struct dw9710_device { + struct device *dev; + struct dw9710_platform_data *pdata; + struct v4l2_int_device *v4l2_int_device; + int opened; + u16 current_lens_posn; + u16 saved_lens_posn; + int detected; + int power_state; +}; + +static struct vcontrol { + struct v4l2_queryctrl qc; + int current_value; +} video_control[] = { + { + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Focus, Absolute", + .minimum = 0, + .maximum = DW9710_MAX_FOCUS_POS, + .step = DW9710_LENS_POSN_STEP, + .default_value = DW9710_DEF_LENS_POSN, + }, + .current_value = DW9710_DEF_LENS_POSN, + } +}; + +/** + * find_vctrl - Finds the requested ID in the video control structure array + * @id: ID of control to search the video control array for + * + * Returns the index of the requested ID from the control structure array + */ +static int find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--) { + if (video_control[i].qc.id == id) + return i; + } + + return -EINVAL; +} + +/** + * dw9710_reg_read - Reads a value from a register in DW9710 Coil driver device. + * @client: Pointer to structure of I2C client. + * @value: Pointer to u16 for returning value of register to read. + * + * Returns zero if successful, or non-zero otherwise. + **/ +static int dw9710_reg_read(struct i2c_client *client, u16 *value) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = I2C_M_RD; + msg->len = 2; + msg->buf = data; + + data[0] = 0; + data[1] = 0; + + err = i2c_transfer(client->adapter, msg, 1); + + if (err >= 0) { + err = ((data[0] & 0xFF) << 8) | (data[1]); + *value = err; + return 0; + } + return err; +} + +/** + * dw9710_reg_write - Writes a value to a register in DW9710 Coil driver device. + * @client: Pointer to structure of I2C client. + * @value: Value of register to write. + * + * Returns zero if successful, or non-zero otherwise. + **/ +static int dw9710_reg_write(struct i2c_client *client, u16 value) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + int retry = 0; + + if (!client->adapter) + return -ENODEV; + +again: + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + + data[0] = (u8)(value >> 8); + data[1] = (u8)(value & 0xFF); + + err = i2c_transfer(client->adapter, msg, 1); + + if (err >= 0) + return 0; + + if (retry <= DW9710_I2C_RETRY_COUNT) { + dev_dbg(&client->dev, "retry ... %d", retry); + retry++; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(20)); + goto again; + } + return err; +} + +/** + * dw9710_detect - Detects DW9710 Coil driver device. + * @client: Pointer to structure of I2C client. + * + * Returns 0 if successful, -1 if camera is off or if test register value + * wasn't stored properly, or returned errors from either dw9710_reg_write or + * dw9710_reg_read functions. + **/ +static int dw9710_detect(struct i2c_client *client) +{ + int err = 0; + u16 wposn = 0, rposn = 0; + u16 posn = 0x05; + + wposn = (DW9710_POWERDN(DW9710_ENABLE) | DW9710_DATA(posn)); + + err = dw9710_reg_write(client, wposn); + if (err) { + dev_err(&client->dev, "Unable to write DW9710 \n"); + return err; + } + + err = dw9710_reg_read(client, &rposn); + if (err) { + dev_err(&client->dev, "Unable to read DW9710\n"); + return err; + } + + if (wposn != rposn) { + dev_err(&client->dev, "W/R MISMATCH!\n"); + return -1; + } + posn = 0; + wposn = (DW9710_POWERDN(DW9710_ENABLE) | DW9710_DATA(posn)); + err = dw9710_reg_write(client, wposn); + + return err; +} + +/** + * dw9710_af_setfocus - Sets the desired focus. + * @posn: Desired focus position, 0 (far) - 100 (close). + * + * Returns 0 on success, -EINVAL if camera is off or focus value is out of + * bounds, or returned errors from either dw9710_reg_write or dw9710_reg_read + * functions. + **/ +int dw9710_af_setfocus(struct v4l2_int_device *s, u16 posn) +{ + struct dw9710_device *lens = s->priv; + struct i2c_client *client = to_i2c_client(lens->dev); + u16 cur_focus_value = 0; + int ret = -EINVAL; + + if (posn > DW9710_MAX_FOCUS_POS) { + dev_err(&client->dev, "Bad posn params 0x%x \n", posn); + return ret; + } + + if ((lens->power_state == V4L2_POWER_OFF) || + (lens->power_state == V4L2_POWER_STANDBY)) { + lens->current_lens_posn = posn; + return 0; + } + + ret = dw9710_reg_read(client, &cur_focus_value); + + if (ret) { + dev_err(&client->dev, "Read of current Lens position failed\n"); + return ret; + } + + if (DW9710_DATA_R(cur_focus_value) == posn) { + dev_dbg(&client->dev, "Device already in requested focal point\n"); + return ret; + } + + ret = dw9710_reg_write(client, DW9710_POWERDN(DW9710_ENABLE) | + DW9710_DATA(posn)); + + if (ret) + dev_err(&client->dev, "Setfocus register write failed\n"); + lens->current_lens_posn = posn; + return ret; +} +EXPORT_SYMBOL(dw9710_af_setfocus); + +/** + * dw9710_af_getfocus - Gets the focus value from device. + * @value: Pointer to u16 variable which will contain the focus value. + * + * Returns 0 if successful, -EINVAL if camera is off, or return value of + * dw9710_reg_read if fails. + **/ +int dw9710_af_getfocus(struct v4l2_int_device *s, u16 *value) +{ + struct dw9710_device *lens = s->priv; + struct i2c_client *client = to_i2c_client(lens->dev); + int ret; + u16 posn = 0; + + if ((lens->power_state == V4L2_POWER_OFF) || + (lens->power_state == V4L2_POWER_STANDBY)) + return -EINVAL; + + ret = dw9710_reg_read(client, &posn); + + if (ret) { + dev_err(&client->dev, "Read of current Lens position failed\n"); + return ret; + } + *value = DW9710_DATA_R(posn); + lens->current_lens_posn = DW9710_DATA_R(posn); + return ret; +} +EXPORT_SYMBOL(dw9710_af_getfocus); + +/** + * ioctl_queryctrl - V4L2 lens interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the video_control[] array. Otherwise, returns -EINVAL if the + * control is not supported. + */ +static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc) +{ + int i; + + i = find_vctrl(qc->id); + if (i == -EINVAL) + qc->flags = V4L2_CTRL_FLAG_DISABLED; + + if (i < 0) + return -EINVAL; + + *qc = video_control[i].qc; + return 0; +} + +/** + * ioctl_g_ctrl - V4L2 DW9710 lens interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + struct vcontrol *lvc; + int i; + u16 curr_posn; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_FOCUS_ABSOLUTE: + if (dw9710_af_getfocus(s, &curr_posn)) + return -EFAULT; + vc->value = curr_posn; + lvc->current_value = curr_posn; + break; + } + + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 DW9710 lens interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = -EINVAL; + int i; + struct vcontrol *lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_FOCUS_ABSOLUTE: + retval = dw9710_af_setfocus(s, vc->value); + if (!retval) + lvc->current_value = vc->value; + break; + } + + return retval; +} + +/** + * ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold sensor's private data address + * + * Returns device's (sensor's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct dw9710_device *lens = s->priv; + + return lens->pdata->priv_data_set(p); + +} + +/** + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * dw9710 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct dw9710_device *lens = s->priv; + struct i2c_client *client = to_i2c_client(lens->dev); + int err; + + err = dw9710_detect(client); + if (err < 0) { + dev_err(&client->dev, "Unable to detect lens\n"); + lens->detected = 0; + return err; + } + lens->detected = 1; + dev_info(&client->dev, "Lens HW detected\n"); + + return 0; +}/** + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power new_power) +{ + struct dw9710_device *lens = s->priv; + int rval = 0; + + switch (new_power) { + case V4L2_POWER_ON: + rval = lens->pdata->power_set(V4L2_POWER_ON); + if (rval) + break; + + if (lens->detected) + dw9710_af_setfocus(s, lens->current_lens_posn); + else { + rval = ioctl_dev_init(s); + if (rval) + goto err_on; + } + break; + case V4L2_POWER_OFF: +err_on: + lens->pdata->power_set(V4L2_POWER_OFF); + break; + case V4L2_POWER_STANDBY: + rval = lens->pdata->power_set(V4L2_POWER_STANDBY); + break; + default: + return -EINVAL; + } + + if (!rval) + lens->power_state = new_power; + return rval; +} + +static struct v4l2_int_ioctl_desc dw9710_ioctl_desc[] = { + { .num = vidioc_int_dev_init_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_init }, + { .num = vidioc_int_dev_exit_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_exit }, + { .num = vidioc_int_s_power_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_power }, + { .num = vidioc_int_g_priv_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_priv }, + { .num = vidioc_int_queryctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_queryctrl }, + { .num = vidioc_int_g_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { .num = vidioc_int_s_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_ctrl }, +}; + +static struct v4l2_int_slave dw9710_slave = { + .ioctls = dw9710_ioctl_desc, + .num_ioctls = ARRAY_SIZE(dw9710_ioctl_desc), +}; + +static struct v4l2_int_device dw9710_int_device = { + .module = THIS_MODULE, + .name = DW9710_NAME, + .type = v4l2_int_type_slave, + .u = { + .slave = &dw9710_slave, + }, +}; + +/** + * dw9710_probe - Probes the driver for valid I2C attachment. + * @client: Pointer to structure of I2C client. + * + * Returns 0 if successful, or -EBUSY if unable to get client attached data. + **/ +static int dw9710_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct dw9710_device *lens; + struct dw9710_platform_data *pdata; + int err; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + lens = kzalloc(sizeof(*lens), GFP_KERNEL); + if (!lens) + return -ENOMEM; + + /* Don't keep pointer to platform data, copy elements instead */ + lens->pdata = kzalloc(sizeof(*lens->pdata), GFP_KERNEL); + if (!lens->pdata) { + err = -ENOMEM; + goto on_err1; + } + + lens->pdata->power_set = pdata->power_set; + lens->pdata->priv_data_set = pdata->priv_data_set; + + lens->detected = 0; + lens->current_lens_posn = DW9710_DEF_LENS_POSN; + + lens->v4l2_int_device = &dw9710_int_device; + lens->v4l2_int_device->priv = lens; + lens->dev = &client->dev; + + i2c_set_clientdata(client, lens); + + err = v4l2_int_device_register(lens->v4l2_int_device); + if (err) { + dev_err(&client->dev, "Failed to Register as V4L2 device.\n"); + goto on_err2; + } + + return 0; +on_err2: + i2c_set_clientdata(client, NULL); + kfree(lens->pdata); +on_err1: + kfree(lens); + return err; +} + +/** + * dw9710_remove - Routine when device its unregistered from I2C + * @client: Pointer to structure of I2C client. + * + * Returns 0 if successful, or -ENODEV if the client isn't attached. + **/ +static int dw9710_remove(struct i2c_client *client) +{ + if (!client->adapter) + return -ENODEV; + + i2c_set_clientdata(client, NULL); + return 0; +} + +static const struct i2c_device_id dw9710_id[] = { + { DW9710_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dw9710_id); + +static struct i2c_driver dw9710_i2c_driver = { + .driver = { + .name = DW9710_NAME, + .owner = THIS_MODULE, + }, + .probe = dw9710_probe, + .remove = dw9710_remove, + .id_table = dw9710_id, +}; + +/** + * dw9710_init - Module initialisation. + * + * Returns 0 if successful, or -EINVAL if device couldn't be initialized, or + * added as a character device. + **/ +static int __init dw9710_init(void) +{ + int err = -EINVAL; + + err = i2c_add_driver(&dw9710_i2c_driver); + if (err) + goto fail; + return err; +fail: + printk(KERN_ERR "Failed to register " DW9710_NAME ".\n"); + return err; +} +module_init(dw9710_init); + +/** + * dw9710_cleanup - Module cleanup. + **/ +static void __exit dw9710_cleanup(void) +{ + i2c_del_driver(&dw9710_i2c_driver); +} +module_exit(dw9710_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DW9710 LENS driver"); diff --git a/drivers/media/video/imx046.c b/drivers/media/video/imx046.c new file mode 100644 index 00000000000..34156725ec3 --- /dev/null +++ b/drivers/media/video/imx046.c @@ -0,0 +1,1829 @@ +/* + * drivers/media/video/imx046.c + * + * Sony imx046 sensor driver + * + * + * Copyright (C) 2008 Hewlett Packard + * + * Leverage mt9p012.c + * + * 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. + */ + +#include +#include + +#include +#include + +#include "imx046_regs.h" +#include "omap34xxcam.h" +#include "isp/isp.h" +#include "isp/ispcsi2.h" + + +#define IMX046_DRIVER_NAME "imx046" +#define IMX046_MOD_NAME "IMX046: " + +#define I2C_M_WR 0 + + +/* from IMX046ES_registerSetting_I2C_MIPI_2lane_def_080925.xls */ +const static struct imx046_reg initial_list[] = { + {IMX046_REG_IMAGE_ORIENTATION, 0x03, I2C_8BIT}, + {IMX046_REG_COARSE_INT_TIME, 0x0120, I2C_16BIT}, + {IMX046_REG_ANALOG_GAIN_GLOBAL, 0x80, I2C_16BIT}, + {0x300A, 0x80, I2C_8BIT}, + {IMX046_REG_Y_OPBADDR_START_DI, 0x08, I2C_8BIT}, + {IMX046_REG_Y_OPBADDR_END_DI, 0x37, I2C_8BIT}, + {IMX046_REG_CHCODE_OUTCHSINGLE, 0x60, I2C_8BIT}, + {IMX046_REG_OUTIF, 0x01, I2C_8BIT}, + {IMX046_REG_RGPOF_RGPOFV2, 0x28, I2C_8BIT}, + {IMX046_REG_CPCKAUTOEN, 0x00, I2C_8BIT}, + {IMX046_REG_RGCPVFB, 0x60, I2C_8BIT}, + {IMX046_REG_RGAZPDRV, 0x24, I2C_8BIT}, + {IMX046_REG_RGAZTEST, 0x34, I2C_8BIT}, + {IMX046_REG_RGVSUNLV, 0x3B, I2C_8BIT}, + {IMX046_REG_CLPOWER, 0x30, I2C_8BIT}, + {IMX046_REG_CLPOWERSP, 0x00, I2C_8BIT}, + {IMX046_REG_ACLDIRV_TVADDCLP, 0x00, I2C_8BIT}, + {IMX046_REG_OPB_CTRL, 0x0080, I2C_16BIT}, + {0x30AB, 0x1C, I2C_8BIT}, + {0x30B0, 0x32, I2C_8BIT}, + {0x30B2, 0x83, I2C_8BIT}, + {IMX046_REG_RAW10CH2V2P_LO, 0xD8, I2C_8BIT}, + {IMX046_REG_RAW10CH2V2D_LO, 0x17, I2C_8BIT}, + {IMX046_REG_COMP8CH1V2P_LO, 0xCF, I2C_8BIT}, + {IMX046_REG_COMP8CH1V2D_LO, 0xF1, I2C_8BIT}, + {IMX046_REG_RAW10CH1V2P_LO, 0xD8, I2C_8BIT}, + {IMX046_REG_RAW10CH1V2D_LO, 0x17, I2C_8BIT}, + {0x3302, 0x0A, I2C_8BIT}, + {0x3303, 0x09, I2C_8BIT}, + {IMX046_REG_RGTLPX, 0x05, I2C_8BIT}, + {IMX046_REG_RGTCLKPREPARE, 0x04, I2C_8BIT}, + {IMX046_REG_RGTCLKZERO, 0x15, I2C_8BIT}, + {IMX046_REG_RGTCLKPRE, 0x03, I2C_8BIT}, + {IMX046_REG_RGTCLKPOST, 0x13, I2C_8BIT}, + {IMX046_REG_RGTCLKTRAIL, 0x05, I2C_8BIT}, + {IMX046_REG_RGTHSEXIT, 0x0B, I2C_8BIT}, + {0x302B, 0x38, I2C_8BIT}, /* for 18Mhz xclk */ + {I2C_REG_TERM, I2C_VAL_TERM, I2C_LEN_TERM} +}; + +/** + * struct imx046_sensor - main structure for storage of sensor information + * @pdata: access functions and data for platform level information + * @v4l2_int_device: V4L2 device structure structure + * @i2c_client: iic client device structure + * @pix: V4L2 pixel format information structure + * @timeperframe: time per frame expressed as V4L fraction + * @scaler: + * @ver: imx046 chip version + * @fps: frames per second value + */ +struct imx046_sensor { + const struct imx046_platform_data *pdata; + struct v4l2_int_device *v4l2_int_device; + struct i2c_client *i2c_client; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; + int scaler; + int ver; + int fps; + int state; + bool resuming; +}; + +static struct imx046_sensor imx046; +static struct i2c_driver imx046sensor_i2c_driver; +static unsigned long xclk_current = IMX046_XCLK_NOM_1; +static enum imx046_image_size isize_current = EIGHT_MP; + +/* list of image formats supported by imx046 sensor */ +const static struct v4l2_fmtdesc imx046_formats[] = { + { + .description = "Bayer10 (RGr/GbB)", + .pixelformat = V4L2_PIX_FMT_SRGGB10, + } +}; + +#define NUM_CAPTURE_FORMATS ARRAY_SIZE(imx046_formats) + +static u32 min_exposure_time = 1000; +static u32 max_exposure_time = 128000; +static enum v4l2_power current_power_state; + +/* Structure of Sensor settings that change with image size */ +static struct imx046_sensor_settings sensor_settings[] = { + /* NOTE: must be in same order as image_size array */ + + /* QUART_MP */ + { + .clk = { + .pre_pll_div = 1, + .pll_mult = 18, + .post_pll_div = 1, + .vt_pix_clk_div = 10, + .vt_sys_clk_div = 1, + }, + .mipi = { + .data_lanes = 1, + .ths_prepare = 2, + .ths_zero = 5, + .ths_settle_lower = 8, + .ths_settle_upper = 28, + }, + .frame = { + .frame_len_lines_min = 629, + .line_len_pck = 3440, + .x_addr_start = 0, + .x_addr_end = 3279, + .y_addr_start = 0, + .y_addr_end = 2463, + .x_output_size = 410, + .y_output_size = 308, + .x_even_inc = 9, + .x_odd_inc = 7, + .y_even_inc = 9, + .y_odd_inc = 7, + .v_mode_add = 0, + .h_mode_add = 0, + .h_add_ave = 1, + }, + }, + + /* HALF_MP */ + { + .clk = { + .pre_pll_div = 1, + .pll_mult = 18, + .post_pll_div = 1, + .vt_pix_clk_div = 10, + .vt_sys_clk_div = 1, + }, + .mipi = { + .data_lanes = 1, + .ths_prepare = 2, + .ths_zero = 5, + .ths_settle_lower = 8, + .ths_settle_upper = 28, + }, + .frame = { + .frame_len_lines_min = 629, + .line_len_pck = 3440, + .x_addr_start = 0, + .x_addr_end = 3279, + .y_addr_start = 0, + .y_addr_end = 2463, + .x_output_size = 820, + .y_output_size = 616, + .x_even_inc = 5, + .x_odd_inc = 3, + .y_even_inc = 5, + .y_odd_inc = 3, + .v_mode_add = 0, + .h_mode_add = 0, + .h_add_ave = 1, + }, + }, + + /* TWO_MP */ + { + .clk = { + .pre_pll_div = 1, + .pll_mult = 18, + .post_pll_div = 1, + .vt_pix_clk_div = 10, + .vt_sys_clk_div = 1, + }, + .mipi = { + .data_lanes = 2, + .ths_prepare = 4, + .ths_zero = 5, + .ths_settle_lower = 13, + .ths_settle_upper = 33, + }, + .frame = { + .frame_len_lines_min = 629, + .line_len_pck = 3440, + .x_addr_start = 0, + .x_addr_end = 3279, + .y_addr_start = 0, + .y_addr_end = 2463, + .x_output_size = 3280, + .y_output_size = 618, + .x_even_inc = 1, + .x_odd_inc = 1, + .y_even_inc = 5, + .y_odd_inc = 3, + .v_mode_add = 0, + .h_mode_add = 0, + .h_add_ave = 0, + }, + }, + + /* EIGHT_MP */ + { + .clk = { + .pre_pll_div = 1, + .pll_mult = 18, + .post_pll_div = 1, + .vt_pix_clk_div = 10, + .vt_sys_clk_div = 1, + }, + .mipi = { + .data_lanes = 2, + .ths_prepare = 4, + .ths_zero = 5, + .ths_settle_lower = 13, + .ths_settle_upper = 33, + }, + .frame = { + .frame_len_lines_min = 2510, + .line_len_pck = 3440, + .x_addr_start = 0, + .x_addr_end = 3279, + .y_addr_start = 0, + .y_addr_end = 2463, + .x_output_size = 3280, + .y_output_size = 2464, + .x_even_inc = 1, + .x_odd_inc = 1, + .y_even_inc = 1, + .y_odd_inc = 1, + .v_mode_add = 0, + .h_mode_add = 0, + .h_add_ave = 0, + }, + }, +}; + +static struct imx046_clock_freq current_clk; + +struct i2c_list { + struct i2c_msg *reg_list; + unsigned int list_size; +}; + +/** + * struct vcontrol - Video controls + * @v4l2_queryctrl: V4L2 VIDIOC_QUERYCTRL ioctl structure + * @current_value: current value of this control + */ +static struct vcontrol { + struct v4l2_queryctrl qc; + int current_value; +} imx046sensor_video_control[] = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = IMX046_MIN_EXPOSURE, + .maximum = IMX046_MAX_EXPOSURE, + .step = IMX046_EXPOSURE_STEP, + .default_value = IMX046_DEF_EXPOSURE, + }, + .current_value = IMX046_DEF_EXPOSURE, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Analog Gain", + .minimum = IMX046_EV_MIN_GAIN, + .maximum = IMX046_EV_MAX_GAIN, + .step = IMX046_EV_GAIN_STEP, + .default_value = IMX046_EV_DEF_GAIN, + }, + .current_value = IMX046_EV_DEF_GAIN, + }, + { + { + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test Pattern", + .minimum = IMX046_MIN_TEST_PATT_MODE, + .maximum = IMX046_MAX_TEST_PATT_MODE, + .step = IMX046_MODE_TEST_PATT_STEP, + .default_value = IMX046_MIN_TEST_PATT_MODE, + }, + .current_value = IMX046_MIN_TEST_PATT_MODE, + } +}; + +/** + * find_vctrl - Finds the requested ID in the video control structure array + * @id: ID of control to search the video control array for + * + * Returns the index of the requested ID from the control structure array + */ +static int +find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(imx046sensor_video_control) - 1); i >= 0; i--) + if (imx046sensor_video_control[i].qc.id == id) + break; + if (i < 0) + i = -EINVAL; + return i; +} + +/** + * imx046_read_reg - Read a value from a register in an imx046 sensor device + * @client: i2c driver client structure + * @data_length: length of data to be read + * @reg: register address / offset + * @val: stores the value that gets read + * + * Read a value from a register in an imx046 sensor device. + * The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int +imx046_read_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 *val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[4]; + + if (!client->adapter) + return -ENODEV; + if (data_length != I2C_8BIT && data_length != I2C_16BIT + && data_length != I2C_32BIT) + return -EINVAL; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + + /* Write addr - high byte goes out first */ + data[0] = (u8) (reg >> 8);; + data[1] = (u8) (reg & 0xff); + err = i2c_transfer(client->adapter, msg, 1); + + /* Read back data */ + if (err >= 0) { + msg->len = data_length; + msg->flags = I2C_M_RD; + err = i2c_transfer(client->adapter, msg, 1); + } + if (err >= 0) { + *val = 0; + /* high byte comes first */ + if (data_length == I2C_8BIT) + *val = data[0]; + else if (data_length == I2C_16BIT) + *val = data[1] + (data[0] << 8); + else + *val = data[3] + (data[2] << 8) + + (data[1] << 16) + (data[0] << 24); + return 0; + } + v4l_err(client, "read from offset 0x%x error %d", reg, err); + return err; +} + +/** + * Write a value to a register in imx046 sensor device. + * @client: i2c driver client structure. + * @reg: Address of the register to read value from. + * @val: Value to be written to a specific register. + * Returns zero if successful, or non-zero otherwise. + */ +static int imx046_write_reg(struct i2c_client *client, u16 reg, + u32 val, u16 data_length) +{ + int err = 0; + struct i2c_msg msg[1]; + unsigned char data[6]; + int retries = 0; + + if (!client->adapter) + return -ENODEV; + + if (data_length != I2C_8BIT && data_length != I2C_16BIT + && data_length != I2C_32BIT) + return -EINVAL; + +retry: + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = data_length+2; /* add address bytes */ + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + if (data_length == I2C_8BIT) { + data[2] = val & 0xff; + } else if (data_length == I2C_16BIT) { + data[2] = (val >> 8) & 0xff; + data[3] = val & 0xff; + } else { + data[2] = (val >> 24) & 0xff; + data[3] = (val >> 16) & 0xff; + data[4] = (val >> 8) & 0xff; + data[5] = val & 0xff; + } + + if (data_length == 1) + dev_dbg(&client->dev, "IMX046 Wrt:[0x%04X]=0x%02X\n", + reg, val); + else if (data_length == 2) + dev_dbg(&client->dev, "IMX046 Wrt:[0x%04X]=0x%04X\n", + reg, val); + + err = i2c_transfer(client->adapter, msg, 1); + + if (err >= 0) + return 0; + + if (retries <= 5) { + v4l_info(client, "Retrying I2C... %d", retries); + retries++; + mdelay(20); + goto retry; + } + + return err; +} + +/** + * Initialize a list of imx046 registers. + * The list of registers is terminated by the pair of values + * {OV3640_REG_TERM, OV3640_VAL_TERM}. + * @client: i2c driver client structure. + * @reglist[]: List of address of the registers to write data. + * Returns zero if successful, or non-zero otherwise. + */ +static int imx046_write_regs(struct i2c_client *client, + const struct imx046_reg reglist[]) +{ + int err = 0; + const struct imx046_reg *list = reglist; + + while (!((list->reg == I2C_REG_TERM) + && (list->val == I2C_VAL_TERM))) { + err = imx046_write_reg(client, list->reg, + list->val, list->length); + if (err) + return err; + list++; + } + return 0; +} + +/** + * imx046_find_size - Find the best match for a requested image capture size + * @width: requested image width in pixels + * @height: requested image height in pixels + * + * Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + * Since the available sizes are subsampled in the vertical direction only, + * the routine will find the size with a height that is equal to or less + * than the requested height. + */ +static enum imx046_image_size imx046_find_size(unsigned int width, + unsigned int height) +{ + enum imx046_image_size isize; + + for (isize = QUART_MP; isize <= EIGHT_MP; isize++) { + if ((imx046_sizes[isize].height >= height) && + (imx046_sizes[isize].width >= width)) { + break; + } + } + + printk(KERN_DEBUG "imx046_find_size: Req Size=%dx%d, " + "Calc Size=%dx%d\n", + width, height, (int)imx046_sizes[isize].width, + (int)imx046_sizes[isize].height); + + return isize; +} + +/** + * Set CSI2 Virtual ID. + * @client: i2c client driver structure + * @id: Virtual channel ID. + * + * Sets the channel ID which identifies data packets sent by this device + * on the CSI2 bus. + **/ +static int imx046_set_virtual_id(struct i2c_client *client, u32 id) +{ + return imx046_write_reg(client, IMX046_REG_CCP2_CHANNEL_ID, + (0x3 & id), I2C_8BIT); +} + +/** + * imx046_set_framerate - Sets framerate by adjusting frame_length_lines reg. + * @s: pointer to standard V4L2 device structure + * @fper: frame period numerator and denominator in seconds + * + * The maximum exposure time is also updated since it is affected by the + * frame rate. + **/ +static int imx046_set_framerate(struct v4l2_int_device *s, + struct v4l2_fract *fper) +{ + int err = 0; + u16 isize = isize_current; + u32 frame_length_lines, line_time_q8; + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + struct imx046_sensor_settings *ss; + + if ((fper->numerator == 0) || (fper->denominator == 0)) { + /* supply a default nominal_timeperframe */ + fper->numerator = 1; + fper->denominator = IMX046_DEF_FPS; + } + + sensor->fps = fper->denominator / fper->numerator; + if (sensor->fps < IMX046_MIN_FPS) { + sensor->fps = IMX046_MIN_FPS; + fper->numerator = 1; + fper->denominator = sensor->fps; + } else if (sensor->fps > IMX046_MAX_FPS) { + sensor->fps = IMX046_MAX_FPS; + fper->numerator = 1; + fper->denominator = sensor->fps; + } + + ss = &sensor_settings[isize_current]; + + line_time_q8 = ((u32)ss->frame.line_len_pck * 1000000) / + (current_clk.vt_pix_clk >> 8); /* usec's */ + + frame_length_lines = (((u32)fper->numerator * 1000000 * 256 / + fper->denominator)) / line_time_q8; + + /* Range check frame_length_lines */ + if (frame_length_lines > IMX046_MAX_FRAME_LENGTH_LINES) + frame_length_lines = IMX046_MAX_FRAME_LENGTH_LINES; + else if (frame_length_lines < ss->frame.frame_len_lines_min) + frame_length_lines = ss->frame.frame_len_lines_min; + + imx046_write_reg(client, IMX046_REG_FRAME_LEN_LINES, + frame_length_lines, I2C_16BIT); + + sensor_settings[isize].frame.frame_len_lines = frame_length_lines; + + /* Update max exposure time */ + max_exposure_time = (line_time_q8 * (frame_length_lines - 1)) >> 8; + + printk(KERN_DEBUG "IMX046 Set Framerate: fper=%d/%d, " + "frame_len_lines=%d, max_expT=%dus\n", fper->numerator, + fper->denominator, frame_length_lines, max_exposure_time); + + return err; +} + +/** + * imx046sensor_calc_xclk - Calculate the required xclk frequency + * + * Xclk is not determined from framerate for the IMX046 + */ +static unsigned long imx046sensor_calc_xclk(void) +{ + xclk_current = IMX046_XCLK_NOM_1; + + return xclk_current; +} + +/** + * Sets the correct orientation based on the sensor version. + * IU046F2-Z version=2 orientation=3 + * IU046F4-2D version>2 orientation=0 + */ +static int imx046_set_orientation(struct i2c_client *client, u32 ver) +{ + int err; + u8 orient; + + orient = (ver <= 0x2) ? 0x3 : 0x0; + err = imx046_write_reg(client, IMX046_REG_IMAGE_ORIENTATION, + orient, I2C_8BIT); + return err; +} + +/** + * imx046sensor_set_exposure_time - sets exposure time per input value + * @exp_time: exposure time to be set on device (in usec) + * @s: pointer to standard V4L2 device structure + * @lvc: pointer to V4L2 exposure entry in imx046sensor_video_controls array + * + * If the requested exposure time is within the allowed limits, the HW + * is configured to use the new exposure time, and the + * imx046sensor_video_control[] array is updated with the new current value. + * The function returns 0 upon success. Otherwise an error code is + * returned. + */ +int imx046sensor_set_exposure_time(u32 exp_time, struct v4l2_int_device *s, + struct vcontrol *lvc) +{ + int err = 0, i; + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + u16 coarse_int_time = 0; + u32 line_time_q8 = 0; + struct imx046_sensor_settings *ss; + + if ((current_power_state == V4L2_POWER_ON) || sensor->resuming) { + if (exp_time < min_exposure_time) { + v4l_err(client, "Exposure time %d us not within" + " the legal range.\n", exp_time); + v4l_err(client, "Exposure time must be between" + " %d us and %d us\n", + min_exposure_time, max_exposure_time); + exp_time = min_exposure_time; + } + + if (exp_time > max_exposure_time) { + v4l_err(client, "Exposure time %d us not within" + " the legal range.\n", exp_time); + v4l_err(client, "Exposure time must be between" + " %d us and %d us\n", + min_exposure_time, max_exposure_time); + exp_time = max_exposure_time; + } + + ss = &sensor_settings[isize_current]; + + line_time_q8 = + ((u32)ss->frame.line_len_pck * 1000000) / + (current_clk.vt_pix_clk >> 8); /* usec's */ + + coarse_int_time = ((exp_time * 256) + (line_time_q8 >> 1)) / + line_time_q8; + + if (coarse_int_time > ss->frame.frame_len_lines - 2) + coarse_int_time = ss->frame.frame_len_lines - 2; + + err = imx046_write_reg(client, IMX046_REG_COARSE_INT_TIME, + coarse_int_time, I2C_16BIT); + } + + if (err) { + v4l_err(client, "Error setting exposure time: %d", err); + } else { + i = find_vctrl(V4L2_CID_EXPOSURE); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = exp_time; + } + } + + return err; +} + +/** + * This table describes what should be written to the sensor register for each + * gain value. The gain(index in the table) is in terms of 0.1EV, i.e. 10 + * indexes in the table give 2 time more gain + * + * Elements in TS2_8_GAIN_TBL doesn't comply linearity. This is because + * there is nonlinear dependecy between analogue_gain_code_global and real gain + * value: Gain_analog = 256 / (256 - analogue_gain_code_global) + */ + +const u16 IMX046_EV_GAIN_TBL[IMX046_EV_TABLE_GAIN_MAX + 1] = { + /* Gain x1 */ + 0, 16, 33, 48, + 62, 74, 88, 98, + 109, 119, + + /* Gain x2 */ + 128, 136, 144, 152, + 159, 165, 171, 177, + 182, 187, + + /* Gain x4 */ + 192, 196, 200, 204, + 208, 211, 214, 216, + 219, 222, + + /* Gain x8 */ + 224 +}; + +/** + * imx046sensor_set_gain - sets sensor analog gain per input value + * @gain: analog gain value to be set on device + * @s: pointer to standard V4L2 device structure + * @lvc: pointer to V4L2 analog gain entry in imx046sensor_video_control array + * + * If the requested analog gain is within the allowed limits, the HW + * is configured to use the new gain value, and the imx046sensor_video_control + * array is updated with the new current value. + * The function returns 0 upon success. Otherwise an error code is + * returned. + */ +int imx046sensor_set_gain(u16 lineargain, struct v4l2_int_device *s, + struct vcontrol *lvc) +{ + int err = 0, i; + u16 reg_gain = 0; + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + + if (current_power_state == V4L2_POWER_ON || sensor->resuming) { + + if (lineargain < IMX046_EV_MIN_GAIN) { + lineargain = IMX046_EV_MIN_GAIN; + v4l_err(client, "Gain out of legal range."); + } + if (lineargain > IMX046_EV_MAX_GAIN) { + lineargain = IMX046_EV_MAX_GAIN; + v4l_err(client, "Gain out of legal range."); + } + + reg_gain = IMX046_EV_GAIN_TBL[lineargain]; + + err = imx046_write_reg(client, IMX046_REG_ANALOG_GAIN_GLOBAL, + reg_gain, I2C_16BIT); + } + + if (err) { + v4l_err(client, "Error setting analog gain: %d", err); + } else { + i = find_vctrl(V4L2_CID_GAIN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = lineargain; + } + } + + return err; +} + +/** + * imx046_update_clocks - calcs sensor clocks based on sensor settings. + * @isize: image size enum + */ +int imx046_update_clocks(u32 xclk, enum imx046_image_size isize) +{ + current_clk.vco_clk = + xclk * sensor_settings[isize].clk.pll_mult / + sensor_settings[isize].clk.pre_pll_div / + sensor_settings[isize].clk.post_pll_div; + + current_clk.vt_pix_clk = current_clk.vco_clk * 2 / + (sensor_settings[isize].clk.vt_pix_clk_div * + sensor_settings[isize].clk.vt_sys_clk_div); + + if (sensor_settings[isize].mipi.data_lanes == 2) + current_clk.mipi_clk = current_clk.vco_clk; + else + current_clk.mipi_clk = current_clk.vco_clk / 2; + + current_clk.ddr_clk = current_clk.mipi_clk / 2; + + printk(KERN_DEBUG "IMX046: xclk=%u, vco_clk=%u, " + "vt_pix_clk=%u, mipi_clk=%u, ddr_clk=%u\n", + xclk, current_clk.vco_clk, current_clk.vt_pix_clk, + current_clk.mipi_clk, current_clk.ddr_clk); + + return 0; +} + +/** + * imx046_setup_pll - initializes sensor PLL registers. + * @c: i2c client driver structure + * @isize: image size enum + */ +int imx046_setup_pll(struct i2c_client *client, enum imx046_image_size isize) +{ + u32 rgpltd_reg; + u32 rgpltd[3] = {2, 0, 1}; + + imx046_write_reg(client, IMX046_REG_PRE_PLL_CLK_DIV, + sensor_settings[isize].clk.pre_pll_div, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_PLL_MULTIPLIER, + sensor_settings[isize].clk.pll_mult, I2C_16BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_RGPLTD_RGCLKEN, + &rgpltd_reg); + rgpltd_reg &= ~RGPLTD_MASK; + rgpltd_reg |= rgpltd[sensor_settings[isize].clk.post_pll_div >> 1]; + imx046_write_reg(client, IMX046_REG_RGPLTD_RGCLKEN, + rgpltd_reg, I2C_8BIT); + + imx046_write_reg(client, IMX046_REG_VT_PIX_CLK_DIV, + sensor_settings[isize].clk.vt_pix_clk_div, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_VT_SYS_CLK_DIV, + sensor_settings[isize].clk.vt_sys_clk_div, I2C_16BIT); + + printk(KERN_DEBUG "IMX046: pre_pll_clk_div=%u, pll_mult=%u, " + "rgpltd=0x%x, vt_pix_clk_div=%u, vt_sys_clk_div=%u\n", + sensor_settings[isize].clk.pre_pll_div, + sensor_settings[isize].clk.pll_mult, rgpltd_reg, + sensor_settings[isize].clk.vt_pix_clk_div, + sensor_settings[isize].clk.vt_sys_clk_div); + + return 0; +} + +/** + * imx046_setup_mipi - initializes sensor & isp MIPI registers. + * @c: i2c client driver structure + * @isize: image size enum + */ +int imx046_setup_mipi(struct imx046_sensor *sensor, + enum imx046_image_size isize) +{ + struct i2c_client *client = sensor->i2c_client; + + /* NOTE: Make sure imx046_update_clocks is called 1st */ + + /* Enable MIPI */ + imx046_write_reg(client, IMX046_REG_RGOUTSEL1, 0x00, I2C_8BIT); + imx046_write_reg(client, IMX046_REG_TESTDI, 0x04, I2C_8BIT); + + /* Set sensor Mipi timing params */ + imx046_write_reg(client, IMX046_REG_RGTHSTRAIL, 0x06, I2C_8BIT); + + imx046_write_reg(client, IMX046_REG_RGTHSPREPARE, + sensor_settings[isize].mipi.ths_prepare, I2C_8BIT); + + imx046_write_reg(client, IMX046_REG_RGTHSZERO, + sensor_settings[isize].mipi.ths_zero, I2C_8BIT); + + /* Set number of lanes in sensor */ + if (sensor_settings[isize].mipi.data_lanes == 2) + imx046_write_reg(client, IMX046_REG_RGLANESEL, 0x00, I2C_8BIT); + else + imx046_write_reg(client, IMX046_REG_RGLANESEL, 0x01, I2C_8BIT); + + /* Set number of lanes in isp */ + sensor->pdata->csi2_lane_count(sensor_settings[isize].mipi.data_lanes); + + /* Send settings to ISP-CSI2 Receiver PHY */ + sensor->pdata->csi2_calc_phy_cfg0(current_clk.mipi_clk, + sensor_settings[isize].mipi.ths_settle_lower, + sensor_settings[isize].mipi.ths_settle_upper); + + /* Dump some registers for debug purposes */ + printk(KERN_DEBUG "imx:THSPREPARE=0x%02X\n", + sensor_settings[isize].mipi.ths_prepare); + printk(KERN_DEBUG "imx:THSZERO=0x%02X\n", + sensor_settings[isize].mipi.ths_zero); + printk(KERN_DEBUG "imx:LANESEL=0x%02X\n", + (sensor_settings[isize].mipi.data_lanes == 2) ? 0 : 1); + + return 0; +} + +/** + * imx046_configure_frame - initializes image frame registers + * @c: i2c client driver structure + * @isize: image size enum + */ +int imx046_configure_frame(struct i2c_client *client, + enum imx046_image_size isize) +{ + u32 val; + + imx046_write_reg(client, IMX046_REG_FRAME_LEN_LINES, + sensor_settings[isize].frame.frame_len_lines_min, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_LINE_LEN_PCK, + sensor_settings[isize].frame.line_len_pck, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_ADDR_START, + sensor_settings[isize].frame.x_addr_start, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_ADDR_END, + sensor_settings[isize].frame.x_addr_end, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_ADDR_START, + sensor_settings[isize].frame.y_addr_start, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_ADDR_END, + sensor_settings[isize].frame.y_addr_end, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_OUTPUT_SIZE, + sensor_settings[isize].frame.x_output_size, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_OUTPUT_SIZE, + sensor_settings[isize].frame.y_output_size, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_EVEN_INC, + sensor_settings[isize].frame.x_even_inc, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_ODD_INC, + sensor_settings[isize].frame.x_odd_inc, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_EVEN_INC, + sensor_settings[isize].frame.y_even_inc, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_ODD_INC, + sensor_settings[isize].frame.y_odd_inc, I2C_16BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_PGACUR_VMODEADD, &val); + val &= ~VMODEADD_MASK; + val |= sensor_settings[isize].frame.v_mode_add << VMODEADD_SHIFT; + imx046_write_reg(client, IMX046_REG_PGACUR_VMODEADD, val, I2C_8BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_HMODEADD, &val); + val &= ~HMODEADD_MASK; + val |= sensor_settings[isize].frame.h_mode_add << HMODEADD_SHIFT; + imx046_write_reg(client, IMX046_REG_HMODEADD, val, I2C_8BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_HADDAVE, &val); + val &= ~HADDAVE_MASK; + val |= sensor_settings[isize].frame.h_add_ave << HADDAVE_SHIFT; + imx046_write_reg(client, IMX046_REG_HADDAVE, val, I2C_8BIT); + + return 0; +} + + /** + * imx046_configure_test_pattern - Configure 3 possible test pattern modes + * @ mode: Test pattern mode. Possible modes : 1 , 2 and 4. + * @s: pointer to standard V4L2 device structure + * @lvc: pointer to V4L2 exposure entry in imx046sensor_video_controls array + * + * If the requested test pattern mode is within the allowed limits, the HW + * is configured for that particular test pattern, and the + * imx046sensor_video_control[] array is updated with the new current value. + * The function returns 0 upon success. Otherwise an error code is + * returned. + */ +int imx046_configure_test_pattern(int mode, struct v4l2_int_device *s, + struct vcontrol *lvc) +{ + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + + if ((current_power_state == V4L2_POWER_ON) || sensor->resuming) { + + switch (mode) { + case IMX046_TEST_PATT_COLOR_BAR: + case IMX046_TEST_PATT_PN9: + /* red */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_RED, + 0x07ff, I2C_16BIT); + /* green-red */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_GREENR, + 0x00ff, I2C_16BIT); + /* blue */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_BLUE, + 0x0000, I2C_16BIT); + /* green-blue */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_GREENB, + 0x0000, I2C_16BIT); + break; + case IMX046_TEST_PATT_SOLID_COLOR: + /* red */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_RED, + (IMX046_BLACK_LEVEL_AVG & 0x00ff), I2C_16BIT); + /* green-red */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_GREENR, + (IMX046_BLACK_LEVEL_AVG & 0x00ff), I2C_16BIT); + /* blue */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_BLUE, + (IMX046_BLACK_LEVEL_AVG & 0x00ff), I2C_16BIT); + /* green-blue */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_GREENB, + (IMX046_BLACK_LEVEL_AVG & 0x00ff), I2C_16BIT); + break; + } + /* test-pattern mode */ + imx046_write_reg(client, IMX046_REG_TEST_PATT_MODE, + (mode & 0x7), I2C_16BIT); + /* Disable sensor ISP processing */ + imx046_write_reg(client, IMX046_REG_TESBYPEN, + (mode == 0) ? 0x0 : 0x10, I2C_8BIT); + } + lvc->current_value = mode; + return 0; +} +/** + * imx046_configure - Configure the imx046 for the specified image mode + * @s: pointer to standard V4L2 device structure + * + * Configure the imx046 for a specified image size, pixel format, and frame + * period. xclk is the frequency (in Hz) of the xclk input to the imx046. + * fper is the frame period (in seconds) expressed as a fraction. + * Returns zero if successful, or non-zero otherwise. + * The actual frame period is returned in fper. + */ +static int imx046_configure(struct v4l2_int_device *s) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &sensor->pix; + struct i2c_client *client = sensor->i2c_client; + enum imx046_image_size isize; + int err, i; + struct vcontrol *lvc = NULL; + + isize = imx046_find_size(pix->width, pix->height); + isize_current = isize; + + err = imx046_write_reg(client, IMX046_REG_SW_RESET, 0x01, I2C_8BIT); + mdelay(5); + + imx046_write_regs(client, initial_list); + + imx046_update_clocks(xclk_current, isize); + imx046_setup_pll(client, isize); + + imx046_setup_mipi(sensor, isize); + + /* configure image size and pixel format */ + imx046_configure_frame(client, isize); + + /* Setting of frame rate */ + err = imx046_set_framerate(s, &sensor->timeperframe); + + imx046_set_orientation(client, sensor->ver); + + sensor->pdata->csi2_cfg_vp_out_ctrl(2); + sensor->pdata->csi2_ctrl_update(false); + + sensor->pdata->csi2_cfg_virtual_id(0, IMX046_CSI2_VIRTUAL_ID); + sensor->pdata->csi2_ctx_update(0, false); + imx046_set_virtual_id(client, IMX046_CSI2_VIRTUAL_ID); + + /* Set initial exposure and gain */ + i = find_vctrl(V4L2_CID_EXPOSURE); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + imx046sensor_set_exposure_time(lvc->current_value, + sensor->v4l2_int_device, lvc); + } + + i = find_vctrl(V4L2_CID_GAIN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + imx046sensor_set_gain(lvc->current_value, + sensor->v4l2_int_device, lvc); + } + + i = find_vctrl(V4L2_CID_TEST_PATTERN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + imx046_configure_test_pattern(lvc->current_value, + sensor->v4l2_int_device, lvc); + } + /* configure streaming ON */ + err = imx046_write_reg(client, IMX046_REG_MODE_SELECT, 0x01, I2C_8BIT); + mdelay(1); + + return err; +} + +/** + * imx046_detect - Detect if an imx046 is present, and if so which revision + * @client: pointer to the i2c client driver structure + * + * Detect if an imx046 is present, and if so which revision. + * A device is considered to be detected if the manufacturer ID (MIDH and MIDL) + * and the product ID (PID) registers match the expected values. + * Any value of the version ID (VER) register is accepted. + * Returns a negative error number if no device is detected, or the + * non-negative value of the version ID register if a device is detected. + */ +static int +imx046_detect(struct i2c_client *client) +{ + u32 model_id, mfr_id, rev; + struct imx046_sensor *sensor; + + if (!client) + return -ENODEV; + + sensor = i2c_get_clientdata(client); + + if (imx046_read_reg(client, I2C_16BIT, IMX046_REG_MODEL_ID, &model_id)) + return -ENODEV; + if (imx046_read_reg(client, I2C_8BIT, IMX046_REG_MFR_ID, &mfr_id)) + return -ENODEV; + if (imx046_read_reg(client, I2C_8BIT, IMX046_REG_REV_NUMBER, &rev)) + return -ENODEV; + + v4l_info(client, "model id detected 0x%x mfr 0x%x, rev# 0x%x\n", + model_id, mfr_id, rev); + if ((model_id != IMX046_MOD_ID) || (mfr_id != IMX046_MFR_ID)) { + /* We didn't read the values we expected, so + * this must not be an IMX046. + */ + v4l_warn(client, "model id mismatch 0x%x mfr 0x%x\n", + model_id, mfr_id); + + return -ENODEV; + } + return rev; +} + +/** + * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the imx046sensor_video_control[] array. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int ioctl_queryctrl(struct v4l2_int_device *s, + struct v4l2_queryctrl *qc) +{ + int i; + + i = find_vctrl(qc->id); + if (i == -EINVAL) + qc->flags = V4L2_CTRL_FLAG_DISABLED; + + if (i < 0) + return -EINVAL; + + *qc = imx046sensor_video_control[i].qc; + return 0; +} + +/** + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the imx046sensor_video_control[] array. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct vcontrol *lvc; + int i; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &imx046sensor_video_control[i]; + + switch (vc->id) { + case V4L2_CID_EXPOSURE: + vc->value = lvc->current_value; + break; + case V4L2_CID_GAIN: + vc->value = lvc->current_value; + break; + case V4L2_CID_TEST_PATTERN: + vc->value = lvc->current_value; + break; + } + + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the imx046sensor_video_control[] array). + * Otherwise, * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + int retval = -EINVAL; + int i; + struct vcontrol *lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &imx046sensor_video_control[i]; + + switch (vc->id) { + case V4L2_CID_EXPOSURE: + retval = imx046sensor_set_exposure_time(vc->value, s, lvc); + break; + case V4L2_CID_GAIN: + retval = imx046sensor_set_gain(vc->value, s, lvc); + break; + case V4L2_CID_TEST_PATTERN: + retval = imx046_configure_test_pattern(vc->value, s, lvc); + break; + } + + return retval; +} + +/** + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + default: + return -EINVAL; + } + + fmt->flags = imx046_formats[index].flags; + strlcpy(fmt->description, imx046_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = imx046_formats[index].pixelformat; + + return 0; +} + +/** + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + enum imx046_image_size isize; + int ifmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct imx046_sensor *sensor = s->priv; + struct v4l2_pix_format *pix2 = &sensor->pix; + + isize = imx046_find_size(pix->width, pix->height); + + pix->width = imx046_sizes[isize].width; + pix->height = imx046_sizes[isize].height; + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (pix->pixelformat == imx046_formats[ifmt].pixelformat) + break; + } + if (ifmt == NUM_CAPTURE_FORMATS) + ifmt = 0; + pix->pixelformat = imx046_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + pix->colorspace = V4L2_COLORSPACE_SRGB; + *pix2 = *pix; + return 0; +} + +/** + * ioctl_s_fmt_cap - V4L2 sensor interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + int rval; + + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + else + sensor->pix = *pix; + + + return rval; +} + +/** + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct imx046_sensor *sensor = s->priv; + f->fmt.pix = sensor->pix; + + return 0; +} + +/** + * ioctl_g_pixclk - V4L2 sensor interface handler for ioctl_g_pixclk + * @s: pointer to standard V4L2 device structure + * @pixclk: pointer to unsigned 32 var to store pixelclk in HZ + * + * Returns the sensor's current pixel clock in HZ + */ +static int ioctl_priv_g_pixclk(struct v4l2_int_device *s, u32 *pixclk) +{ + *pixclk = xclk_current; + + return 0; +} + +/** + * ioctl_g_activesize - V4L2 sensor interface handler for ioctl_g_activesize + * @s: pointer to standard V4L2 device structure + * @pix: pointer to standard V4L2 v4l2_pix_format structure + * + * Returns the sensor's current active image basesize. + */ +static int ioctl_priv_g_activesize(struct v4l2_int_device *s, + struct v4l2_pix_format *pix) +{ + struct imx046_sensor *sensor = s->priv; + + pix->width = sensor->pix.width; + pix->height = sensor->pix.height; + + return 0; +} + +/** + * ioctl_g_fullsize - V4L2 sensor interface handler for ioctl_g_fullsize + * @s: pointer to standard V4L2 device structure + * @pix: pointer to standard V4L2 v4l2_pix_format structure + * + * Returns the sensor's biggest image basesize. + */ +static int ioctl_priv_g_fullsize(struct v4l2_int_device *s, + struct v4l2_pix_format *pix) +{ + pix->width = imx046_sizes[NUM_IMAGE_SIZES - 1].width; + pix->height = imx046_sizes[NUM_IMAGE_SIZES - 1].height; + + return 0; +} + +/** + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = sensor->timeperframe; + + return 0; +} + +/** + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + + sensor->timeperframe = *timeperframe; + imx046sensor_calc_xclk(); + *timeperframe = sensor->timeperframe; + + return 0; +} + + +/** + * ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold sensor's private data address + * + * Returns device's (sensor's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct imx046_sensor *sensor = s->priv; + + return sensor->pdata->priv_data_set(s, p); + +} + +/** + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +{ + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + struct vcontrol *lvc; + int rval, i; + + if ((on == V4L2_POWER_STANDBY) && (sensor->state == SENSOR_DETECTED)) { + /* imx046_write_regs(c, stream_off_list, + I2C_STREAM_OFF_LIST_SIZE); */ + } + + if (on != V4L2_POWER_ON) + sensor->pdata->set_xclk(s, 0); + else + sensor->pdata->set_xclk(s, xclk_current); + + rval = sensor->pdata->power_set(s, on); + if (rval < 0) { + v4l_err(client, "Unable to set the power state: " + IMX046_DRIVER_NAME " sensor\n"); + sensor->pdata->set_xclk(s, 0); + return rval; + } + + if ((current_power_state == V4L2_POWER_STANDBY) && + (on == V4L2_POWER_ON) && + (sensor->state == SENSOR_DETECTED)) { + sensor->resuming = true; + imx046_configure(s); + } + + if ((on == V4L2_POWER_ON) && (sensor->state == SENSOR_NOT_DETECTED)) { + rval = imx046_detect(client); + if (rval < 0) { + v4l_err(client, "Unable to detect " + IMX046_DRIVER_NAME " sensor\n"); + sensor->state = SENSOR_NOT_DETECTED; + return rval; + } + sensor->state = SENSOR_DETECTED; + sensor->ver = rval; + v4l_info(client, IMX046_DRIVER_NAME + " chip version 0x%02x detected\n", sensor->ver); + } + + if (on == V4L2_POWER_OFF) { + /* Reset defaults for controls */ + i = find_vctrl(V4L2_CID_GAIN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = IMX046_EV_DEF_GAIN; + } + i = find_vctrl(V4L2_CID_EXPOSURE); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = IMX046_DEF_EXPOSURE; + } + i = find_vctrl(V4L2_CID_TEST_PATTERN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = IMX046_MIN_TEST_PATT_MODE; + } + } + + sensor->resuming = false; + current_power_state = on; + return 0; +} + +/** + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the sensor device (call imx046_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * imx046 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + int err; + + err = imx046_detect(client); + if (err < 0) { + v4l_err(client, "Unable to detect " IMX046_DRIVER_NAME + " sensor\n"); + return err; + } + + sensor->ver = err; + v4l_info(client, IMX046_DRIVER_NAME " chip version " + "0x%02x detected\n", sensor->ver); + + return 0; +} + +/** + * ioctl_enum_framesizes - V4L2 sensor if handler for vidioc_int_enum_framesizes + * @s: pointer to standard V4L2 device structure + * @frms: pointer to standard V4L2 framesizes enumeration structure + * + * Returns possible framesizes depending on choosen pixel format + **/ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *frms) +{ + int ifmt; + + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (frms->pixel_format == imx046_formats[ifmt].pixelformat) + break; + } + /* Is requested pixelformat not found on sensor? */ + if (ifmt == NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Check that the index we are being asked for is not + out of bounds. */ + if (frms->index >= ARRAY_SIZE(imx046_sizes)) + return -EINVAL; + + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = imx046_sizes[frms->index].width; + frms->discrete.height = imx046_sizes[frms->index].height; + + return 0; +} + +const struct v4l2_fract imx046_frameintervals[] = { + { .numerator = 3, .denominator = 30 }, + { .numerator = 1, .denominator = 30 }, +}; + +static int ioctl_enum_frameintervals(struct v4l2_int_device *s, + struct v4l2_frmivalenum *frmi) +{ + int ifmt; + + /* Check that the requested format is one we support */ + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (frmi->pixel_format == imx046_formats[ifmt].pixelformat) + break; + } + + if (ifmt == NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Check that the index we are being asked for is not + out of bounds. */ + if (frmi->index >= ARRAY_SIZE(imx046_frameintervals)) + return -EINVAL; + + /* Make sure that the 8MP size reports a max of 10fps */ + if (frmi->width == 3280 && frmi->height == 2464) { + if (frmi->index != 0) + return -EINVAL; + } + + frmi->type = V4L2_FRMIVAL_TYPE_DISCRETE; + frmi->discrete.numerator = + imx046_frameintervals[frmi->index].numerator; + frmi->discrete.denominator = + imx046_frameintervals[frmi->index].denominator; + + return 0; +} + +static struct v4l2_int_ioctl_desc imx046_ioctl_desc[] = { + { .num = vidioc_int_enum_framesizes_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_framesizes}, + { .num = vidioc_int_enum_frameintervals_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_frameintervals}, + { .num = vidioc_int_dev_init_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_init}, + { .num = vidioc_int_dev_exit_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_exit}, + { .num = vidioc_int_s_power_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_power }, + { .num = vidioc_int_g_priv_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_priv }, + { .num = vidioc_int_init_num, + .func = (v4l2_int_ioctl_func *)ioctl_init }, + { .num = vidioc_int_enum_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, + { .num = vidioc_int_try_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, + { .num = vidioc_int_g_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, + { .num = vidioc_int_s_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, + { .num = vidioc_int_g_parm_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_parm }, + { .num = vidioc_int_s_parm_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_parm }, + { .num = vidioc_int_queryctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_queryctrl }, + { .num = vidioc_int_g_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { .num = vidioc_int_s_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_ctrl }, + { .num = vidioc_int_priv_g_pixclk_num, + .func = (v4l2_int_ioctl_func *)ioctl_priv_g_pixclk }, + { .num = vidioc_int_priv_g_activesize_num, + .func = (v4l2_int_ioctl_func *)ioctl_priv_g_activesize }, + { .num = vidioc_int_priv_g_fullsize_num, + .func = (v4l2_int_ioctl_func *)ioctl_priv_g_fullsize }, +}; + +static struct v4l2_int_slave imx046_slave = { + .ioctls = imx046_ioctl_desc, + .num_ioctls = ARRAY_SIZE(imx046_ioctl_desc), +}; + +static struct v4l2_int_device imx046_int_device = { + .module = THIS_MODULE, + .name = IMX046_DRIVER_NAME, + .priv = &imx046, + .type = v4l2_int_type_slave, + .u = { + .slave = &imx046_slave, + }, +}; + +/** + * imx046_probe - sensor driver i2c probe handler + * @client: i2c driver client device structure + * + * Register sensor as an i2c client device and V4L2 + * device. + */ +static int __devinit imx046_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct imx046_sensor *sensor = &imx046; + int err; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + sensor->pdata = client->dev.platform_data; + + if (!sensor->pdata) { + v4l_err(client, "no platform data?\n"); + return -ENODEV; + } + + sensor->v4l2_int_device = &imx046_int_device; + sensor->i2c_client = client; + + i2c_set_clientdata(client, sensor); + + /* Make the default capture format QCIF V4L2_PIX_FMT_SRGGB10 */ + sensor->pix.width = IMX046_IMAGE_WIDTH_MAX; + sensor->pix.height = IMX046_IMAGE_HEIGHT_MAX; + sensor->pix.pixelformat = V4L2_PIX_FMT_SRGGB10; + + err = v4l2_int_device_register(sensor->v4l2_int_device); + if (err) + i2c_set_clientdata(client, NULL); + + return 0; +} + +/** + * imx046_remove - sensor driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister sensor as an i2c client device and V4L2 + * device. Complement of imx046_probe(). + */ +static int __exit +imx046_remove(struct i2c_client *client) +{ + struct imx046_sensor *sensor = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(sensor->v4l2_int_device); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id imx046_id[] = { + { IMX046_DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, imx046_id); + +static struct i2c_driver imx046sensor_i2c_driver = { + .driver = { + .name = IMX046_DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = imx046_probe, + .remove = __exit_p(imx046_remove), + .id_table = imx046_id, +}; + +static struct imx046_sensor imx046 = { + .timeperframe = { + .numerator = 1, + .denominator = 30, + }, + .state = SENSOR_NOT_DETECTED, +}; + +/** + * imx046sensor_init - sensor driver module_init handler + * + * Registers driver as an i2c client driver. Returns 0 on success, + * error code otherwise. + */ +static int __init imx046sensor_init(void) +{ + int err; + + err = i2c_add_driver(&imx046sensor_i2c_driver); + if (err) { + printk(KERN_ERR "Failed to register" IMX046_DRIVER_NAME ".\n"); + return err; + } + return 0; +} +late_initcall(imx046sensor_init); + +/** + * imx046sensor_cleanup - sensor driver module_exit handler + * + * Unregisters/deletes driver as an i2c client driver. + * Complement of imx046sensor_init. + */ +static void __exit imx046sensor_cleanup(void) +{ + i2c_del_driver(&imx046sensor_i2c_driver); +} +module_exit(imx046sensor_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("imx046 camera sensor driver"); diff --git a/drivers/media/video/imx046_regs.h b/drivers/media/video/imx046_regs.h new file mode 100644 index 00000000000..9824619d08e --- /dev/null +++ b/drivers/media/video/imx046_regs.h @@ -0,0 +1,314 @@ +/* + * imx046_regs.h + * + * Register definitions for the IMX046 Sensor. + * + * Leverage MT9P012.h + * + * Copyright (C) 2008 Hewlett Packard. + * + * 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 IMX046_REGS_H +#define IMX046_REGS_H + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_PRIVATE_BASE + 1) + +/* The ID values we are looking for */ +#define IMX046_MOD_ID 0x0046 +#define IMX046_MFR_ID 0x000B + +/* IMX046 has 8/16/32 I2C registers */ +#define I2C_8BIT 1 +#define I2C_16BIT 2 +#define I2C_32BIT 4 + +/* Terminating list entry for reg */ +#define I2C_REG_TERM 0xFFFF +/* Terminating list entry for val */ +#define I2C_VAL_TERM 0xFFFFFFFF +/* Terminating list entry for len */ +#define I2C_LEN_TERM 0xFFFF + +/* terminating token for reg list */ +#define IMX046_TOK_TERM 0xFF + +/* delay token for reg list */ +#define IMX046_TOK_DELAY 100 + +/* CSI2 Virtual ID */ +#define IMX046_CSI2_VIRTUAL_ID 0x0 + +#define IMX046_CLKRC 0x11 + +/* Used registers */ +#define IMX046_REG_MODEL_ID 0x0000 +#define IMX046_REG_REV_NUMBER 0x0002 +#define IMX046_REG_MFR_ID 0x0003 + +#define IMX046_REG_MODE_SELECT 0x0100 +#define IMX046_REG_IMAGE_ORIENTATION 0x0101 +#define IMX046_REG_SW_RESET 0x0103 +#define IMX046_REG_GROUPED_PAR_HOLD 0x0104 +#define IMX046_REG_CCP2_CHANNEL_ID 0x0110 + +#define IMX046_REG_FINE_INT_TIME 0x0200 +#define IMX046_REG_COARSE_INT_TIME 0x0202 + +#define IMX046_REG_ANALOG_GAIN_GLOBAL 0x0204 +#define IMX046_REG_ANALOG_GAIN_GREENR 0x0206 +#define IMX046_REG_ANALOG_GAIN_RED 0x0208 +#define IMX046_REG_ANALOG_GAIN_BLUE 0x020A +#define IMX046_REG_ANALOG_GAIN_GREENB 0x020C +#define IMX046_REG_DIGITAL_GAIN_GREENR 0x020E +#define IMX046_REG_DIGITAL_GAIN_RED 0x0210 +#define IMX046_REG_DIGITAL_GAIN_BLUE 0x0212 +#define IMX046_REG_DIGITAL_GAIN_GREENB 0x0214 + +#define IMX046_REG_VT_PIX_CLK_DIV 0x0300 +#define IMX046_REG_VT_SYS_CLK_DIV 0x0302 +#define IMX046_REG_PRE_PLL_CLK_DIV 0x0304 +#define IMX046_REG_PLL_MULTIPLIER 0x0306 +#define IMX046_REG_OP_PIX_CLK_DIV 0x0308 +#define IMX046_REG_OP_SYS_CLK_DIV 0x030A + +#define IMX046_REG_FRAME_LEN_LINES 0x0340 +#define IMX046_REG_LINE_LEN_PCK 0x0342 + +#define IMX046_REG_X_ADDR_START 0x0344 +#define IMX046_REG_Y_ADDR_START 0x0346 +#define IMX046_REG_X_ADDR_END 0x0348 +#define IMX046_REG_Y_ADDR_END 0x034A +#define IMX046_REG_X_OUTPUT_SIZE 0x034C +#define IMX046_REG_Y_OUTPUT_SIZE 0x034E +#define IMX046_REG_X_EVEN_INC 0x0380 +#define IMX046_REG_X_ODD_INC 0x0382 +#define IMX046_REG_Y_EVEN_INC 0x0384 +#define IMX046_REG_Y_ODD_INC 0x0386 + +#define IMX046_REG_HMODEADD 0x3001 +#define HMODEADD_SHIFT 7 +#define HMODEADD_MASK (0x1 << HMODEADD_SHIFT) +#define IMX046_REG_OPB_CTRL 0x300C +#define IMX046_REG_Y_OPBADDR_START_DI 0x3014 +#define IMX046_REG_Y_OPBADDR_END_DI 0x3015 +#define IMX046_REG_PGACUR_VMODEADD 0x3016 +#define VMODEADD_SHIFT 6 +#define VMODEADD_MASK (0x1 << VMODEADD_SHIFT) +#define IMX046_REG_CHCODE_OUTCHSINGLE 0x3017 +#define IMX046_REG_OUTIF 0x301C +#define IMX046_REG_RGPLTD_RGCLKEN 0x3022 +#define RGPLTD_MASK 0x3 +#define IMX046_REG_RGPOF_RGPOFV2 0x3031 +#define IMX046_REG_CPCKAUTOEN 0x3040 +#define IMX046_REG_RGCPVFB 0x3041 +#define IMX046_REG_RGAZPDRV 0x3051 +#define IMX046_REG_RGAZTEST 0x3053 +#define IMX046_REG_RGVSUNLV 0x3055 +#define IMX046_REG_CLPOWER 0x3060 +#define IMX046_REG_CLPOWERSP 0x3065 +#define IMX046_REG_ACLDIRV_TVADDCLP 0x30AA +#define IMX046_REG_TESTDI 0x30E5 +#define IMX046_REG_HADDAVE 0x30E8 +#define HADDAVE_SHIFT 7 +#define HADDAVE_MASK (0x1 << HADDAVE_SHIFT) + +#define IMX046_REG_RAW10CH2V2P_LO 0x31A4 +#define IMX046_REG_RAW10CH2V2D_LO 0x31A6 +#define IMX046_REG_COMP8CH1V2P_LO 0x31AC +#define IMX046_REG_COMP8CH1V2D_LO 0x31AE +#define IMX046_REG_RAW10CH1V2P_LO 0x31B4 +#define IMX046_REG_RAW10CH1V2D_LO 0x31B6 + +#define IMX046_REG_RGOUTSEL1 0x3300 +#define IMX046_REG_RGLANESEL 0x3301 +#define IMX046_REG_RGTLPX 0x3304 +#define IMX046_REG_RGTCLKPREPARE 0x3305 +#define IMX046_REG_RGTCLKZERO 0x3306 +#define IMX046_REG_RGTCLKPRE 0x3307 +#define IMX046_REG_RGTCLKPOST 0x3308 +#define IMX046_REG_RGTCLKTRAIL 0x3309 +#define IMX046_REG_RGTHSEXIT 0x330A +#define IMX046_REG_RGTHSPREPARE 0x330B +#define IMX046_REG_RGTHSZERO 0x330C +#define IMX046_REG_RGTHSTRAIL 0x330D + +#define IMX046_REG_TESBYPEN 0x30D8 +#define IMX046_REG_TEST_PATT_MODE 0x0600 +#define IMX046_REG_TEST_PATT_RED 0x0602 +#define IMX046_REG_TEST_PATT_GREENR 0x0604 +#define IMX046_REG_TEST_PATT_BLUE 0x0606 +#define IMX046_REG_TEST_PATT_GREENB 0x0608 + +/* + * The nominal xclk input frequency of the IMX046 is 18MHz, maximum + * frequency is 45MHz, and minimum frequency is 6MHz. + */ +#define IMX046_XCLK_MIN 6000000 +#define IMX046_XCLK_MAX 45000000 +#define IMX046_XCLK_NOM_1 18000000 +#define IMX046_XCLK_NOM_2 18000000 + +/* FPS Capabilities */ +#define IMX046_MIN_FPS 7 +#define IMX046_DEF_FPS 15 +#define IMX046_MAX_FPS 30 + +#define I2C_RETRY_COUNT 5 + +/* Still capture 8 MP */ +#define IMX046_IMAGE_WIDTH_MAX 3280 +#define IMX046_IMAGE_HEIGHT_MAX 2464 + +/* Analog gain values */ +#define IMX046_EV_MIN_GAIN 0 +#define IMX046_EV_MAX_GAIN 30 +#define IMX046_EV_DEF_GAIN 21 +#define IMX046_EV_GAIN_STEP 1 +/* maximum index in the gain EVT */ +#define IMX046_EV_TABLE_GAIN_MAX 30 + +/* Exposure time values */ +#define IMX046_MIN_EXPOSURE 250 +#define IMX046_MAX_EXPOSURE 128000 +#define IMX046_DEF_EXPOSURE 33000 +#define IMX046_EXPOSURE_STEP 50 + +/* Test Pattern Values */ +#define IMX046_MIN_TEST_PATT_MODE 0 +#define IMX046_MAX_TEST_PATT_MODE 4 +#define IMX046_MODE_TEST_PATT_STEP 1 + +#define IMX046_TEST_PATT_SOLID_COLOR 1 +#define IMX046_TEST_PATT_COLOR_BAR 2 +#define IMX046_TEST_PATT_PN9 4 + +#define IMX046_MAX_FRAME_LENGTH_LINES 0xFFFF + +#define SENSOR_DETECTED 1 +#define SENSOR_NOT_DETECTED 0 + +#define NUM_IMAGE_SIZES ARRAY_SIZE(imx046_sizes) +/** + * struct imx046_reg - imx046 register format + * @reg: 16-bit offset to register + * @val: 8/16/32-bit register value + * @length: length of the register + * + * Define a structure for IMX046 register initialization values + */ +struct imx046_reg { + u16 reg; + u32 val; + u16 length; +}; + +enum imx046_image_size { + QUART_MP, + HALF_MP, + TWO_MP, + EIGHT_MP +}; + +#define NUM_IMAGE_SIZES ARRAY_SIZE(imx046_sizes) +/** + * struct imx046_capture_size - image capture size information + * @width: image width in pixels + * @height: image height in pixels + */ +struct imx046_capture_size { + unsigned long width; + unsigned long height; +}; + +/** + * struct struct clk_settings - struct for storage of sensor + * clock settings + */ +struct imx046_clk_settings { + u16 pre_pll_div; + u16 pll_mult; + u16 post_pll_div; + u16 vt_pix_clk_div; + u16 vt_sys_clk_div; +}; + +/** + * struct struct mipi_settings - struct for storage of sensor + * mipi settings + */ +struct imx046_mipi_settings { + u16 data_lanes; + u16 ths_prepare; + u16 ths_zero; + u16 ths_settle_lower; + u16 ths_settle_upper; +}; + +/** + * struct struct frame_settings - struct for storage of sensor + * frame settings + */ +struct imx046_frame_settings { + u16 frame_len_lines_min; + u16 frame_len_lines; + u16 line_len_pck; + u16 x_addr_start; + u16 x_addr_end; + u16 y_addr_start; + u16 y_addr_end; + u16 x_output_size; + u16 y_output_size; + u16 x_even_inc; + u16 x_odd_inc; + u16 y_even_inc; + u16 y_odd_inc; + u16 v_mode_add; + u16 h_mode_add; + u16 h_add_ave; +}; + +/** + * struct struct imx046_sensor_settings - struct for storage of + * sensor settings. + */ +struct imx046_sensor_settings { + struct imx046_clk_settings clk; + struct imx046_mipi_settings mipi; + struct imx046_frame_settings frame; +}; + +/** + * struct struct imx046_clock_freq - struct for storage of sensor + * clock frequencies + */ +struct imx046_clock_freq { + u32 vco_clk; + u32 mipi_clk; + u32 ddr_clk; + u32 vt_pix_clk; +}; + +/** + * Array of image sizes supported by IMX046. These must be ordered from + * smallest image size to largest. + */ +const static struct imx046_capture_size imx046_sizes[] = { + { 410, 308 }, /* QUART_MP - 1/8 Vertical Elim */ + { 820, 616 }, /* 0.5Mp - 4X Horizontal & Vertical Elim. */ + { 3280, 616 }, /* 2Mp - 4X Vertical Elim. */ + { 3280, 2464}, /* 8MP - Full Resolution */ +}; + +/* PLL settings for imx046 */ +enum imx046_pll_type { + PLL_QUART_MP = 0, + PLL_0_5MP, + PLL_2MP, + PLL_8MP, +}; + +#endif /* ifndef IMX046_REGS_H */ diff --git a/drivers/media/video/isp/Kconfig b/drivers/media/video/isp/Kconfig new file mode 100644 index 00000000000..9566da00d7f --- /dev/null +++ b/drivers/media/video/isp/Kconfig @@ -0,0 +1,18 @@ +# Kconfig for OMAP3 ISP driver + +config VIDEO_OMAP3_ISP + bool + select VIDEOBUF_GEN + select VIDEOBUF_DMA_SG + select OMAP_IOMMU + +config VIDEO_OMAP34XX_ISP_PREVIEWER + tristate "OMAP ISP Previewer" + depends on !ARCH_OMAP3410 + select VIDEO_OMAP3_ISP + + +config VIDEO_OMAP34XX_ISP_RESIZER + tristate "OMAP ISP Resizer" + depends on !ARCH_OMAP3410 + select VIDEO_OMAP3_ISP diff --git a/drivers/media/video/isp/Makefile b/drivers/media/video/isp/Makefile new file mode 100644 index 00000000000..629aab5064e --- /dev/null +++ b/drivers/media/video/isp/Makefile @@ -0,0 +1,15 @@ +# Makefile for OMAP3 ISP driver + +isp-mod-objs += \ + isp.o ispccdc.o \ + isppreview.o ispresizer.o isph3a.o isphist.o isp_af.o ispcsi2.o \ + ispstat.o + +obj-$(CONFIG_VIDEO_OMAP3_ISP) += isp-mod.o + +obj-$(CONFIG_VIDEO_OMAP34XX_ISP_PREVIEWER) += \ + omap_previewer.o + +obj-$(CONFIG_VIDEO_OMAP34XX_ISP_RESIZER) += \ + omap_resizer.o + diff --git a/drivers/media/video/isp/bluegamma_table.h b/drivers/media/video/isp/bluegamma_table.h new file mode 100644 index 00000000000..301382a222a --- /dev/null +++ b/drivers/media/video/isp/bluegamma_table.h @@ -0,0 +1,1040 @@ +/* + * bluegamma_table.h + * + * Gamma Table values for BLUE for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 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. + */ + +0, +0, +1, +2, +3, +3, +4, +5, +6, +8, +10, +12, +14, +16, +18, +20, +22, +23, +25, +26, +28, +29, +31, +32, +34, +35, +36, +37, +39, +40, +41, +42, +43, +44, +45, +46, +47, +48, +49, +50, +51, +52, +52, +53, +54, +55, +56, +57, +58, +59, +60, +61, +62, +63, +63, +64, +65, +66, +66, +67, +68, +69, +69, +70, +71, +72, +72, +73, +74, +75, +75, +76, +77, +78, +78, +79, +80, +81, +81, +82, +83, +84, +84, +85, +86, +87, +88, +88, +89, +90, +91, +91, +92, +93, +94, +94, +95, +96, +97, +97, +98, +98, +99, +99, +100, +100, +101, +101, +102, +103, +104, +104, +105, +106, +107, +108, +108, +109, +110, +111, +111, +112, +113, +114, +114, +115, +116, +117, +117, +118, +119, +119, +120, +120, +121, +121, +122, +122, +123, +123, +124, +124, +125, +125, +126, +126, +127, +127, +128, +128, +129, +129, +130, +130, +131, +131, +132, +132, +133, +133, +134, +134, +135, +135, +136, +136, +137, +137, +138, +138, +139, +139, +140, +140, +141, +141, +142, +142, +143, +143, +144, +144, +145, +145, +146, +146, +147, +147, +148, +148, +149, +149, +150, +150, +151, +151, +152, +152, +153, +153, +153, +153, +154, +154, +154, +154, +155, +155, +156, +156, +157, +157, +158, +158, +158, +159, +159, +159, +160, +160, +160, +161, +161, +162, +162, +163, +163, +164, +164, +164, +164, +165, +165, +165, +165, +166, +166, +167, +167, +168, +168, +169, +169, +170, +170, +170, +170, +171, +171, +171, +171, +172, +172, +173, +173, +174, +174, +175, +175, +176, +176, +176, +176, +177, +177, +177, +177, +178, +178, +178, +178, +179, +179, +179, +179, +180, +180, +180, +180, +181, +181, +181, +181, +182, +182, +182, +182, +183, +183, +183, +183, +184, +184, +184, +184, +185, +185, +185, +185, +186, +186, +186, +186, +187, +187, +187, +187, +188, +188, +188, +188, +189, +189, +189, +189, +190, +190, +190, +190, +191, +191, +191, +191, +192, +192, +192, +192, +193, +193, +193, +193, +194, +194, +194, +194, +195, +195, +195, +195, +196, +196, +196, +196, +197, +197, +197, +197, +198, +198, +198, +198, +199, +199, +199, +199, +200, +200, +200, +200, +201, +201, +201, +201, +202, +202, +202, +203, +203, +203, +203, +204, +204, +204, +204, +205, +205, +205, +205, +206, +206, +206, +206, +207, +207, +207, +207, +208, +208, +208, +208, +209, +209, +209, +209, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +211, +211, +211, +211, +211, +211, +211, +211, +211, +211, +211, +212, +212, +212, +212, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +214, +214, +214, +214, +215, +215, +215, +215, +215, +215, +215, +215, +215, +215, +215, +216, +216, +216, +216, +217, +217, +217, +217, +218, +218, +218, +218, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +220, +220, +220, +220, +221, +221, +221, +221, +221, +221, +221, +221, +221, +221, +221, +222, +222, +222, +222, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +224, +224, +224, +224, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +226, +226, +226, +226, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +228, +228, +228, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +230, +230, +230, +230, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +233, +233, +233, +233, +234, +234, +234, +234, +234, +234, +234, +234, +234, +234, +234, +235, +235, +235, +235, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +237, +237, +237, +237, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +239, +239, +239, +239, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +241, +241, +241, +241, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +243, +243, +243, +243, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +245, +245, +245, +245, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +247, +247, +247, +247, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +249, +249, +249, +249, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +251, +251, +251, +251, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +254, +254, +254, +254, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255 diff --git a/drivers/media/video/isp/cfa_coef_table.h b/drivers/media/video/isp/cfa_coef_table.h new file mode 100644 index 00000000000..722a123ca73 --- /dev/null +++ b/drivers/media/video/isp/cfa_coef_table.h @@ -0,0 +1,602 @@ +/* + * cfa_coef_table.h + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Sakari Ailus + * Tuukka Toivonen + * + * Written by Gjorgji Rosikopulos + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +0, +247, +0, +244, +247, +36, +27, +12, +0, +27, +0, +250, +244, +12, +250, +4, +0, +0, +0, +248, +0, +0, +40, +0, +4, +250, +12, +244, +250, +0, +27, +0, +12, +27, +36, +247, +244, +0, +247, +0, +0, +40, +0, +0, +248, +0, +0, +0, +0, +247, +0, +244, +247, +36, +27, +12, +0, +27, +0, +250, +244, +12, +250, +4, +0, +0, +0, +248, +0, +0, +40, +0, +4, +250, +12, +244, +250, +0, +27, +0, +12, +27, +36, +247, +244, +0, +247, +0, +0, +40, +0, +0, +248, +0, +0, +0, +0, +247, +0, +244, +247, +36, +27, +12, +0, +27, +0, +250, +244, +12, +250, +4, +0, +0, +0, +248, +0, +0, +40, +0, +4, +250, +12, +244, +250, +0, +27, +0, +12, +27, +36, +247, +244, +0, +247, +0, +0, +40, +0, +0, +248, +0, +0, +0, +244, +0, +247, +0, +12, +27, +36, +247, +250, +0, +27, +0, +4, +250, +12, +244, +248, +0, +0, +0, +0, +40, +0, +0, +244, +12, +250, +4, +0, +27, +0, +250, +247, +36, +27, +12, +0, +247, +0, +244, +0, +0, +40, +0, +0, +0, +0, +248, +244, +0, +247, +0, +12, +27, +36, +247, +250, +0, +27, +0, +4, +250, +12, +244, +248, +0, +0, +0, +0, +40, +0, +0, +244, +12, +250, +4, +0, +27, +0, +250, +247, +36, +27, +12, +0, +247, +0, +244, +0, +0, +40, +0, +0, +0, +0, +248, +244, +0, +247, +0, +12, +27, +36, +247, +250, +0, +27, +0, +4, +250, +12, +244, +248, +0, +0, +0, +0, +40, +0, +0, +244, +12, +250, +4, +0, +27, +0, +250, +247, +36, +27, +12, +0, +247, +0, +244, +0, +0, +40, +0, +0, +0, +0, +248, +244, +12, +250, +4, +0, +27, +0, +250, +247, +36, +27, +12, +0, +247, +0, +244, +248, +0, +0, +0, +0, +40, +0, +0, +244, +0, +247, +0, +12, +27, +36, +247, +250, +0, +27, +0, +4, +250, +12, +244, +0, +0, +40, +0, +0, +0, +0, +248, +244, +12, +250, +4, +0, +27, +0, +250, +247, +36, +27, +12, +0, +247, +0, +244, +248, +0, +0, +0, +0, +40, +0, +0, +244, +0, +247, +0, +12, +27, +36, +247, +250, +0, +27, +0, +4, +250, +12, +244, +0, +0, +40, +0, +0, +0, +0, +248, +244, +12, +250, +4, +0, +27, +0, +250, +247, +36, +27, +12, +0, +247, +0, +244, +248, +0, +0, +0, +0, +40, +0, +0, +244, +0, +247, +0, +12, +27, +36, +247, +250, +0, +27, +0, +4, +250, +12, +244, +0, +0, +40, +0, +0, +0, +0, +248, +4, +250, +12, +244, +250, +0, +27, +0, +12, +27, +36, +247, +244, +0, +247, +0, +0, +0, +0, +248, +0, +0, +40, +0, +0, +247, +0, +244, +247, +36, +27, +12, +0, +27, +0, +250, +244, +12, +250, +4, +0, +40, +0, +0, +248, +0, +0, +0, +4, +250, +12, +244, +250, +0, +27, +0, +12, +27, +36, +247, +244, +0, +247, +0, +0, +0, +0, +248, +0, +0, +40, +0, +0, +247, +0, +244, +247, +36, +27, +12, +0, +27, +0, +250, +244, +12, +250, +4, +0, +40, +0, +0, +248, +0, +0, +0, +4, +250, +12, +244, +250, +0, +27, +0, +12, +27, +36, +247, +244, +0, +247, +0, +0, +0, +0, +248, +0, +0, +40, +0, +0, +247, +0, +244, +247, +36, +27, +12, +0, +27, +0, +250, +244, +12, +250, +4, +0, +40, +0, +0, +248, +0, +0, +0 diff --git a/drivers/media/video/isp/greengamma_table.h b/drivers/media/video/isp/greengamma_table.h new file mode 100644 index 00000000000..0f5c5e44490 --- /dev/null +++ b/drivers/media/video/isp/greengamma_table.h @@ -0,0 +1,1040 @@ +/* + * greengamma_table.h + * + * Gamma Table values for GREEN for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 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. + */ + +0, +0, +1, +2, +3, +3, +4, +5, +6, +8, +10, +12, +14, +16, +18, +20, +22, +23, +25, +26, +28, +29, +31, +32, +34, +35, +36, +37, +39, +40, +41, +42, +43, +44, +45, +46, +47, +48, +49, +50, +51, +52, +52, +53, +54, +55, +56, +57, +58, +59, +60, +61, +62, +63, +63, +64, +65, +66, +66, +67, +68, +69, +69, +70, +71, +72, +72, +73, +74, +75, +75, +76, +77, +78, +78, +79, +80, +81, +81, +82, +83, +84, +84, +85, +86, +87, +88, +88, +89, +90, +91, +91, +92, +93, +94, +94, +95, +96, +97, +97, +98, +98, +99, +99, +100, +100, +101, +101, +102, +103, +104, +104, +105, +106, +107, +108, +108, +109, +110, +111, +111, +112, +113, +114, +114, +115, +116, +117, +117, +118, +119, +119, +120, +120, +121, +121, +122, +122, +123, +123, +124, +124, +125, +125, +126, +126, +127, +127, +128, +128, +129, +129, +130, +130, +131, +131, +132, +132, +133, +133, +134, +134, +135, +135, +136, +136, +137, +137, +138, +138, +139, +139, +140, +140, +141, +141, +142, +142, +143, +143, +144, +144, +145, +145, +146, +146, +147, +147, +148, +148, +149, +149, +150, +150, +151, +151, +152, +152, +153, +153, +153, +153, +154, +154, +154, +154, +155, +155, +156, +156, +157, +157, +158, +158, +158, +159, +159, +159, +160, +160, +160, +161, +161, +162, +162, +163, +163, +164, +164, +164, +164, +165, +165, +165, +165, +166, +166, +167, +167, +168, +168, +169, +169, +170, +170, +170, +170, +171, +171, +171, +171, +172, +172, +173, +173, +174, +174, +175, +175, +176, +176, +176, +176, +177, +177, +177, +177, +178, +178, +178, +178, +179, +179, +179, +179, +180, +180, +180, +180, +181, +181, +181, +181, +182, +182, +182, +182, +183, +183, +183, +183, +184, +184, +184, +184, +185, +185, +185, +185, +186, +186, +186, +186, +187, +187, +187, +187, +188, +188, +188, +188, +189, +189, +189, +189, +190, +190, +190, +190, +191, +191, +191, +191, +192, +192, +192, +192, +193, +193, +193, +193, +194, +194, +194, +194, +195, +195, +195, +195, +196, +196, +196, +196, +197, +197, +197, +197, +198, +198, +198, +198, +199, +199, +199, +199, +200, +200, +200, +200, +201, +201, +201, +201, +202, +202, +202, +203, +203, +203, +203, +204, +204, +204, +204, +205, +205, +205, +205, +206, +206, +206, +206, +207, +207, +207, +207, +208, +208, +208, +208, +209, +209, +209, +209, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +211, +211, +211, +211, +211, +211, +211, +211, +211, +211, +211, +212, +212, +212, +212, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +214, +214, +214, +214, +215, +215, +215, +215, +215, +215, +215, +215, +215, +215, +215, +216, +216, +216, +216, +217, +217, +217, +217, +218, +218, +218, +218, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +220, +220, +220, +220, +221, +221, +221, +221, +221, +221, +221, +221, +221, +221, +221, +222, +222, +222, +222, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +224, +224, +224, +224, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +226, +226, +226, +226, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +228, +228, +228, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +230, +230, +230, +230, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +233, +233, +233, +233, +234, +234, +234, +234, +234, +234, +234, +234, +234, +234, +234, +235, +235, +235, +235, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +237, +237, +237, +237, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +239, +239, +239, +239, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +241, +241, +241, +241, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +243, +243, +243, +243, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +245, +245, +245, +245, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +247, +247, +247, +247, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +249, +249, +249, +249, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +251, +251, +251, +251, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +254, +254, +254, +254, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255 diff --git a/drivers/media/video/isp/isp.c b/drivers/media/video/isp/isp.c new file mode 100644 index 00000000000..6714bd09863 --- /dev/null +++ b/drivers/media/video/isp/isp.c @@ -0,0 +1,2823 @@ +/* + * isp.c + * + * Driver Library for ISP Control module in TI's OMAP3 Camera ISP + * ISP interface and IRQ related APIs are defined here. + * + * Copyright (C) 2009 Texas Instruments. + * Copyright (C) 2009 Nokia. + * + * Contributors: + * Sameer Venkatraman + * Mohit Jalori + * Sergio Aguirre + * Sakari Ailus + * Tuukka Toivonen + * Toni Leinonen + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispccdc.h" +#include "isph3a.h" +#include "isphist.h" +#include "isp_af.h" +#include "isppreview.h" +#include "ispresizer.h" +#include "ispcsi2.h" + +static struct platform_device *omap3isp_pdev; + +static void isp_save_ctx(struct device *dev); + +static void isp_restore_ctx(struct device *dev); + +static void isp_buf_init(struct device *dev); + +/* List of image formats supported via OMAP ISP */ +const static struct v4l2_fmtdesc isp_formats[] = { + { + .description = "UYVY, packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "Bayer10 (GrR/BGb)", + .pixelformat = V4L2_PIX_FMT_SGRBG10, + }, +}; + +/** + * struct vcontrol - Video control structure. + * @qc: V4L2 Query control structure. + * @current_value: Current value of the control. + */ +static struct vcontrol { + struct v4l2_queryctrl qc; + int current_value; +} video_control[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = ISPPRV_BRIGHT_LOW, + .maximum = ISPPRV_BRIGHT_HIGH, + .step = ISPPRV_BRIGHT_STEP, + .default_value = ISPPRV_BRIGHT_DEF, + }, + .current_value = ISPPRV_BRIGHT_DEF, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = ISPPRV_CONTRAST_LOW, + .maximum = ISPPRV_CONTRAST_HIGH, + .step = ISPPRV_CONTRAST_STEP, + .default_value = ISPPRV_CONTRAST_DEF, + }, + .current_value = ISPPRV_CONTRAST_DEF, + }, + { + { + .id = V4L2_CID_COLORFX, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Color Effects", + .minimum = V4L2_COLORFX_NONE, + .maximum = V4L2_COLORFX_SEPIA, + .step = 1, + .default_value = V4L2_COLORFX_NONE, + }, + .current_value = V4L2_COLORFX_NONE, + } +}; + +static struct v4l2_querymenu video_menu[] = { + { + .id = V4L2_CID_COLORFX, + .index = 0, + .name = "None", + }, + { + .id = V4L2_CID_COLORFX, + .index = 1, + .name = "B&W", + }, + { + .id = V4L2_CID_COLORFX, + .index = 2, + .name = "Sepia", + }, +}; + +/* Structure for saving/restoring ISP module registers */ +static struct isp_reg isp_reg_list[] = { + {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_IRQ1ENABLE, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_GRESET_LENGTH, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_REPLAY, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_FRAME, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_DELAY, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_STRB_DELAY, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_SHUT_DELAY, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_LENGTH, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_STRB_LENGTH, 0}, + {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_SHUT_LENGTH, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF_SYSCONFIG, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF_IRQENABLE, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_CTRL, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_CTRL, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_START, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_START, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_END, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_END, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_WINDOWSIZE, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_WINDOWSIZE, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_THRESHOLD, 0}, + {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_THRESHOLD, 0}, + {0, ISP_TOK_TERM, 0} +}; + +/** + * isp_flush - Post pending L3 bus writes by doing a register readback + * @dev: Device pointer specific to the OMAP3 ISP. + * + * In order to force posting of pending writes, we need to write and + * readback the same register, in this case the revision register. + * + * See this link for reference: + * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html + **/ +void isp_flush(struct device *dev) +{ + isp_reg_writel(dev, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); +} + +/** + * isp_reg_readl - Read value of an OMAP3 ISP register + * @dev: Device pointer specific to the OMAP3 ISP. + * @isp_mmio_range: Range to which the register offset refers to. + * @reg_offset: Register offset to read from. + * + * Returns an unsigned 32 bit value with the required register contents. + **/ +u32 isp_reg_readl(struct device *dev, enum isp_mem_resources isp_mmio_range, + u32 reg_offset) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset); +} +EXPORT_SYMBOL(isp_reg_readl); + +/** + * isp_reg_writel - Write value to an OMAP3 ISP register + * @dev: Device pointer specific to the OMAP3 ISP. + * @reg_value: 32 bit value to write to the register. + * @isp_mmio_range: Range to which the register offset refers to. + * @reg_offset: Register offset to write into. + **/ +void isp_reg_writel(struct device *dev, u32 reg_value, + enum isp_mem_resources isp_mmio_range, u32 reg_offset) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset); +} +EXPORT_SYMBOL(isp_reg_writel); + +/** + * isp_reg_and - Do AND binary operation within an OMAP3 ISP register value + * @dev: Device pointer specific to the OMAP3 ISP. + * @mmio_range: Range to which the register offset refers to. + * @reg: Register offset to work on. + * @and_bits: 32 bit value which would be 'ANDed' with current register value. + **/ +void isp_reg_and(struct device *dev, enum isp_mem_resources mmio_range, u32 reg, + u32 and_bits) +{ + u32 v = isp_reg_readl(dev, mmio_range, reg); + + isp_reg_writel(dev, v & and_bits, mmio_range, reg); +} +EXPORT_SYMBOL(isp_reg_and); + +/** + * isp_reg_or - Do OR binary operation within an OMAP3 ISP register value + * @dev: Device pointer specific to the OMAP3 ISP. + * @mmio_range: Range to which the register offset refers to. + * @reg: Register offset to work on. + * @or_bits: 32 bit value which would be 'ORed' with current register value. + **/ +void isp_reg_or(struct device *dev, enum isp_mem_resources mmio_range, u32 reg, + u32 or_bits) +{ + u32 v = isp_reg_readl(dev, mmio_range, reg); + + isp_reg_writel(dev, v | or_bits, mmio_range, reg); +} +EXPORT_SYMBOL(isp_reg_or); + +/** + * isp_reg_and_or - Do AND and OR binary ops within an OMAP3 ISP register value + * @dev: Device pointer specific to the OMAP3 ISP. + * @mmio_range: Range to which the register offset refers to. + * @reg: Register offset to work on. + * @and_bits: 32 bit value which would be 'ANDed' with current register value. + * @or_bits: 32 bit value which would be 'ORed' with current register value. + * + * The AND operation is done first, and then the OR operation. Mostly useful + * when clearing a group of bits before setting a value. + **/ +void isp_reg_and_or(struct device *dev, enum isp_mem_resources mmio_range, + u32 reg, u32 and_bits, u32 or_bits) +{ + u32 v = isp_reg_readl(dev, mmio_range, reg); + + isp_reg_writel(dev, (v & and_bits) | or_bits, mmio_range, reg); +} +EXPORT_SYMBOL(isp_reg_and_or); + +/** + * find_vctrl - Return the index of the ctrl array of the requested ctrl ID. + * @id: Requested control ID. + * + * Returns 0 if successful, -EINVAL if not found, or -EDOM if its out of + * domain. + **/ +static int find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--) + if (video_control[i].qc.id == id) + break; + + if (i < 0) + i = -EINVAL; + + return i; +} + +/** + * find_next_vctrl - Return next v4l2 ctrl ID available after the specified ID + * @id: Reference V4L2 control ID. + * + * Returns 0 if successful, or -EINVAL if not found. + **/ +static int find_next_vctrl(int id) +{ + int i; + u32 best = (u32)-1; + + for (i = 0; i < ARRAY_SIZE(video_control); i++) { + if (video_control[i].qc.id > id && + (best == (u32)-1 || + video_control[i].qc.id < + video_control[best].qc.id)) { + best = i; + } + } + + if (best == (u32)-1) + return -EINVAL; + + return best; +} + +/** + * find_vmenu - Return index of the menu array of the requested ctrl option. + * @id: Requested control ID. + * @index: Requested menu option index. + * + * Returns 0 if successful, -EINVAL if not found, or -EDOM if its out of + * domain. + **/ +static int find_vmenu(int id, int index) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(video_menu) - 1); i >= 0; i--) { + if (video_menu[i].id != id || video_menu[i].index != index) + continue; + return i; + } + + return -EINVAL; +} + +/** + * isp_release_resources - Free all currently requested ISP submodules. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +static void isp_release_resources(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (isp->pipeline.modules & OMAP_ISP_CCDC) + ispccdc_free(&isp->isp_ccdc); + + if (isp->pipeline.modules & OMAP_ISP_PREVIEW) + isppreview_free(&isp->isp_prev); + + if (isp->pipeline.modules & OMAP_ISP_RESIZER) + ispresizer_free(&isp->isp_res); + return; +} + +/** + * isp_wait - Wait for idle or busy state transition with a time limit + * @dev: Device pointer specific to the OMAP3 ISP. + * @busy: Function pointer which determines if submodule is busy. + * @wait_for_busy: If 0, waits for idle state, if 1, waits for busy state. + * @max_wait: Max retry count in us for wait for idle/busy transition. + * @priv: Function parameter to send to busy check function. + **/ +static int isp_wait(struct device *dev, int (*busy)(void *), int wait_for_busy, + int max_wait, void *priv) +{ + int wait = 0; + + if (max_wait == 0) + max_wait = 10000; /* 10 ms */ + + while ((wait_for_busy && !busy(priv)) + || (!wait_for_busy && busy(priv))) { + rmb(); + udelay(1); + wait++; + if (wait > max_wait) { + dev_alert(dev, "%s: wait is too much\n", __func__); + return -EBUSY; + } + } + DPRINTK_ISPCTRL(KERN_ALERT "%s: wait %d\n", __func__, wait); + + return 0; +} + +/** + * ispccdc_sbl_wait_idle - Wrapper to wait for ccdc sbl status bits to be idle. + * @isp_ccdc: Pointer to ISP CCDC device. + * @max_wait: Max retry count for wait for idle transition of the CCDC SBL bits + **/ +static int ispccdc_sbl_wait_idle(struct isp_ccdc_device *isp_ccdc, int max_wait) +{ + return isp_wait(isp_ccdc->dev, ispccdc_sbl_busy, 0, max_wait, isp_ccdc); +} + +/** + * isp_enable_interrupts - Enable ISP interrupts. + * @dev: Device pointer specific to the OMAP3 ISP. + * @is_raw: Determines if current pipeline ends in CCDC (!0) or Resizer (0) + **/ +static void isp_enable_interrupts(struct device *dev, int is_raw) +{ + struct isp_device *isp = dev_get_drvdata(dev); + u32 irq0enable; + + irq0enable = IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ + | IRQ0ENABLE_HS_VS_IRQ + | IRQ0ENABLE_CCDC_VD0_IRQ | IRQ0ENABLE_CCDC_VD1_IRQ + | IRQ0ENABLE_CSIA_IRQ + | IRQ0ENABLE_CSIB_IRQ + | IRQ0ENABLE_H3A_AWB_DONE_IRQ | IRQ0ENABLE_H3A_AF_DONE_IRQ + | isp->interrupts; + + if (!is_raw) + irq0enable |= IRQ0ENABLE_PRV_DONE_IRQ | IRQ0ENABLE_RSZ_DONE_IRQ; + + isp_reg_writel(dev, -1, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + isp_reg_writel(dev, irq0enable, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); + + return; +} + +/** + * isp_disable_interrupts - Disable all ISP interrupts. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +static void isp_disable_interrupts(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (isp->bt656ifen == 0) + isp_reg_writel(dev, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); + else + isp_reg_writel(dev, 0 | IRQ0ENABLE_RSZ_DONE_IRQ, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); +} + +/** + * isp_set_callback - Set an external callback for an ISP interrupt. + * @dev: Device pointer specific to the OMAP3 ISP. + * @type: Type of the event for which callback is requested. + * @callback: Method to be called as callback in the ISR context. + * @arg1: First argument to be passed when callback is called in ISR. + * @arg2: Second argument to be passed when callback is called in ISR. + * + * This function sets a callback function for a done event in the ISP + * module, and enables the corresponding interrupt. + **/ +int isp_set_callback(struct device *dev, enum isp_callback_type type, + isp_callback_t callback, isp_vbq_callback_ptr arg1, + void *arg2) +{ + struct isp_device *isp = dev_get_drvdata(dev); + unsigned long irqflags = 0; + + if (callback == NULL) { + DPRINTK_ISPCTRL("ISP_ERR : Null Callback\n"); + return -EINVAL; + } + + spin_lock_irqsave(&isp->lock, irqflags); + isp->irq.isp_callbk[type] = callback; + isp->irq.isp_callbk_arg1[type] = arg1; + isp->irq.isp_callbk_arg2[type] = arg2; + spin_unlock_irqrestore(&isp->lock, irqflags); + + switch (type) { + case CBK_HIST_DONE: + isp->interrupts |= IRQ0ENABLE_HIST_DONE_IRQ; + if (isp->running != ISP_RUNNING) + break; + isp_reg_writel(dev, IRQ0ENABLE_HIST_DONE_IRQ, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + IRQ0ENABLE_HIST_DONE_IRQ); + break; + case CBK_PREV_DONE: + isp_reg_writel(dev, IRQ0ENABLE_PRV_DONE_IRQ, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + IRQ0ENABLE_PRV_DONE_IRQ); + break; + case CBK_RESZ_DONE: + isp_reg_writel(dev, IRQ0ENABLE_RSZ_DONE_IRQ, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + IRQ0ENABLE_RSZ_DONE_IRQ); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL(isp_set_callback); + +/** + * isp_unset_callback - Clears the callback for the ISP module done events. + * @dev: Device pointer specific to the OMAP3 ISP. + * @type: Type of the event for which callback to be cleared. + * + * This function clears a callback function for a done event in the ISP + * module, and disables the corresponding interrupt. + **/ +int isp_unset_callback(struct device *dev, enum isp_callback_type type) +{ + struct isp_device *isp = dev_get_drvdata(dev); + unsigned long irqflags = 0; + + spin_lock_irqsave(&isp->lock, irqflags); + isp->irq.isp_callbk[type] = NULL; + isp->irq.isp_callbk_arg1[type] = NULL; + isp->irq.isp_callbk_arg2[type] = NULL; + spin_unlock_irqrestore(&isp->lock, irqflags); + + switch (type) { + case CBK_HIST_DONE: + isp->interrupts &= ~IRQ0ENABLE_HIST_DONE_IRQ; + if (isp->running != ISP_RUNNING) + break; + isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + ~IRQ0ENABLE_HIST_DONE_IRQ); + break; + case CBK_PREV_DONE: + isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + ~IRQ0ENABLE_PRV_DONE_IRQ); + break; + case CBK_RESZ_DONE: + isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + ~IRQ0ENABLE_RSZ_DONE_IRQ); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL(isp_unset_callback); + +/** + * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. + * @dev: Device pointer specific to the OMAP3 ISP. + * @xclk: Desired frequency of the clock in Hz. + * @xclksel: XCLK to configure (0 = A, 1 = B). + * + * Configures the specified MCLK divisor in the ISP timing control register + * (TCTRL_CTRL) to generate the desired xclk clock value. + * + * Divisor = CM_CAM_MCLK_HZ / xclk + * + * Returns the final frequency that is actually being generated + **/ +u32 isp_set_xclk(struct device *dev, u32 xclk, u8 xclksel) +{ + u32 divisor; + u32 currentxclk; + + if (xclk >= CM_CAM_MCLK_HZ) { + divisor = ISPTCTRL_CTRL_DIV_BYPASS; + currentxclk = CM_CAM_MCLK_HZ; + } else if (xclk >= 2) { + divisor = CM_CAM_MCLK_HZ / xclk; + if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) + divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; + currentxclk = CM_CAM_MCLK_HZ / divisor; + } else { + divisor = xclk; + currentxclk = 0; + } + + switch (xclksel) { + case 0: + isp_reg_and_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, + ~ISPTCTRL_CTRL_DIVA_MASK, + divisor << ISPTCTRL_CTRL_DIVA_SHIFT); + DPRINTK_ISPCTRL("isp_set_xclk(): cam_xclka set to %d Hz\n", + currentxclk); + break; + case 1: + isp_reg_and_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, + ~ISPTCTRL_CTRL_DIVB_MASK, + divisor << ISPTCTRL_CTRL_DIVB_SHIFT); + DPRINTK_ISPCTRL("isp_set_xclk(): cam_xclkb set to %d Hz\n", + currentxclk); + break; + default: + DPRINTK_ISPCTRL("ISP_ERR: isp_set_xclk(): Invalid requested " + "xclk. Must be 0 (A) or 1 (B).\n"); + return -EINVAL; + } + + return currentxclk; +} +EXPORT_SYMBOL(isp_set_xclk); + +/** + * isp_power_settings - Sysconfig settings, for Power Management. + * @dev: Device pointer specific to the OMAP3 ISP. + * @idle: Consider idle state. + * + * Sets the power settings for the ISP, and SBL bus. + **/ +static void isp_power_settings(struct device *dev, int idle) +{ + if (idle) { + isp_reg_writel(dev, ISP_SYSCONFIG_AUTOIDLE | + (ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY << + ISP_SYSCONFIG_MIDLEMODE_SHIFT), + OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); + if (omap_rev() == OMAP3430_REV_ES1_0) { + isp_reg_writel(dev, ISPCSI1_AUTOIDLE | + (ISPCSI1_MIDLEMODE_SMARTSTANDBY << + ISPCSI1_MIDLEMODE_SHIFT), + OMAP3_ISP_IOMEM_CSI2A, + ISP_CSIA_SYSCONFIG); + isp_reg_writel(dev, ISPCSI1_AUTOIDLE | + (ISPCSI1_MIDLEMODE_SMARTSTANDBY << + ISPCSI1_MIDLEMODE_SHIFT), + OMAP3_ISP_IOMEM_CCP2, + ISP_CSIB_SYSCONFIG); + } + isp_reg_writel(dev, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL); + + } else { + isp_reg_writel(dev, ISP_SYSCONFIG_AUTOIDLE | + (ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY << + ISP_SYSCONFIG_MIDLEMODE_SHIFT), + OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); + if (omap_rev() == OMAP3430_REV_ES1_0) { + isp_reg_writel(dev, ISPCSI1_AUTOIDLE | + (ISPCSI1_MIDLEMODE_FORCESTANDBY << + ISPCSI1_MIDLEMODE_SHIFT), + OMAP3_ISP_IOMEM_CSI2A, + ISP_CSIA_SYSCONFIG); + + isp_reg_writel(dev, ISPCSI1_AUTOIDLE | + (ISPCSI1_MIDLEMODE_FORCESTANDBY << + ISPCSI1_MIDLEMODE_SHIFT), + OMAP3_ISP_IOMEM_CCP2, + ISP_CSIB_SYSCONFIG); + } + + isp_reg_writel(dev, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL); + } +} + +#define BIT_SET(var, shift, mask, val) \ + do { \ + var = (var & ~(mask << shift)) \ + | (val << shift); \ + } while (0) + +/** + * isp_csi_enable - Enable CSI1/CCP2 interface. + * @dev: Device pointer specific to the OMAP3 ISP. + * @enable: Enable flag. + **/ +static void isp_csi_enable(struct device *dev, u8 enable) +{ + isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL, + ~(BIT(0) | BIT(4)), + enable ? (BIT(0) | BIT(4)) : 0); +} + +/** + * isp_init_csi - Initialize CSI1/CCP2 interface. + * @dev: Device pointer specific to the OMAP3 ISP. + * @config: Pointer to ISP interface config structure. + * + * This will analize the parameters passed by the interface config + * structure, and configure the respective registers for proper CSI1/CCP2 + * config. + * + * Returns -EINVAL if wrong format, -EIO if strobe is choosen in CSI1 mode, or + * 0 on success. + **/ +static int isp_init_csi(struct device *dev, struct isp_interface_config *config) +{ + u32 i = 0, val, reg; + int format; + + switch (config->u.csi.format) { + case V4L2_PIX_FMT_SGRBG10: + format = 0x16; /* RAW10+VP */ + break; + case V4L2_PIX_FMT_SGRBG10DPCM8: + format = 0x12; /* RAW8+DPCM10+VP */ + break; + default: + dev_err(dev, "isp_init_csi: bad csi format\n"); + return -EINVAL; + } + + /* Reset the CSI and wait for reset to complete */ + isp_reg_writel(dev, isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, + ISPCSI1_SYSCONFIG) | BIT(1), + OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSCONFIG); + while (!(isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSSTATUS) & + BIT(0))) { + udelay(10); + if (i++ > 10) + break; + } + if (!(isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSSTATUS) & + BIT(0))) { + dev_warn(dev, + "omap3_isp: timeout waiting for csi reset\n"); + } + + /* ISPCSI1_CTRL */ + val = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL); + val &= ~BIT(11); /* Enable VP only off -> + extract embedded data to interconnect */ + BIT_SET(val, 8, 0x3, config->u.csi.vpclk); /* Video port clock */ +/* val |= BIT(3); */ /* Wait for FEC before disabling interface */ + val |= BIT(2); /* I/O cell output is parallel + (no effect, but errata says should be enabled + for class 1/2) */ + val |= BIT(12); /* VP clock polarity to falling edge + (needed or bad picture!) */ + + /* Data/strobe physical layer */ + BIT_SET(val, 1, 1, config->u.csi.signalling); + BIT_SET(val, 10, 1, config->u.csi.strobe_clock_inv); + val |= BIT(4); /* Magic bit to enable CSI1 and strobe mode */ + isp_reg_writel(dev, val, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL); + + /* ISPCSI1_LCx_CTRL logical channel #0 */ + reg = ISPCSI1_LCx_CTRL(0); /* reg = ISPCSI1_CTRL1; */ + val = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, reg); + /* Format = RAW10+VP or RAW8+DPCM10+VP*/ + BIT_SET(val, 3, 0x1f, format); + /* Enable setting of frame regions of interest */ + BIT_SET(val, 1, 1, 1); + BIT_SET(val, 2, 1, config->u.csi.crc); + isp_reg_writel(dev, val, OMAP3_ISP_IOMEM_CCP2, reg); + + /* ISPCSI1_DAT_START for logical channel #0 */ + reg = ISPCSI1_LCx_DAT_START(0); /* reg = ISPCSI1_DAT_START; */ + val = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, reg); + BIT_SET(val, 16, 0xfff, config->u.csi.data_start); + isp_reg_writel(dev, val, OMAP3_ISP_IOMEM_CCP2, reg); + + /* ISPCSI1_DAT_SIZE for logical channel #0 */ + reg = ISPCSI1_LCx_DAT_SIZE(0); /* reg = ISPCSI1_DAT_SIZE; */ + val = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, reg); + BIT_SET(val, 16, 0xfff, config->u.csi.data_size); + isp_reg_writel(dev, val, OMAP3_ISP_IOMEM_CCP2, reg); + + /* Clear status bits for logical channel #0 */ + val = ISPCSI1_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | + ISPCSI1_LC01_IRQSTATUS_LC0_CRC_IRQ | + ISPCSI1_LC01_IRQSTATUS_LC0_FSP_IRQ | + ISPCSI1_LC01_IRQSTATUS_LC0_FW_IRQ | + ISPCSI1_LC01_IRQSTATUS_LC0_FSC_IRQ | + ISPCSI1_LC01_IRQSTATUS_LC0_SSC_IRQ; + + /* Clear IRQ status bits for logical channel #0 */ + isp_reg_writel(dev, val, OMAP3_ISP_IOMEM_CCP2, + ISPCSI1_LC01_IRQSTATUS); + + /* Enable IRQs for logical channel #0 */ + isp_reg_or(dev, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_LC01_IRQENABLE, val); + + /* Enable CSI1 */ + isp_csi_enable(dev, 1); + + if (!(isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, + ISPCSI1_CTRL) & BIT(4))) { + dev_warn(dev, "OMAP3 CSI1 bus not available\n"); + if (config->u.csi.signalling) { + /* Strobe mode requires CCP2 */ + return -EIO; + } + } + + return 0; +} + +/** + * isp_configure_interface - Configures ISP Control I/F related parameters. + * @dev: Device pointer specific to the OMAP3 ISP. + * @config: Pointer to structure containing the desired configuration for the + * ISP. + * + * Configures ISP control register (ISP_CTRL) with the values specified inside + * the config structure. Controls: + * - Selection of parallel or serial input to the preview hardware. + * - Data lane shifter. + * - Pixel clock polarity. + * - 8 to 16-bit bridge at the input of CCDC module. + * - HS or VS synchronization signal detection + * + * Returns 0 on success, otherwise, will return other negative error value. + **/ +int isp_configure_interface(struct device *dev, + struct isp_interface_config *config) +{ + struct isp_device *isp = dev_get_drvdata(dev); + u32 ispctrl_val = isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + u32 fmtcfg; + int r; + + isp->config = config; + + ispctrl_val &= ISPCTRL_SHIFT_MASK; + ispctrl_val |= config->dataline_shift << ISPCTRL_SHIFT_SHIFT; + ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; + + ispctrl_val &= ISPCTRL_PAR_SER_CLK_SEL_MASK; + + isp_buf_init(dev); + + switch (config->ccdc_par_ser) { + case ISP_PARLL: + case ISP_PARLL_YUV_BT: + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; + ispctrl_val |= config->u.par.par_clk_pol + << ISPCTRL_PAR_CLK_POL_SHIFT; + ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_BENDIAN; + ispctrl_val |= config->u.par.par_bridge + << ISPCTRL_PAR_BRIDGE_SHIFT; + if (config->ccdc_par_ser == ISP_PARLL_YUV_BT) + isp->bt656ifen = 1; + break; + case ISP_CSIA: + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; + ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_BENDIAN; + + if (config->u.csi.crc) + isp_csi2_ctrl_config_ecc_enable(true); + + isp_csi2_ctrl_config_vp_out_ctrl(config->u.csi.vpclk); + isp_csi2_ctrl_config_vp_only_enable(true); + isp_csi2_ctrl_config_vp_clk_enable(true); + isp_csi2_ctrl_update(false); + + isp_csi2_ctx_config_format(0, config->u.csi.format); + isp_csi2_ctx_update(0, false); + + isp_csi2_irq_complexio1_set(1); + isp_csi2_irq_status_set(1); + + isp_csi2_enable(1); + mdelay(3); + break; + case ISP_CSIB: + ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; + r = isp_init_csi(dev, config); + if (r) + return r; + break; + case ISP_NONE: + return 0; + default: + return -EINVAL; + } + + ispctrl_val &= ~ISPCTRL_SYNC_DETECT_VSRISE; + ispctrl_val |= config->hsvs_syncdetect; + + isp_reg_writel(dev, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + + /* Set sensor specific fields in CCDC and Previewer module. */ + ispccdc_set_wenlog(&isp->isp_ccdc, config->wenlog); + + /* FIXME: this should be set in ispccdc_config_vp() */ + fmtcfg = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); + fmtcfg &= ISPCCDC_FMTCFG_VPIF_FRQ_MASK; + if (config->pixelclk) { + unsigned long l3_ick = clk_get_rate(isp->l3_ick); + unsigned long div = l3_ick / config->pixelclk; + if (div < 2) + div = 2; + if (div > 6) + div = 6; + fmtcfg |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; + } + isp_reg_writel(dev, fmtcfg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); + + return 0; +} +EXPORT_SYMBOL(isp_configure_interface); + +static int isp_buf_process(struct device *dev, struct isp_bufs *bufs); + +/** + * omap34xx_isp_isr - Interrupt Service Routine for Camera ISP module. + * @irq: Not used currently. + * @_pdev: Pointer to the platform device associated with the OMAP3 ISP. + * + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the + * IRQ wasn't handled. + **/ +static irqreturn_t omap34xx_isp_isr(int irq, void *_pdev) +{ + struct device *dev = &((struct platform_device *)_pdev)->dev; + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_irq *irqdis = &isp->irq; + struct isp_bufs *bufs = &isp->bufs; + unsigned long flags; + u32 irqstatus = 0; + u32 sbl_pcr; + unsigned long irqflags = 0; + int wait_hs_vs = 0; + u8 fld_stat; + + if (isp->running == ISP_STOPPED) { + dev_err(dev, "ouch %8.8x!\n",isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS)); + return IRQ_NONE; + } + + irqstatus = isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + isp_reg_writel(dev, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); + + if (isp->running == ISP_STOPPING) + return IRQ_HANDLED; + + spin_lock_irqsave(&bufs->lock, flags); + wait_hs_vs = bufs->wait_hs_vs; + if (irqstatus & HS_VS) { + if (bufs->wait_hs_vs) { + bufs->wait_hs_vs--; + } else { + if (isp->pipeline.pix.field == V4L2_FIELD_INTERLACED) { + fld_stat = (isp_reg_readl(dev, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE) & + ISPCCDC_SYN_MODE_FLDSTAT) ? 1 : 0; + isp->current_field = fld_stat; + } + } + } + spin_unlock_irqrestore(&bufs->lock, flags); + + spin_lock_irqsave(&isp->lock, irqflags); + + if (irqstatus & RESZ_DONE) { + if (irqdis->isp_callbk[CBK_RESZ_DONE]) + irqdis->isp_callbk[CBK_RESZ_DONE]( + RESZ_DONE, + irqdis->isp_callbk_arg1[CBK_RESZ_DONE], + irqdis->isp_callbk_arg2[CBK_RESZ_DONE]); + else if (!RAW_CAPTURE(isp)) { + ispresizer_config_shadow_registers(&isp->isp_res); + isp_buf_process(dev, bufs); + } + } + /* + * We need to wait for the first HS_VS interrupt from CCDC. + * Otherwise our frame (and everything else) might be bad. + */ + switch (wait_hs_vs) { + case 1: + /* + * Enable preview for the first time. We just have + * missed the start-of-frame so we can do it now. + */ + if (irqstatus & HS_VS && !RAW_CAPTURE(isp)) + isppreview_enable(&isp->isp_prev); + default: + goto out_ignore_buff; + case 0: + break; + } + + if (irqstatus & CCDC_VD0) { + if (isp->pipeline.pix.field == V4L2_FIELD_INTERLACED) { + /* Skip even fields, and process only odd fields */ + if (isp->current_field != 0) + if (RAW_CAPTURE(isp)) + isp_buf_process(dev, bufs); + } + if (!ispccdc_busy(&isp->isp_ccdc)) + ispccdc_config_shadow_registers(&isp->isp_ccdc); + } + + if (irqstatus & PREV_DONE) { + if (irqdis->isp_callbk[CBK_PREV_DONE]) + irqdis->isp_callbk[CBK_PREV_DONE]( + PREV_DONE, + irqdis->isp_callbk_arg1[CBK_PREV_DONE], + irqdis->isp_callbk_arg2[CBK_PREV_DONE]); + else if (!RAW_CAPTURE(isp)) { + if (ispresizer_busy(&isp->isp_res)) { + ISP_BUF_DONE(bufs)->vb_state = + VIDEOBUF_ERROR; + dev_err(dev, "%s: resizer busy!\n", __func__); + } else { + ispresizer_enable(&isp->isp_res, 1); + } + isppreview_config_shadow_registers(&isp->isp_prev); + isppreview_enable(&isp->isp_prev); + } + } + + if (irqstatus & H3A_AWB_DONE) + isph3a_aewb_isr(&isp->isp_h3a); + + if (irqstatus & HIST_DONE) { + if (irqdis->isp_callbk[CBK_HIST_DONE]) + irqdis->isp_callbk[CBK_HIST_DONE]( + HIST_DONE, + irqdis->isp_callbk_arg1[CBK_HIST_DONE], + irqdis->isp_callbk_arg2[CBK_HIST_DONE]); + } + + if (irqstatus & H3A_AF_DONE) + isp_af_isr(&isp->isp_af); + + /* Handle shared buffer logic overflows for video buffers. */ + /* ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored. */ + sbl_pcr = isp_reg_readl(dev, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR) & + ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF; + isp_reg_writel(dev, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); + if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF + | ISPSBL_PCR_RSZ2_WBL_OVF + | ISPSBL_PCR_RSZ3_WBL_OVF + | ISPSBL_PCR_RSZ4_WBL_OVF + | ISPSBL_PCR_PRV_WBL_OVF + | ISPSBL_PCR_CCDC_WBL_OVF + | ISPSBL_PCR_CSIA_WBL_OVF + | ISPSBL_PCR_CSIB_WBL_OVF)) { + struct isp_buf *buf = ISP_BUF_DONE(bufs); + buf->vb_state = VIDEOBUF_ERROR; + dev_info(dev, "%s: sbl overflow, sbl_pcr = %8.8x\n", + __func__, sbl_pcr); + } + +out_ignore_buff: + if (irqstatus & LSC_PRE_ERR) { + struct isp_buf *buf = ISP_BUF_DONE(bufs); + /* Mark buffer faulty. */ + buf->vb_state = VIDEOBUF_ERROR; + ispccdc_lsc_error_handler(&isp->isp_ccdc); + dev_err(dev, "%s: lsc prefetch error\n", __func__); + } + + if (irqstatus & CSIA) { + struct isp_buf *buf = ISP_BUF_DONE(bufs); + int ret = isp_csi2_isr(); + if (ret) + buf->vb_state = VIDEOBUF_ERROR; + } + + if (irqstatus & IRQ0STATUS_CSIB_IRQ) { + struct isp_buf *buf = ISP_BUF_DONE(bufs); + u32 ispcsi1_irqstatus; + + ispcsi1_irqstatus = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCP2, + ISPCSI1_LC01_IRQSTATUS); + isp_reg_writel(dev, ispcsi1_irqstatus, OMAP3_ISP_IOMEM_CCP2, + ISPCSI1_LC01_IRQSTATUS); + buf->vb_state = VIDEOBUF_ERROR; + dev_err(dev, "CCP2 err:%x\n", ispcsi1_irqstatus); + } + + if (irqdis->isp_callbk[CBK_CATCHALL]) { + irqdis->isp_callbk[CBK_CATCHALL]( + irqstatus, + irqdis->isp_callbk_arg1[CBK_CATCHALL], + irqdis->isp_callbk_arg2[CBK_CATCHALL]); + } + + spin_unlock_irqrestore(&isp->lock, irqflags); + + isp_flush(dev); + +#if 1 + { + static const struct { + int num; + char *name; + } bits[] = { + { 31, "HS_VS_IRQ" }, + { 30, "SEC_ERR_IRQ" }, + { 29, "OCP_ERR_IRQ" }, + { 28, "MMU_ERR_IRQ" }, + { 27, "res27" }, + { 26, "res26" }, + { 25, "OVF_IRQ" }, + { 24, "RSZ_DONE_IRQ" }, + { 23, "res23" }, + { 22, "res22" }, + { 21, "CBUFF_IRQ" }, + { 20, "PRV_DONE_IRQ" }, + { 19, "CCDC_LSC_PREFETCH_ERROR" }, + { 18, "CCDC_LSC_PREFETCH_COMPLETED" }, + { 17, "CCDC_LSC_DONE" }, + { 16, "HIST_DONE_IRQ" }, + { 15, "res15" }, + { 14, "res14" }, + { 13, "H3A_AWB_DONE_IRQ" }, + { 12, "H3A_AF_DONE_IRQ" }, + { 11, "CCDC_ERR_IRQ" }, + { 10, "CCDC_VD2_IRQ" }, + { 9, "CCDC_VD1_IRQ" }, + { 8, "CCDC_VD0_IRQ" }, + { 7, "res7" }, + { 6, "res6" }, + { 5, "res5" }, + { 4, "CSIB_IRQ" }, + { 3, "CSIB_LCM_IRQ" }, + { 2, "res2" }, + { 1, "res1" }, + { 0, "CSIA_IRQ" }, + }; + int i; + for (i = 0; i < ARRAY_SIZE(bits); i++) { + if ((1 << bits[i].num) & irqstatus) + DPRINTK_ISPCTRL("%s ", bits[i].name); + } + DPRINTK_ISPCTRL("\n"); + } +#endif + + return IRQ_HANDLED; +} + +/* Device name, needed for resource tracking layer */ +struct device_driver camera_drv = { + .name = "camera" +}; + +struct device camera_dev = { + .driver = &camera_drv, +}; + +/** + * isp_tmp_buf_free - Free buffer for CCDC->PRV->RSZ datapath workaround. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +static void isp_tmp_buf_free(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (isp->tmp_buf) { + iommu_vfree(isp->iommu, isp->tmp_buf); + isp->tmp_buf = 0; + isp->tmp_buf_size = 0; + } +} + +/** + * isp_tmp_buf_alloc - Allocate buffer for CCDC->PRV->RSZ datapath workaround. + * @dev: Device pointer specific to the OMAP3 ISP. + * @size: Byte size of the buffer to allocate + * + * Returns 0 if successful, or -ENOMEM if there's no available memory. + **/ +static u32 isp_tmp_buf_alloc(struct device *dev, size_t size) +{ + struct isp_device *isp = dev_get_drvdata(dev); + u32 da; + + isp_tmp_buf_free(dev); + + dev_dbg(dev, "%s: allocating %d bytes\n", __func__, size); + + da = iommu_vmalloc(isp->iommu, 0, size, IOMMU_FLAG); + if (IS_ERR_VALUE(da)) { + dev_err(dev, "iommu_vmap mapping failed "); + return -ENOMEM; + } + isp->tmp_buf = da; + isp->tmp_buf_size = size; + + isppreview_set_outaddr(&isp->isp_prev, isp->tmp_buf); + ispresizer_set_inaddr(&isp->isp_res, isp->tmp_buf); + + return 0; +} + +/** + * isp_start - Set ISP in running state + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +void isp_start(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + isp->running = ISP_RUNNING; + + return; +} +EXPORT_SYMBOL(isp_start); + +#define ISP_STATISTICS_BUSY \ + () +#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) + +/** + * __isp_disable_modules - Disable ISP submodules with a timeout to be idle. + * @dev: Device pointer specific to the OMAP3 ISP. + * @suspend: If 0, disable modules; if 1, send modules to suspend state. + * + * Returns 0 if stop/suspend left in idle state all the submodules properly, + * or returns 1 if a general Reset is required to stop/suspend the submodules. + **/ +static int __isp_disable_modules(struct device *dev, int suspend) +{ + struct isp_device *isp = dev_get_drvdata(dev); + unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; + int reset = 0; + + /* + * We need to stop all the modules after CCDC first or they'll + * never stop since they may not get a full frame from CCDC. + */ + if (suspend) { + isp_af_suspend(&isp->isp_af); + isph3a_aewb_suspend(&isp->isp_h3a); + isp_hist_suspend(&isp->isp_hist); + } else { + isp_af_enable(&isp->isp_af, 0); + isph3a_aewb_enable(&isp->isp_h3a, 0); + isp_hist_enable(&isp->isp_hist, 0); + } + ispresizer_enable(&isp->isp_res, 0); + + timeout = jiffies + ISP_STOP_TIMEOUT; + while (isp_af_busy(&isp->isp_af) + || isph3a_aewb_busy(&isp->isp_h3a) + || isp_hist_busy(&isp->isp_hist) + || isppreview_busy(&isp->isp_prev) + || ispresizer_busy(&isp->isp_res)) { + if (time_after(jiffies, timeout)) { + dev_err(dev, "%s: can't stop non-ccdc modules\n", + __func__); + reset = 1; + break; + } + msleep(1); + } + + /* Let's stop CCDC now. */ + ispccdc_enable(&isp->isp_ccdc, 0); + + timeout = jiffies + ISP_STOP_TIMEOUT; + while (ispccdc_busy(&isp->isp_ccdc)) { + if (time_after(jiffies, timeout)) { + dev_err(dev, "%s: can't stop ccdc\n", __func__); + reset = 1; + break; + } + msleep(1); + } + + isp_csi_enable(dev, 0); + isp_csi2_enable(0); + isp_buf_init(dev); + + return reset; +} + +/** + * isp_stop_modules - Stop ISP submodules. + * @dev: Device pointer specific to the OMAP3 ISP. + * + * Returns 0 if stop left in idle state all the submodules properly, + * or returns 1 if a general Reset is required to stop the submodules. + **/ +static int isp_stop_modules(struct device *dev) +{ + return __isp_disable_modules(dev, 0); +} + +#ifdef CONFIG_PM +/** + * isp_suspend_modules - Suspend ISP submodules. + * @dev: Device pointer specific to the OMAP3 ISP. + * + * Returns 0 if suspend left in idle state all the submodules properly, + * or returns 1 if a general Reset is required to suspend the submodules. + **/ +static int isp_suspend_modules(struct device *dev) +{ + return __isp_disable_modules(dev, 1); +} + +/** + * isp_resume_modules - Resume ISP submodules. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +static void isp_resume_modules(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + isp_hist_resume(&isp->isp_hist); + isph3a_aewb_resume(&isp->isp_h3a); + isp_af_resume(&isp->isp_af); +} +#endif /* CONFIG_PM */ + +/** + * isp_reset - Reset ISP with a timeout wait for idle. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +static void isp_reset(struct device *dev) +{ + unsigned long timeout = 0; + + isp_reg_writel(dev, + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) + | ISP_SYSCONFIG_SOFTRESET, + OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); + while (!(isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, + ISP_SYSSTATUS) & 0x1)) { + if (timeout++ > 10000) { + dev_alert(dev, "%s: cannot reset ISP\n", __func__); + break; + } + udelay(1); + } +} + +/** + * isp_stop - Stop ISP. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +void isp_stop(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int reset; + + isp->running = ISP_STOPPING; + isp_disable_interrupts(dev); + synchronize_irq(((struct isp_device *)dev_get_drvdata(dev))->irq_num); + isp->running = ISP_STOPPED; + reset = isp_stop_modules(dev); + if (!reset) + return; + + isp_save_ctx(dev); + isp_reset(dev); + isp_restore_ctx(dev); +} +EXPORT_SYMBOL(isp_stop); + +/** + * isp_set_buf - Program output buffer address based on current pipeline. + * @dev: Device pointer specific to the OMAP3 ISP. + * @buf: Pointer to ISP buffer structure. + **/ +static void isp_set_buf(struct device *dev, struct isp_buf *buf) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (isp->pipeline.modules & OMAP_ISP_RESIZER + && is_ispresizer_enabled()) + ispresizer_set_outaddr(&isp->isp_res, buf->isp_addr); + else if (isp->pipeline.modules & OMAP_ISP_CCDC) + ispccdc_set_outaddr(&isp->isp_ccdc, buf->isp_addr); + +} + +/** + * isp_try_pipeline - Retrieve and simulate resulting internal ISP pipeline. + * @dev: Device pointer specific to the OMAP3 ISP. + * @pix_input: Pointer to pixel format to use as input in the ISP. + * @pipe: Pointer to ISP pipeline structure to fill back. + * + * Returns the closest possible output size based on silicon limitations + * detailed through the pipe structure. + * + * If the input can't be read, it'll return -EINVAL. Returns 0 on success. + **/ +static int isp_try_pipeline(struct device *dev, + struct v4l2_pix_format *pix_input, + struct isp_pipeline *pipe) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct v4l2_pix_format *pix_output = &pipe->pix; + unsigned int wanted_width = pix_output->width; + unsigned int wanted_height = pix_output->height; + int ifmt; + int rval; + + if ((pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10 || + pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8 || + pix_input->pixelformat == V4L2_PIX_FMT_SRGGB10 || + pix_input->pixelformat == V4L2_PIX_FMT_SBGGR10 || + pix_input->pixelformat == V4L2_PIX_FMT_SGBRG10) && + (pix_output->pixelformat == V4L2_PIX_FMT_YUYV || + pix_output->pixelformat == V4L2_PIX_FMT_UYVY)) { + pipe->modules = OMAP_ISP_CCDC | OMAP_ISP_PREVIEW + | OMAP_ISP_RESIZER; + if (pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10 || + pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8) + pipe->ccdc_in = CCDC_RAW_GRBG; + if (pix_input->pixelformat == V4L2_PIX_FMT_SRGGB10) + pipe->ccdc_in = CCDC_RAW_RGGB; + if (pix_input->pixelformat == V4L2_PIX_FMT_SBGGR10) + pipe->ccdc_in = CCDC_RAW_BGGR; + if (pix_input->pixelformat == V4L2_PIX_FMT_SGBRG10) + pipe->ccdc_in = CCDC_RAW_GBRG; + pipe->ccdc_out = CCDC_OTHERS_VP; + pipe->prv_in = PRV_RAW_CCDC; + pipe->prv_out = PREVIEW_MEM; + pipe->rsz_in = RSZ_MEM_YUV; + } else { + pipe->modules = OMAP_ISP_CCDC; + if (pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10 || + pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8 || + pix_input->pixelformat == V4L2_PIX_FMT_SRGGB10 || + pix_input->pixelformat == V4L2_PIX_FMT_SBGGR10 || + pix_input->pixelformat == V4L2_PIX_FMT_SGBRG10) { + pipe->ccdc_out = CCDC_OTHERS_VP_MEM; + if (pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10 || + pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8) + pipe->ccdc_in = CCDC_RAW_GRBG; + if (pix_input->pixelformat == V4L2_PIX_FMT_SRGGB10) + pipe->ccdc_in = CCDC_RAW_RGGB; + if (pix_input->pixelformat == V4L2_PIX_FMT_SBGGR10) + pipe->ccdc_in = CCDC_RAW_BGGR; + if (pix_input->pixelformat == V4L2_PIX_FMT_SGBRG10) + pipe->ccdc_in = CCDC_RAW_GBRG; + } else if (pix_input->pixelformat == V4L2_PIX_FMT_YUYV || + pix_input->pixelformat == V4L2_PIX_FMT_UYVY) { + if (isp->bt656ifen) + pipe->ccdc_in = CCDC_YUV_BT; + else + pipe->ccdc_in = CCDC_YUV_SYNC; + pipe->ccdc_out = CCDC_OTHERS_MEM; + } else + return -EINVAL; + } + + if (pipe->modules & OMAP_ISP_CCDC) { + pipe->ccdc_in_w = pix_input->width; + pipe->ccdc_in_h = pix_input->height; + rval = ispccdc_try_pipeline(&isp->isp_ccdc, pipe); + if (rval) { + dev_err(dev, "the dimensions %dx%d are not" + " supported\n", pix_input->width, + pix_input->height); + return rval; + } + pix_output->width = pipe->ccdc_out_w_img; + pix_output->height = pipe->ccdc_out_h; + pix_output->bytesperline = + pipe->ccdc_out_w * ISP_BYTES_PER_PIXEL; + } + + if (pipe->modules & OMAP_ISP_PREVIEW) { + rval = isppreview_try_pipeline(&isp->isp_prev, pipe); + if (rval) { + dev_err(dev, "the dimensions %dx%d are not" + " supported\n", pix_input->width, + pix_input->height); + return rval; + } + pix_output->width = pipe->prv_out_w; + pix_output->height = pipe->prv_out_h; + } + + if (pipe->modules & OMAP_ISP_RESIZER) { + pipe->rsz_out_w = wanted_width; + pipe->rsz_out_h = wanted_height; + + pipe->rsz_crop.left = pipe->rsz_crop.top = 0; + pipe->rsz_crop.width = pipe->prv_out_w_img; + pipe->rsz_crop.height = pipe->prv_out_h_img; + + rval = ispresizer_try_pipeline(&isp->isp_res, pipe); + if (rval) { + dev_err(dev, "The dimensions %dx%d are not" + " supported\n", pix_input->width, + pix_input->height); + return rval; + } + + pix_output->width = pipe->rsz_out_w; + pix_output->height = pipe->rsz_out_h; + pix_output->bytesperline = + pipe->rsz_out_w * ISP_BYTES_PER_PIXEL; + } + + if (isp->bt656ifen) + pix_output->field = pix_input->field; + else { + pix_output->field = V4L2_FIELD_NONE; + pix_output->sizeimage = + PAGE_ALIGN(pix_output->bytesperline * + pix_output->height); + } + pix_output->priv = 0; + + for (ifmt = 0; ifmt < NUM_ISP_CAPTURE_FORMATS; ifmt++) { + if (pix_output->pixelformat == isp_formats[ifmt].pixelformat) + break; + } + if (ifmt == NUM_ISP_CAPTURE_FORMATS) + pix_output->pixelformat = V4L2_PIX_FMT_YUYV; + + switch (pix_output->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + if (isp->bt656ifen) + pix_output->colorspace = pix_input->colorspace; + else + pix_output->colorspace = V4L2_COLORSPACE_JPEG; + break; + default: + pix_output->colorspace = V4L2_COLORSPACE_SRGB; + } + + return 0; +} + +/** + * isp_s_pipeline - Configure internal ISP pipeline. + * @dev: Device pointer specific to the OMAP3 ISP. + * @pix_input: Pointer to pixel format to use as input in the ISP. + * @pix_output: Pointer to pixel format to use as output in the ISP. + * + * Returns the closest possible output size based on silicon limitations. + * + * If the input can't be read, it'll return -EINVAL. Returns 0 on success. + **/ +static int isp_s_pipeline(struct device *dev, + struct v4l2_pix_format *pix_input, + struct v4l2_pix_format *pix_output) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_pipeline pipe; + int rval; + + isp_release_resources(dev); + + pipe.pix = *pix_output; + + rval = isp_try_pipeline(dev, pix_input, &pipe); + if (rval) + return rval; + + ispccdc_request(&isp->isp_ccdc); + ispccdc_s_pipeline(&isp->isp_ccdc, &pipe); + + if (pix_input->pixelformat == V4L2_PIX_FMT_UYVY) + ispccdc_config_y8pos(&isp->isp_ccdc, Y8POS_ODD); + else if (pix_input->pixelformat == V4L2_PIX_FMT_YUYV) + ispccdc_config_y8pos(&isp->isp_ccdc, Y8POS_EVEN); + + if (((pix_input->pixelformat == V4L2_PIX_FMT_UYVY) && + (pix_output->pixelformat == V4L2_PIX_FMT_UYVY)) || + ((pix_input->pixelformat == V4L2_PIX_FMT_YUYV) && + (pix_output->pixelformat == V4L2_PIX_FMT_YUYV))) + /* input and output formats are in same order */ + ispccdc_config_byteswap(&isp->isp_ccdc, 0); + else if (((pix_input->pixelformat == V4L2_PIX_FMT_YUYV) && + (pix_output->pixelformat == V4L2_PIX_FMT_UYVY)) || + ((pix_input->pixelformat == V4L2_PIX_FMT_UYVY) && + (pix_output->pixelformat == V4L2_PIX_FMT_YUYV))) + /* input and output formats are in reverse order */ + ispccdc_config_byteswap(&isp->isp_ccdc, 1); + /* + * Configure Pitch - This enables application to use a + * different pitch + * other than active pixels per line. + */ + if (isp->bt656ifen) + ispccdc_config_outlineoffset(&isp->isp_ccdc, + pipe.pix.bytesperline, 0, 0); + if (pipe.modules & OMAP_ISP_PREVIEW) { + isppreview_request(&isp->isp_prev); + isppreview_s_pipeline(&isp->isp_prev, &pipe); + } + + if (pipe.modules & OMAP_ISP_RESIZER) { + ispresizer_request(&isp->isp_res); + ispresizer_s_pipeline(&isp->isp_res, &pipe); + } + + isp->pipeline = pipe; + *pix_output = isp->pipeline.pix; + + return 0; +} + +/** + * isp_vbq_sync - Flush the entire cache + * @vb: Videobuffer to sync. (Not used) + * @when: (Not used) + * + * FIXME: This impacts the performance on the other systems when camera is + * running, but seems to be needed to ensure coherency of DMA transfers + * somehow. Investigation ongoing... + **/ +static int isp_vbq_sync(struct videobuf_buffer *vb, int when) +{ + flush_cache_all(); + + return 0; +} + +/** + * isp_buf_init - Initialize the internal buffer queue handling. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +static void isp_buf_init(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_bufs *bufs = &isp->bufs; + int sg; + + bufs->queue = 0; + bufs->done = 0; + bufs->wait_hs_vs = isp->config->wait_hs_vs; + for (sg = 0; sg < NUM_BUFS; sg++) { + if (bufs->buf[sg].vb) { + isp_vbq_sync(bufs->buf[sg].vb, DMA_FROM_DEVICE); + bufs->buf[sg].vb->state = VIDEOBUF_ERROR; + bufs->buf[sg].complete(bufs->buf[sg].vb, + bufs->buf[sg].priv); + } + bufs->buf[sg].complete = NULL; + bufs->buf[sg].vb = NULL; + bufs->buf[sg].priv = NULL; + } +} + +/** + * isp_buf_process - Do final handling when a buffer has been processed. + * @dev: Device pointer specific to the OMAP3 ISP. + * @bufs: Pointer to ISP buffer handling structure. + * + * Updates the pointers accordingly depending of the internal pipeline. + **/ +static int isp_buf_process(struct device *dev, struct isp_bufs *bufs) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_buf *buf = NULL; + unsigned long flags; + int last; + + spin_lock_irqsave(&bufs->lock, flags); + + if (ISP_BUFS_IS_EMPTY(bufs)) + goto out; + + if (RAW_CAPTURE(isp) && ispccdc_sbl_wait_idle(&isp->isp_ccdc, 1000)) { + dev_err(dev, "ccdc %d won't become idle!\n", + RAW_CAPTURE(isp)); + goto out; + } + + /* We had at least one buffer in queue. */ + buf = ISP_BUF_DONE(bufs); + last = ISP_BUFS_IS_LAST(bufs); + + if (!last) { + /* Set new buffer address. */ + isp_set_buf(dev, ISP_BUF_NEXT_DONE(bufs)); + } else { + /* Tell ISP not to write any of our buffers. */ + isp_disable_interrupts(dev); + if (RAW_CAPTURE(isp)) + ispccdc_enable(&isp->isp_ccdc, 0); + else if (isp->bt656ifen == 0) + ispresizer_enable(&isp->isp_res, 0); + /* + * We must wait for the HS_VS since before that the + * CCDC may trigger interrupts even if it's not + * receiving a frame. + */ + bufs->wait_hs_vs = isp->config->wait_hs_vs; + } + if ((RAW_CAPTURE(isp) && ispccdc_busy(&isp->isp_ccdc)) + || (!RAW_CAPTURE(isp) && ispresizer_busy(&isp->isp_res))) { + /* + * Next buffer available: for the transfer to succeed, the + * CCDC (RAW capture) or resizer (YUV capture) must be idle + * for the duration of transfer setup. Bad things happen + * otherwise! + * + * Next buffer not available: if we fail to stop the + * ISP the buffer is probably going to be bad. + */ + /* Mark this buffer faulty. */ + buf->vb_state = VIDEOBUF_ERROR; + /* Mark next faulty, too, in case we have one. */ + if (!last) { + ISP_BUF_NEXT_DONE(bufs)->vb_state = VIDEOBUF_ERROR; + dev_alert(dev, "OUCH!!!\n"); + } else { + dev_alert(dev, "Ouch!\n"); + } + } + + /* Mark the current buffer as done. */ + ISP_BUF_MARK_DONE(bufs); + + DPRINTK_ISPCTRL(KERN_ALERT "%s: finish %d mmu %p\n", __func__, + (bufs->done - 1 + NUM_BUFS) % NUM_BUFS, + (bufs->buf+((bufs->done - 1 + NUM_BUFS) + % NUM_BUFS))->isp_addr); + +out: + spin_unlock_irqrestore(&bufs->lock, flags); + + if (buf && buf->vb) { + /* + * We want to dequeue a buffer from the video buffer + * queue. Let's do it! + */ + isp_vbq_sync(buf->vb, DMA_FROM_DEVICE); + buf->vb->state = buf->vb_state; + buf->complete(buf->vb, buf->priv); + buf->vb = NULL; + } + + return 0; +} + +/** + * isp_buf_queue - Queue a buffer into the internal ISP queue list. + * @dev: Device pointer specific to the OMAP3 ISP. + * @vb: Pointer to video buffer to queue. + * @complete: Pointer to function to call when buffer is completely processed. + * @priv: Pointer to private paramemter to send to complete function. + * + * Always returns 0. + **/ +int isp_buf_queue(struct device *dev, struct videobuf_buffer *vb, + void (*complete)(struct videobuf_buffer *vb, void *priv), + void *priv) +{ + struct isp_device *isp = dev_get_drvdata(dev); + unsigned long flags; + struct isp_buf *buf; + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + const struct scatterlist *sglist = dma->sglist; + struct isp_bufs *bufs = &isp->bufs; + int sglen = dma->sglen; + + if (isp->running != ISP_RUNNING) { + vb->state = VIDEOBUF_ERROR; + complete(vb, priv); + + return 0; + } + + BUG_ON(sglen < 0 || !sglist); + + isp_vbq_sync(vb, DMA_TO_DEVICE); + + spin_lock_irqsave(&bufs->lock, flags); + + BUG_ON(ISP_BUFS_IS_FULL(bufs)); + + buf = ISP_BUF_QUEUE(bufs); + + buf->isp_addr = bufs->isp_addr_capture[vb->i]; + buf->complete = complete; + buf->vb = vb; + buf->priv = priv; + buf->vb_state = VIDEOBUF_DONE; + buf->vb->state = VIDEOBUF_ACTIVE; + + if (ISP_BUFS_IS_EMPTY(bufs)) { + isp_enable_interrupts(dev, RAW_CAPTURE(isp)); + isp_set_buf(dev, buf); + ispccdc_enable(&isp->isp_ccdc, 1); + } + + ISP_BUF_MARK_QUEUED(bufs); + + spin_unlock_irqrestore(&bufs->lock, flags); + + DPRINTK_ISPCTRL(KERN_ALERT "%s: queue %d vb %d, mmu %p\n", __func__, + (bufs->queue - 1 + NUM_BUFS) % NUM_BUFS, vb->i, + buf->isp_addr); + + return 0; +} +EXPORT_SYMBOL(isp_buf_queue); + +/** + * isp_vbq_setup - Do ISP specific actions when the VB wueue is set. + * @dev: Device pointer specific to the OMAP3 ISP. + * @vbq: Pointer to video buffer queue. + * @cnt: Pointer to buffer count size of the queue list. + * @size: Pointer to the bytesize of every video buffer queue entry. + * + * Currently, this just allocates the temporary buffer used for the + * ISP Workaround when having CCDC->PRV->RSZ internal datapath. + **/ +int isp_vbq_setup(struct device *dev, struct videobuf_queue *vbq, + unsigned int *cnt, unsigned int *size) +{ + struct isp_device *isp = dev_get_drvdata(dev); + size_t tmp_size = PAGE_ALIGN(isp->pipeline.prv_out_w + * isp->pipeline.prv_out_h + * ISP_BYTES_PER_PIXEL); + + if (isp->pipeline.modules & OMAP_ISP_PREVIEW + && isp->tmp_buf_size < tmp_size) + return isp_tmp_buf_alloc(dev, tmp_size); + + return 0; +} +EXPORT_SYMBOL(isp_vbq_setup); + +/** + * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list + * @dev: Device pointer specific to the OMAP3 ISP. + * @sglist: Pointer to source Scatter gather list to allocate. + * @sglen: Number of elements of the scatter-gatter list. + * + * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if + * we ran out of memory. + **/ +dma_addr_t ispmmu_vmap(struct device *dev, const struct scatterlist *sglist, + int sglen) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int err; + u32 da; + struct sg_table *sgt; + unsigned int i; + struct scatterlist *sg, *src = (struct scatterlist *)sglist; + + /* + * convert isp sglist to iommu sgt + * FIXME: should be fixed in the upper layer? + */ + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + err = sg_alloc_table(sgt, sglen, GFP_KERNEL); + if (err) + goto err_sg_alloc; + + for_each_sg(sgt->sgl, sg, sgt->nents, i) + sg_set_buf(sg, phys_to_virt(sg_dma_address(src + i)), + sg_dma_len(src + i)); + + da = iommu_vmap(isp->iommu, 0, sgt, IOMMU_FLAG); + if (IS_ERR_VALUE(da)) + goto err_vmap; + + return (dma_addr_t)da; + +err_vmap: + sg_free_table(sgt); +err_sg_alloc: + kfree(sgt); + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(ispmmu_vmap); + +/** + * ispmmu_vunmap - Unmap a device address from the ISP MMU + * @dev: Device pointer specific to the OMAP3 ISP. + * @da: Device address generated from a ispmmu_vmap call. + **/ +void ispmmu_vunmap(struct device *dev, dma_addr_t da) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct sg_table *sgt; + + sgt = iommu_vunmap(isp->iommu, (u32)da); + if (!sgt) + return; + sg_free_table(sgt); + kfree(sgt); +} +EXPORT_SYMBOL_GPL(ispmmu_vunmap); + +/** + * isp_vbq_prepare - Videobuffer queue prepare. + * @dev: Device pointer specific to the OMAP3 ISP. + * @vbq: Pointer to videobuf_queue structure. + * @vb: Pointer to videobuf_buffer structure. + * @field: Requested Field order for the videobuffer. + * + * Returns 0 if successful, or -EIO if the ispmmu was unable to map a + * scatter-gather linked list data space. + **/ +int isp_vbq_prepare(struct device *dev, struct videobuf_queue *vbq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct isp_device *isp = dev_get_drvdata(dev); + unsigned int isp_addr; + struct videobuf_dmabuf *vdma; + struct isp_bufs *bufs = &isp->bufs; + + int err = 0; + + vdma = videobuf_to_dma(vb); + + isp_addr = ispmmu_vmap(dev, vdma->sglist, vdma->sglen); + + if (IS_ERR_VALUE(isp_addr)) + err = -EIO; + else + bufs->isp_addr_capture[vb->i] = isp_addr; + + return err; +} +EXPORT_SYMBOL(isp_vbq_prepare); + +/** + * isp_vbq_release - Videobuffer queue release. + * @dev: Device pointer specific to the OMAP3 ISP. + * @vbq: Pointer to videobuf_queue structure. + * @vb: Pointer to videobuf_buffer structure. + **/ +void isp_vbq_release(struct device *dev, struct videobuf_queue *vbq, + struct videobuf_buffer *vb) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_bufs *bufs = &isp->bufs; + + ispmmu_vunmap(dev, bufs->isp_addr_capture[vb->i]); + bufs->isp_addr_capture[vb->i] = (dma_addr_t)NULL; + return; +} +EXPORT_SYMBOL(isp_vbq_release); + +/** + * isp_queryctrl - Query V4L2 control from existing controls in ISP. + * @a: Pointer to v4l2_queryctrl structure. It only needs the id field filled. + * + * Returns 0 if successful, or -EINVAL if not found in ISP. + **/ +int isp_queryctrl(struct v4l2_queryctrl *a) +{ + int i; + + if (a->id & V4L2_CTRL_FLAG_NEXT_CTRL) { + a->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; + i = find_next_vctrl(a->id); + } else { + i = find_vctrl(a->id); + } + + if (i < 0) + return -EINVAL; + + *a = video_control[i].qc; + return 0; +} +EXPORT_SYMBOL(isp_queryctrl); + +/** + * isp_queryctrl - Query V4L2 control from existing controls in ISP. + * @a: Pointer to v4l2_queryctrl structure. It only needs the id field filled. + * + * Returns 0 if successful, or -EINVAL if not found in ISP. + **/ +int isp_querymenu(struct v4l2_querymenu *a) +{ + int i; + + i = find_vmenu(a->id, a->index); + + if (i < 0) + return -EINVAL; + + *a = video_menu[i]; + return 0; +} +EXPORT_SYMBOL(isp_querymenu); + +/** + * isp_g_ctrl - Get value of the desired V4L2 control. + * @dev: Device pointer specific to the OMAP3 ISP. + * @a: V4L2 control to read actual value from. + * + * Return 0 if successful, or -EINVAL if chosen control is not found. + **/ +int isp_g_ctrl(struct device *dev, struct v4l2_control *a) +{ + struct isp_device *isp = dev_get_drvdata(dev); + u8 current_value; + int rval = 0; + + if (!isp->ref_count) + return -EINVAL; + + switch (a->id) { + case V4L2_CID_BRIGHTNESS: + isppreview_query_brightness(&isp->isp_prev, ¤t_value); + a->value = current_value / ISPPRV_BRIGHT_UNITS; + break; + case V4L2_CID_CONTRAST: + isppreview_query_contrast(&isp->isp_prev, ¤t_value); + a->value = current_value / ISPPRV_CONTRAST_UNITS; + break; + case V4L2_CID_COLORFX: + isppreview_get_color(&isp->isp_prev, ¤t_value); + a->value = current_value; + break; + default: + rval = -EINVAL; + break; + } + + return rval; +} +EXPORT_SYMBOL(isp_g_ctrl); + +/** + * isp_s_ctrl - Set value of the desired V4L2 control. + * @dev: Device pointer specific to the OMAP3 ISP. + * @a: V4L2 control to read actual value from. + * + * Return 0 if successful, -EINVAL if chosen control is not found or value + * is out of bounds, -EFAULT if copy_from_user or copy_to_user operation fails + * from camera abstraction layer related controls or the transfered user space + * pointer via the value field is not set properly. + **/ +int isp_s_ctrl(struct device *dev, struct v4l2_control *a) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int rval = 0; + u8 new_value = a->value; + + if (!isp->ref_count) + return -EINVAL; + + switch (a->id) { + case V4L2_CID_BRIGHTNESS: + if (a->value > ISPPRV_BRIGHT_HIGH) + rval = -EINVAL; + else + isppreview_update_brightness(&isp->isp_prev, + &new_value); + break; + case V4L2_CID_CONTRAST: + if (a->value > ISPPRV_CONTRAST_HIGH) + rval = -EINVAL; + else + isppreview_update_contrast(&isp->isp_prev, &new_value); + break; + case V4L2_CID_COLORFX: + if (a->value > V4L2_COLORFX_SEPIA) + rval = -EINVAL; + else + isppreview_set_color(&isp->isp_prev, &new_value); + break; + default: + rval = -EINVAL; + break; + } + + return rval; +} +EXPORT_SYMBOL(isp_s_ctrl); + +/** + * isp_handle_private - Handle all private ioctls for isp module. + * @dev: Device pointer specific to the OMAP3 ISP. + * @cmd: ioctl cmd value + * @arg: ioctl arg value + * + * Return 0 if successful, -EINVAL if chosen cmd value is not handled or value + * is out of bounds, -EFAULT if ioctl arg value is not valid. + * Function simply routes the input ioctl cmd id to the appropriate handler in + * the isp module. + **/ +int isp_handle_private(struct device *dev, int cmd, void *arg) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int rval = 0; + + if (!isp->ref_count) + return -EINVAL; + + switch (cmd) { + case VIDIOC_PRIVATE_ISP_CCDC_CFG: + rval = omap34xx_isp_ccdc_config(&isp->isp_ccdc, arg); + break; + case VIDIOC_PRIVATE_ISP_PRV_CFG: + rval = omap34xx_isp_preview_config(&isp->isp_prev, arg); + break; + case VIDIOC_PRIVATE_ISP_AEWB_CFG: { + struct isph3a_aewb_config *params; + params = (struct isph3a_aewb_config *)arg; + rval = isph3a_aewb_configure(&isp->isp_h3a, params); + } + break; + case VIDIOC_PRIVATE_ISP_AEWB_REQ: { + struct isph3a_aewb_data *data; + data = (struct isph3a_aewb_data *)arg; + rval = isph3a_aewb_request_statistics(&isp->isp_h3a, data); + } + break; + case VIDIOC_PRIVATE_ISP_HIST_CFG: { + struct isp_hist_config *params; + params = (struct isp_hist_config *)arg; + rval = isp_hist_configure(&isp->isp_hist, params); + } + break; + case VIDIOC_PRIVATE_ISP_HIST_REQ: { + struct isp_hist_data *data; + data = (struct isp_hist_data *)arg; + rval = isp_hist_request_statistics(&isp->isp_hist, data); + } + break; + case VIDIOC_PRIVATE_ISP_AF_CFG: { + struct af_configuration *params; + params = (struct af_configuration *)arg; + rval = isp_af_configure(&isp->isp_af, params); + } + break; + case VIDIOC_PRIVATE_ISP_AF_REQ: { + struct isp_af_data *data; + data = (struct isp_af_data *)arg; + rval = isp_af_request_statistics(&isp->isp_af, data); + } + break; + default: + rval = -EINVAL; + break; + } + return rval; +} +EXPORT_SYMBOL(isp_handle_private); + +/** + * isp_enum_fmt_cap - Get more information of chosen format index and type + * @f: Pointer to structure containing index and type of format to read from. + * + * Returns 0 if successful, or -EINVAL if format index or format type is + * invalid. + **/ +int isp_enum_fmt_cap(struct v4l2_fmtdesc *f) +{ + int index = f->index; + enum v4l2_buf_type type = f->type; + int rval = -EINVAL; + + if (index >= NUM_ISP_CAPTURE_FORMATS) + goto err; + + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + rval = 0; + break; + default: + goto err; + } + + f->flags = isp_formats[index].flags; + strncpy(f->description, isp_formats[index].description, + sizeof(f->description)); + f->pixelformat = isp_formats[index].pixelformat; +err: + return rval; +} +EXPORT_SYMBOL(isp_enum_fmt_cap); + +/** + * isp_g_fmt_cap - Get current output image format. + * @dev: Device pointer specific to the OMAP3 ISP. + * @pix: Pointer to V4L2 format structure to return current output format + **/ +void isp_g_fmt_cap(struct device *dev, struct v4l2_pix_format *pix) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + *pix = isp->pipeline.pix; + return; +} +EXPORT_SYMBOL(isp_g_fmt_cap); + +/** + * isp_s_fmt_cap - Set I/O formats and crop, and configure pipeline in ISP + * @dev: Device pointer specific to the OMAP3 ISP. + * @pix_input: Pointer to V4L2 format structure to represent current input. + * @pix_output: Pointer to V4L2 format structure to represent current output. + * + * Returns 0 if successful, -EINVAL if ISP hasn't been opened, or return + * value of isp_s_pipeline if there is an error. + **/ +int isp_s_fmt_cap(struct device *dev, struct v4l2_pix_format *pix_input, + struct v4l2_pix_format *pix_output) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (!isp->ref_count) + return -EINVAL; + + return isp_s_pipeline(dev, pix_input, pix_output); +} +EXPORT_SYMBOL(isp_s_fmt_cap); + +/** + * isp_g_crop - Get crop rectangle size and position. + * @dev: Device pointer specific to the OMAP3 ISP. + * @crop: Pointer to V4L2 crop structure to be filled. + * + * Always returns 0. + **/ +int isp_g_crop(struct device *dev, struct v4l2_crop *crop) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + if (isp->pipeline.modules & OMAP_ISP_RESIZER) { + crop->c = isp->pipeline.rsz_crop; + } else { + crop->c.left = 0; + crop->c.top = 0; + crop->c.width = isp->pipeline.ccdc_out_w_img; + crop->c.height = isp->pipeline.ccdc_out_h; + } + + return 0; +} +EXPORT_SYMBOL(isp_g_crop); + +/** + * isp_s_crop - Set crop rectangle size and position. + * @dev: Device pointer specific to the OMAP3 ISP. + * @a: Pointer to V4L2 crop structure with desired parameters. + * + * Always returns 0. + * + * FIXME: Hardcoded to configure always the resizer, which could not be always + * the case. + **/ +int isp_s_crop(struct device *dev, struct v4l2_crop *a) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + ispresizer_config_crop(&isp->isp_res, a); + + return 0; +} +EXPORT_SYMBOL(isp_s_crop); + +/** + * isp_try_fmt_cap - Try desired input/output image formats + * @dev: Device pointer specific to the OMAP3 ISP. + * @pix_input: Pointer to V4L2 pixel format structure for input image. + * @pix_output: Pointer to V4L2 pixel format structure for output image. + * + * Returns 0 if successful, or return value of either isp_try_size or + * isp_try_fmt if there is an error. + **/ +int isp_try_fmt_cap(struct device *dev, struct v4l2_pix_format *pix_input, + struct v4l2_pix_format *pix_output) +{ + struct isp_pipeline pipe; + int rval; + + pipe.pix = *pix_output; + + rval = isp_try_pipeline(dev, pix_input, &pipe); + if (rval) + return rval; + + *pix_output = pipe.pix; + + return 0; +} +EXPORT_SYMBOL(isp_try_fmt_cap); + +/** + * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. + * @dev: Device pointer specific to the OMAP3 ISP. + * + * Routine for saving the context of each module in the ISP. + * CCDC, HIST, H3A, PREV, RESZ and IOMMU. + **/ +static void isp_save_ctx(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + isp_save_context(dev, isp_reg_list); + ispccdc_save_context(dev); + if (isp->iommu) + iommu_save_ctx(isp->iommu); + isphist_save_context(dev); + isph3a_save_context(dev); + isppreview_save_context(dev); + ispresizer_save_context(dev); + ispcsi2_save_context(dev); +} + +/** + * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. + * @dev: Device pointer specific to the OMAP3 ISP. + * + * Routine for restoring the context of each module in the ISP. + * CCDC, HIST, H3A, PREV, RESZ and IOMMU. + **/ +static void isp_restore_ctx(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + isp_restore_context(dev, isp_reg_list); + ispccdc_restore_context(dev); + if (isp->iommu) + iommu_restore_ctx(isp->iommu); + isphist_restore_context(dev); + isph3a_restore_context(dev); + isppreview_restore_context(dev); + ispresizer_restore_context(dev); + ispcsi2_restore_context(dev); +} + +/** + * isp_enable_clocks - Enable ISP clocks + * @dev: Device pointer specific to the OMAP3 ISP. + * + * Return 0 if successful, or clk_enable return value if any of tthem fails. + **/ +static int isp_enable_clocks(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int r; + + r = clk_enable(isp->cam_ick); + if (r) { + dev_err(dev, "clk_enable cam_ick failed\n"); + goto out_clk_enable_ick; + } + r = clk_enable(isp->cam_mclk); + if (r) { + dev_err(dev, "clk_enable cam_mclk failed\n"); + goto out_clk_enable_mclk; + } + r = clk_enable(isp->csi2_fck); + if (r) { + dev_err(dev, "clk_enable csi2_fck failed\n"); + goto out_clk_enable_csi2_fclk; + } + return 0; + +out_clk_enable_csi2_fclk: + clk_disable(isp->cam_mclk); +out_clk_enable_mclk: + clk_disable(isp->cam_ick); +out_clk_enable_ick: + return r; +} + +/** + * isp_disable_clocks - Disable ISP clocks + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +static void isp_disable_clocks(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + clk_disable(isp->cam_ick); + clk_disable(isp->cam_mclk); + clk_disable(isp->csi2_fck); +} + +/** + * isp_get - Acquire the ISP resource. + * + * Initializes the clocks for the first acquire. + * + * Returns pointer for isp device structure. + **/ +struct device *isp_get(void) +{ + struct platform_device *pdev = omap3isp_pdev; + struct isp_device *isp; + static int has_context; + int ret_err = 0; + + if (!pdev) + return NULL; + isp = platform_get_drvdata(pdev); + + DPRINTK_ISPCTRL("isp_get: old %d\n", isp->ref_count); + mutex_lock(&(isp->isp_mutex)); + if (isp->ref_count == 0) { + ret_err = isp_enable_clocks(&pdev->dev); + if (ret_err) + goto out_err; + /* We don't want to restore context before saving it! */ + if (has_context) + isp_restore_ctx(&pdev->dev); + else + has_context = 1; + } else { + mutex_unlock(&isp->isp_mutex); + return NULL; + } + isp->ref_count++; + mutex_unlock(&(isp->isp_mutex)); + + DPRINTK_ISPCTRL("isp_get: new %d\n", isp->ref_count); + /* FIXME: ISP should register as v4l2 device to store its priv data */ + return &pdev->dev; + +out_err: + mutex_unlock(&(isp->isp_mutex)); + return NULL; +} +EXPORT_SYMBOL(isp_get); + +/** + * isp_put - Release the ISP resource. + * + * Releases the clocks also for the last release. + * + * Return resulting reference count, or -EBUSY if ISP structure is not + * allocated. + **/ +int isp_put(void) +{ + struct platform_device *pdev = omap3isp_pdev; + struct isp_device *isp = platform_get_drvdata(pdev); + + if (!isp) + return -EBUSY; + + DPRINTK_ISPCTRL("isp_put: old %d\n", isp->ref_count); + mutex_lock(&(isp->isp_mutex)); + if (isp->ref_count) { + if (--isp->ref_count == 0) { + isp_save_ctx(&pdev->dev); + isp_tmp_buf_free(&pdev->dev); + isp_release_resources(&pdev->dev); + isp_disable_clocks(&pdev->dev); + } + } + mutex_unlock(&(isp->isp_mutex)); + DPRINTK_ISPCTRL("isp_put: new %d\n", isp->ref_count); + return isp->ref_count; +} +EXPORT_SYMBOL(isp_put); + +/** + * isp_save_context - Saves the values of the ISP module registers. + * @dev: Device pointer specific to the OMAP3 ISP. + * @reg_list: Structure containing pairs of register address and value to + * modify on OMAP. + **/ +void isp_save_context(struct device *dev, struct isp_reg *reg_list) +{ + struct isp_reg *next = reg_list; + + for (; next->reg != ISP_TOK_TERM; next++) + next->val = isp_reg_readl(dev, next->mmio_range, next->reg); +} +EXPORT_SYMBOL(isp_save_context); + +/** + * isp_restore_context - Restores the values of the ISP module registers. + * @dev: Device pointer specific to the OMAP3 ISP. + * @reg_list: Structure containing pairs of register address and value to + * modify on OMAP. + **/ +void isp_restore_context(struct device *dev, struct isp_reg *reg_list) +{ + struct isp_reg *next = reg_list; + + for (; next->reg != ISP_TOK_TERM; next++) + isp_reg_writel(dev, next->val, next->mmio_range, next->reg); +} +EXPORT_SYMBOL(isp_restore_context); + +/** + * isp_remove - Remove ISP platform device + * @pdev: Pointer to ISP platform device + * + * Always returns 0. + **/ +static int isp_remove(struct platform_device *pdev) +{ + struct isp_device *isp = platform_get_drvdata(pdev); + int i; + + if (!isp) + return 0; + + isp_csi2_cleanup(&pdev->dev); + isp_af_exit(&pdev->dev); + isp_preview_cleanup(&pdev->dev); + isp_resizer_cleanup(&pdev->dev); + isp_get(); + if (isp->iommu) + iommu_put(isp->iommu); + isp_put(); + isph3a_aewb_cleanup(&pdev->dev); + isp_hist_cleanup(&pdev->dev); + isp_ccdc_cleanup(&pdev->dev); + + clk_put(isp->cam_ick); + clk_put(isp->cam_mclk); + clk_put(isp->csi2_fck); + clk_put(isp->l3_ick); + + free_irq(isp->irq_num, isp); + + for (i = 0; i <= OMAP3_ISP_IOMEM_CSI2PHY; i++) { + if (isp->mmio_base[i]) { + iounmap((void *)isp->mmio_base[i]); + isp->mmio_base[i] = 0; + } + + if (isp->mmio_base_phys[i]) { + release_mem_region(isp->mmio_base_phys[i], + isp->mmio_size[i]); + isp->mmio_base_phys[i] = 0; + } + } + + omap3isp_pdev = NULL; + kfree(isp); + + return 0; +} + +#ifdef CONFIG_PM + +/** + * isp_suspend - Suspend routine for the ISP + * @dev: Pointer to ISP device + * + * Always returns 0. + **/ +static int isp_suspend(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int reset; + + DPRINTK_ISPCTRL("isp_suspend: starting\n"); + + if (mutex_is_locked(&isp->isp_mutex)) + dev_err(dev, "%s: bug: isp_mutex is locked\n", __func__); + + if (isp->ref_count == 0) + goto out; + + isp_disable_interrupts(dev); + reset = isp_suspend_modules(dev); + isp_save_ctx(dev); + if (reset) + isp_reset(dev); + + isp_disable_clocks(dev); + +out: + DPRINTK_ISPCTRL("isp_suspend: done\n"); + + return 0; +} + +/** + * isp_resume - Resume routine for the ISP + * @dev: Pointer to ISP device + * + * Returns 0 if successful, or isp_enable_clocks return value otherwise. + **/ +static int isp_resume(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + int ret_err = 0; + + DPRINTK_ISPCTRL("isp_resume: starting\n"); + + if (mutex_is_locked(&isp->isp_mutex)) + dev_err(dev, "%s: bug: isp_mutex is locked\n", __func__); + + if (isp->ref_count == 0) + goto out; + + ret_err = isp_enable_clocks(dev); + if (ret_err) + goto out; + isp_restore_ctx(dev); + isp_resume_modules(dev); + +out: + DPRINTK_ISPCTRL("isp_resume: done \n"); + + return ret_err; +} + +#else + +#define isp_suspend NULL +#define isp_resume NULL + +#endif /* CONFIG_PM */ + +/** + * isp_probe - Probe ISP platform device + * @pdev: Pointer to ISP platform device + * + * Returns 0 if successful, + * -ENOMEM if no memory available, + * -ENODEV if no platform device resources found + * or no space for remapping registers, + * -EINVAL if couldn't install ISR, + * or clk_get return error value. + **/ +static int isp_probe(struct platform_device *pdev) +{ + struct isp_device *isp; + int ret_err = 0; + int i; + + isp = kzalloc(sizeof(*isp), GFP_KERNEL); + if (!isp) { + dev_err(&pdev->dev, "could not allocate memory\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, isp); + + isp->dev = &pdev->dev; + + for (i = 0; i <= OMAP3_ISP_IOMEM_CSI2PHY; i++) { + struct resource *mem; + /* request the mem region for the camera registers */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!mem) { + dev_err(isp->dev, "no mem resource?\n"); + ret_err = -ENODEV; + goto out_free_mmio; + } + + if (!request_mem_region(mem->start, mem->end - mem->start + 1, + pdev->name)) { + dev_err(isp->dev, + "cannot reserve camera register I/O region\n"); + ret_err = -ENODEV; + goto out_free_mmio; + } + isp->mmio_base_phys[i] = mem->start; + isp->mmio_size[i] = mem->end - mem->start + 1; + + /* map the region */ + isp->mmio_base[i] = (unsigned long) + ioremap_nocache(isp->mmio_base_phys[i], + isp->mmio_size[i]); + if (!isp->mmio_base[i]) { + dev_err(isp->dev, + "cannot map camera register I/O region\n"); + ret_err = -ENODEV; + goto out_free_mmio; + } + } + + isp->irq_num = platform_get_irq(pdev, 0); + if (isp->irq_num <= 0) { + dev_err(isp->dev, "no irq for camera?\n"); + ret_err = -ENODEV; + goto out_free_mmio; + } + + isp->cam_ick = clk_get(&camera_dev, "cam_ick"); + if (IS_ERR(isp->cam_ick)) { + dev_err(isp->dev, "clk_get cam_ick failed\n"); + ret_err = PTR_ERR(isp->cam_ick); + goto out_free_mmio; + } + isp->cam_mclk = clk_get(&camera_dev, "cam_mclk"); + if (IS_ERR(isp->cam_mclk)) { + dev_err(isp->dev, "clk_get cam_mclk failed\n"); + ret_err = PTR_ERR(isp->cam_mclk); + goto out_clk_get_mclk; + } + isp->csi2_fck = clk_get(&camera_dev, "csi2_96m_fck"); + if (IS_ERR(isp->csi2_fck)) { + dev_err(isp->dev, "clk_get csi2_96m_fck failed\n"); + ret_err = PTR_ERR(isp->csi2_fck); + goto out_clk_get_csi2_fclk; + } + isp->l3_ick = clk_get(&camera_dev, "l3_ick"); + if (IS_ERR(isp->l3_ick)) { + dev_err(isp->dev, "clk_get l3_ick failed\n"); + ret_err = PTR_ERR(isp->l3_ick); + goto out_clk_get_l3_ick; + } + + if (request_irq(isp->irq_num, omap34xx_isp_isr, IRQF_SHARED, + "Omap 3 Camera ISP", pdev)) { + dev_err(isp->dev, "could not install isr\n"); + ret_err = -EINVAL; + goto out_request_irq; + } + + isp->ref_count = 0; + omap3isp_pdev = pdev; + + mutex_init(&(isp->isp_mutex)); + spin_lock_init(&isp->lock); + spin_lock_init(&isp->bufs.lock); + spin_lock_init(&isp->h3a_lock); + + isp_get(); + isp->iommu = iommu_get("isp"); + if (IS_ERR(isp->iommu)) { + ret_err = PTR_ERR(isp->iommu); + isp->iommu = NULL; + } + isp_put(); + if (!isp->iommu) + goto out_iommu_get; + + isp_ccdc_init(&pdev->dev); + isp_hist_init(&pdev->dev); + isph3a_aewb_init(&pdev->dev); + isp_preview_init(&pdev->dev); + isp_resizer_init(&pdev->dev); + isp_af_init(&pdev->dev); + isp_csi2_init(&pdev->dev); + + isp_get(); + isp_power_settings(&pdev->dev, 1); + isp_put(); + + return 0; + +out_iommu_get: + free_irq(isp->irq_num, isp); + omap3isp_pdev = NULL; +out_request_irq: + clk_put(isp->l3_ick); +out_clk_get_l3_ick: + clk_put(isp->csi2_fck); +out_clk_get_csi2_fclk: + clk_put(isp->cam_mclk); +out_clk_get_mclk: + clk_put(isp->cam_ick); +out_free_mmio: + for (i = 0; i <= OMAP3_ISP_IOMEM_CSI2PHY; i++) { + if (isp->mmio_base[i]) { + iounmap((void *)isp->mmio_base[i]); + isp->mmio_base[i] = 0; + } + + if (isp->mmio_base_phys[i]) { + release_mem_region(isp->mmio_base_phys[i], + isp->mmio_size[i]); + isp->mmio_base_phys[i] = 0; + } + } + + kfree(isp); + return ret_err; +} + +static struct dev_pm_ops omap3isp_pm_ops = { + .suspend = isp_suspend, + .resume = isp_resume, +}; + +static struct platform_driver omap3isp_driver = { + .probe = isp_probe, + .remove = isp_remove, + .driver = { + .name = "omap3isp", + .pm = &omap3isp_pm_ops, + }, +}; + +/** + * isp_init - ISP module initialization. + **/ +static int __init isp_init(void) +{ + return platform_driver_register(&omap3isp_driver); +} + +/** + * isp_cleanup - ISP module cleanup. + **/ +static void __exit isp_cleanup(void) +{ + platform_driver_unregister(&omap3isp_driver); +} + +/** + * isp_print_status - Prints the values of the ISP Control Module registers + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +void isp_print_status(struct device *dev) +{ + if (!is_ispctrl_debug_enabled()) + return; + + DPRINTK_ISPCTRL("###ISP_CTRL=0x%x\n", + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); + DPRINTK_ISPCTRL("###ISP_TCTRL_CTRL=0x%x\n", + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, + ISP_TCTRL_CTRL)); + DPRINTK_ISPCTRL("###ISP_SYSCONFIG=0x%x\n", + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, + ISP_SYSCONFIG)); + DPRINTK_ISPCTRL("###ISP_SYSSTATUS=0x%x\n", + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, + ISP_SYSSTATUS)); + DPRINTK_ISPCTRL("###ISP_IRQ0ENABLE=0x%x\n", + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, + ISP_IRQ0ENABLE)); + DPRINTK_ISPCTRL("###ISP_IRQ0STATUS=0x%x\n", + isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, + ISP_IRQ0STATUS)); +} +EXPORT_SYMBOL(isp_print_status); + +module_init(isp_init); +module_exit(isp_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("ISP Control Module Library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/isp/isp.h b/drivers/media/video/isp/isp.h new file mode 100644 index 00000000000..dc85d61b258 --- /dev/null +++ b/drivers/media/video/isp/isp.h @@ -0,0 +1,545 @@ +/* + * isp.h + * + * Top level public header file for ISP Control module in + * TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments. + * Copyright (C) 2009 Nokia. + * + * Contributors: + * Sameer Venkatraman + * Mohit Jalori + * Sergio Aguirre + * Sakari Ailus + * Tuukka Toivonen + * + * 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 OMAP_ISP_TOP_H +#define OMAP_ISP_TOP_H +#include +#include +#include + +#include + +#include +#include + +struct isp_pipeline; + +#include "ispstat.h" +#include "isp_af.h" +#include "isphist.h" +#include "ispccdc.h" +#include "ispreg.h" +#include "isph3a.h" +#include "ispresizer.h" +#include "isppreview.h" + +#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) + +#define OMAP_ISP_CCDC (1 << 0) +#define OMAP_ISP_PREVIEW (1 << 1) +#define OMAP_ISP_RESIZER (1 << 2) +#define OMAP_ISP_AEWB (1 << 3) +#define OMAP_ISP_AF (1 << 4) +#define OMAP_ISP_HIST (1 << 5) + +#define ISP_TOK_TERM 0xFFFFFFFF /* + * terminating token for ISP + * modules reg list + */ +#define NUM_BUFS VIDEO_MAX_FRAME + +#ifndef CONFIG_ARCH_OMAP3410 +#define USE_ISP_PREVIEW +#define USE_ISP_RESZ +#define is_isppreview_enabled() 1 +#define is_ispresizer_enabled() 1 +#else +#define is_isppreview_enabled() 0 +#define is_ispresizer_enabled() 0 +#endif + +#define ISP_BYTES_PER_PIXEL 2 +#define NUM_ISP_CAPTURE_FORMATS (sizeof(isp_formats) / \ + sizeof(isp_formats[0])) + +typedef int (*isp_vbq_callback_ptr) (struct videobuf_buffer *vb); +typedef void (*isp_callback_t) (unsigned long status, + isp_vbq_callback_ptr arg1, void *arg2); + +enum isp_mem_resources { + OMAP3_ISP_IOMEM_MAIN, + OMAP3_ISP_IOMEM_CBUFF, + OMAP3_ISP_IOMEM_CCP2, + OMAP3_ISP_IOMEM_CCDC, + OMAP3_ISP_IOMEM_HIST, + OMAP3_ISP_IOMEM_H3A, + OMAP3_ISP_IOMEM_PREV, + OMAP3_ISP_IOMEM_RESZ, + OMAP3_ISP_IOMEM_SBL, + OMAP3_ISP_IOMEM_CSI2A, + OMAP3_ISP_IOMEM_CSI2PHY +}; + +enum isp_interface_type { + ISP_PARLL = 1, + ISP_CSIA = 2, + ISP_CSIB = 4, + ISP_PARLL_YUV_BT = 8, + ISP_NONE = 16 /* memory input to preview / resizer */ +}; + +enum isp_irqevents { + CSIA = 0x01, + CSIB = 0x10, + CCDC_VD0 = 0x100, + CCDC_VD1 = 0x200, + CCDC_VD2 = 0x400, + CCDC_ERR = 0x800, + H3A_AWB_DONE = 0x2000, + H3A_AF_DONE = 0x1000, + HIST_DONE = 0x10000, + PREV_DONE = 0x100000, + LSC_DONE = 0x20000, + LSC_PRE_COMP = 0x40000, + LSC_PRE_ERR = 0x80000, + RESZ_DONE = 0x1000000, + SBL_OVF = 0x2000000, + MMU_ERR = 0x10000000, + OCP_ERR = 0x20000000, + HS_VS = 0x80000000 +}; + +enum isp_callback_type { + CBK_CCDC_VD0, + CBK_CCDC_VD1, + CBK_PREV_DONE, + CBK_RESZ_DONE, + CBK_MMU_ERR, + CBK_HIST_DONE, + CBK_HS_VS, + CBK_LSC_ISR, + CBK_CATCHALL, + CBK_CSIA, + CBK_CSIB, + CBK_END, +}; + +enum isp_running { + ISP_STOPPED, + ISP_RUNNING, + ISP_STOPPING, +}; + +/** + * struct isp_reg - Structure for ISP register values. + * @reg: 32-bit Register address. + * @val: 32-bit Register value. + */ +struct isp_reg { + enum isp_mem_resources mmio_range; + u32 reg; + u32 val; +}; + +/** + * struct isp_interface_config - ISP interface configuration. + * @ccdc_par_ser: ISP interface type. 0 - Parallel, 1 - CSIA, 2 - CSIB to CCDC. + * @dataline_shift: Data lane shifter. + * 0 - No Shift, 1 - CAMEXT[13 to 2]->CAM[11 to 0] + * 2 - CAMEXT[13 to 4]->CAM[9 to 0] + * 3 - CAMEXT[13 to 6]->CAM[7 to 0] + * @hsvs_syncdetect: HS or VS synchronization signal detection. + * 0 - HS Falling, 1 - HS rising + * 2 - VS falling, 3 - VS rising + * @strobe: Strobe related parameter. + * @prestrobe: PreStrobe related parameter. + * @shutter: Shutter related parameter. + * @prev_sph: Horizontal Start Pixel performed in Preview module. + * @prev_slv: Vertical Start Line performed in Preview module. + * @wenlog: Store the value for the sensor specific wenlog field. + * @wait_hs_vs: Wait for this many hs_vs before anything else in the beginning. + * @pixelclk: Pixel data rate from sensor. + * @par_bridge: CCDC Bridge input control. Parallel interface. + * 0 - Disable, 1 - Enable, first byte->cam_d(bits 7 to 0) + * 2 - Enable, first byte -> cam_d(bits 15 to 8) + * @par_clk_pol: Pixel clock polarity on the parallel interface. + * 0 - Non Inverted, 1 - Inverted + * @crc: Use cyclic redundancy check. + * @mode: (?) + * @edge: Falling or rising edge + * @signalling: Use strobe mode (only valid for CCP2 mode) + * @strobe_clock_inv: Strobe/clock signal inversion. + * @vs_edge: Type of edge used for detecting VSync signal. + * @channel: Logical channel number used in transmission. + * @vpclk: Video port output clock. + * @data_start: Start vertical position of the region of interest. + * @data_size: Vertical size of the region of interest. + * @format: V4L2 format which matches with the transmitted frame data. + */ +struct isp_interface_config { + enum isp_interface_type ccdc_par_ser; + u8 dataline_shift; + u32 hsvs_syncdetect; + int strobe; + int prestrobe; + int shutter; + u32 prev_sph; + u32 prev_slv; + u32 wenlog; + int wait_hs_vs; + unsigned int pixelclk; + union { + struct par { + unsigned par_bridge:2; + unsigned par_clk_pol:1; + } par; + struct csi { + unsigned crc:1; + unsigned mode:1; + unsigned edge:1; + unsigned signalling:1; + unsigned strobe_clock_inv:1; + unsigned vs_edge:1; + unsigned channel:3; + unsigned vpclk:2; + unsigned int data_start; + unsigned int data_size; + u32 format; + } csi; + } u; +}; + +/** + * struct isp_buf - ISP buffer information structure. + * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer. + * @complete: Pointer to function used to handle the buffer once its complete + * @vb: Pointer to associated video buffer structure. + * @priv: Private pointer to send to associated complete handling function. + * @vb_state: Current ISP video buffer state. + */ +struct isp_buf { + dma_addr_t isp_addr; + void (*complete)(struct videobuf_buffer *vb, void *priv); + struct videobuf_buffer *vb; + void *priv; + u32 vb_state; +}; + +#define ISP_BUFS_IS_FULL(bufs) \ + (((bufs)->queue + 1) % NUM_BUFS == (bufs)->done) +#define ISP_BUFS_IS_EMPTY(bufs) ((bufs)->queue == (bufs)->done) +#define ISP_BUFS_IS_LAST(bufs) \ + ((bufs)->queue == ((bufs)->done + 1) % NUM_BUFS) +#define ISP_BUFS_QUEUED(bufs) \ + ((((bufs)->done - (bufs)->queue + NUM_BUFS)) % NUM_BUFS) +#define ISP_BUF_DONE(bufs) ((bufs)->buf + (bufs)->done) +#define ISP_BUF_NEXT_DONE(bufs) \ + ((bufs)->buf + ((bufs)->done + 1) % NUM_BUFS) +#define ISP_BUF_QUEUE(bufs) ((bufs)->buf + (bufs)->queue) +#define ISP_BUF_MARK_DONE(bufs) \ + (bufs)->done = ((bufs)->done + 1) % NUM_BUFS; +#define ISP_BUF_MARK_QUEUED(bufs) \ + (bufs)->queue = ((bufs)->queue + 1) % NUM_BUFS; + +/** + * struct isp_bufs - ISP internal buffer queue list. + * @isp_addr_capture: Array of addresses for the ISP buffers inside the list. + * @lock: For handling current buffer + * @buf: Array of ISP buffers inside the list. + * @queue: Next slot to queue a buffer. + * @done: Buffer that is being processed. + * @wait_hs_vs: Wait for this many hs_vs before anything else. + */ +struct isp_bufs { + dma_addr_t isp_addr_capture[VIDEO_MAX_FRAME]; + spinlock_t lock; + struct isp_buf buf[NUM_BUFS]; + int queue; + int done; + int wait_hs_vs; +}; + +/** + * struct ispirq - Structure for containing callbacks to be called in ISP ISR. + * @isp_callbk: Array which stores callback functions, indexed by the type of + * callback (8 possible types). + * @isp_callbk_arg1: Pointer to array containing pointers to the first argument + * to be passed to the requested callback function. + * @isp_callbk_arg2: Pointer to array containing pointers to the second + * argument to be passed to the requested callback function. + * + * This structure is used to contain all the callback functions related for + * each callback type (CBK_CCDC_VD0, CBK_CCDC_VD1, CBK_PREV_DONE, + * CBK_RESZ_DONE, CBK_MMU_ERR, CBK_H3A_AWB_DONE, CBK_HIST_DONE, CBK_HS_VS, + * CBK_LSC_ISR). + */ +struct isp_irq { + isp_callback_t isp_callbk[CBK_END]; + isp_vbq_callback_ptr isp_callbk_arg1[CBK_END]; + void *isp_callbk_arg2[CBK_END]; +}; + +/** + * struct isp_pipeline - ISP pipeline description. + * @modules: ISP submodules in use. + * @pix: Output pixel format details in v4l2_pix_format structure. + * @ccdc_in_v_st: CCDC input vertical start. + * @ccdc_in_h_st: CCDC input horizontal start. + * @ccdc_in_w: CCDC input width. + * @ccdc_in_h: CCDC input height. + * @ccdc_out_w: CCDC output width (with extra padding pixels). + * @ccdc_out_h: CCDC output height. + * @ccdc_out_w_img: CCDC output width. + * @ccdc_in: CCDC input source. + * @ccdc_out: CCDC output destination. + * @prv_out_w: Preview output width (with extra padding pixels). + * @prv_out_h: Preview output height (with extra padding pixels). + * @prv_out_w_img: Preview output width. + * @prv_out_h_img: Preview output height. + * @prv_in: Preview input source. + * @prv_out: Preview output destination. + * @rsz_crop: Resizer crop region. + * @rsz_out_w: Resizer output width (with extra padding pixels). + * @rsz_out_h: Resizer output height. + * @rsz_out_w_img: Resizer output width (valid image region). + */ +struct isp_pipeline { + unsigned int modules; + struct v4l2_pix_format pix; + unsigned int ccdc_in_v_st; + unsigned int ccdc_in_h_st; + unsigned int ccdc_in_w; + unsigned int ccdc_in_h; + unsigned int ccdc_out_w; + unsigned int ccdc_out_h; + unsigned int ccdc_out_w_img; + enum ccdc_input ccdc_in; + enum ccdc_output ccdc_out; + unsigned int prv_out_w; + unsigned int prv_out_h; + unsigned int prv_out_w_img; + unsigned int prv_out_h_img; + enum preview_input prv_in; + enum preview_output prv_out; + struct v4l2_rect rsz_crop; + unsigned int rsz_out_w; + unsigned int rsz_out_h; + unsigned int rsz_out_w_img; + enum resizer_input rsz_in; +}; + +#define RAW_CAPTURE(isp) \ + (!((isp)->pipeline.modules & OMAP_ISP_PREVIEW)) + +/** + * struct isp_device - ISP device structure. + * @dev: Device pointer specific to the OMAP3 ISP. + * @isp_obj: ISP information structure. + * @irq_num: Currently used IRQ number. + * @mmio_base: Array with kernel base addresses for ioremapped ISP register + * regions. + * @mmio_base_phys: Array with physical L4 bus addresses for ISP register + * regions. + * @mmio_size: Array with ISP register regions size in bytes. + * @lock: Spinlock for handling registered ISP callbacks. + * @h3a_lock: Spinlock for handling H3a (Not used) (?) + * @isp_mutex: Mutex for serializing requests to ISP. + * @ref_count: Reference count for handling multiple ISP requests. + * @cam_ick: Pointer to camera interface clock structure. + * @cam_mclk: Pointer to camera functional clock structure. + * @cam_fck: Pointer to camera functional clock structure. (outdated) + * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. + * @l3_ick: Pointer to OMAP3 L3 bus interface clock. + * @config: Pointer to currently set ISP interface configuration. + * @tmp_buf: ISP MMU mapped temporary buffer address used for 34xx Workaround + * for CCDC->PRV->RSZ datapath errata. + * @tmp_buf_size: ISP MMU mapped temporary buffer size used for 34xx Workaround + * for CCDC->PRV->RSZ datapath errata. + * @tmp_buf_offset: ISP MMU mapped temporary buffer line offset used for 34xx + * Workaround for CCDC->PRV->RSZ datapath errata. + * @bufs: Internal ISP buffer queue list. + * @irq: Currently attached ISP ISR callbacks information structure. + * @pipeline: Currently used internal ISP pipeline information. + * @interrupts: ISP interrupts staged for deferred enabling. + * @running: Current running/stopped status of ISP. + * @isp_af: Pointer to current settings for ISP AutoFocus SCM. + * @isp_hist: Pointer to current settings for ISP Histogram SCM. + * @isp_h3a: Pointer to current settings for ISP Auto Exposure and + * White Balance SCM. + * @isp_res: Pointer to current settings for ISP Resizer. + * @isp_prev: Pointer to current settings for ISP Preview. + * @isp_ccdc: Pointer to current settings for ISP CCDC. + * @iommu: Pointer to requested IOMMU instance for ISP. + * + * This structure is used to store the OMAP ISP Information. + */ +struct isp_device { + struct device *dev; + struct isp *isp_obj; + + /*** platform HW resources ***/ + unsigned int irq_num; + +#define mmio_base_main mmio_base[OMAP3_ISP_IOMEM_MAIN] +#define mmio_cbuff_main mmio_base[OMAP3_ISP_IOMEM_CBUFF] +#define mmio_ccp2_main mmio_base[OMAP3_ISP_IOMEM_CCP2] +#define mmio_ccdc_main mmio_base[OMAP3_ISP_IOMEM_CCDC] +#define mmio_hist_main mmio_base[OMAP3_ISP_IOMEM_HIST] +#define mmio_h3a_main mmio_base[OMAP3_ISP_IOMEM_H3A] +#define mmio_prev_main mmio_base[OMAP3_ISP_IOMEM_PREV] +#define mmio_resz_main mmio_base[OMAP3_ISP_IOMEM_RESZ] +#define mmio_sbl_main mmio_base[OMAP3_ISP_IOMEM_SBL] +#define mmio_csi2_main mmio_base[OMAP3_ISP_IOMEM_CSI2A] +#define mmio_csi2phy_main mmio_base[OMAP3_ISP_IOMEM_CSI2PHY] + unsigned long mmio_base[OMAP3_ISP_IOMEM_CSI2PHY + 1]; + unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_CSI2PHY + 1]; + unsigned long mmio_size[OMAP3_ISP_IOMEM_CSI2PHY + 1]; + + /* ISP Obj */ + spinlock_t lock; /* For handling registered ISP callbacks */ + spinlock_t h3a_lock; + struct mutex isp_mutex; /* For handling ref_count field */ + int ref_count; + struct clk *cam_ick; + struct clk *cam_mclk; + struct clk *csi2_fck; + struct clk *l3_ick; + struct isp_interface_config *config; + dma_addr_t tmp_buf; + size_t tmp_buf_size; + unsigned long tmp_buf_offset; + struct isp_bufs bufs; + struct isp_irq irq; + struct isp_pipeline pipeline; + u32 interrupts; + enum isp_running running; + int current_field; + int bt656ifen; + + /* ISP modules */ + struct isp_af_device isp_af; + struct isp_hist_device isp_hist; + struct isp_h3a_device isp_h3a; + struct isp_res_device isp_res; + struct isp_prev_device isp_prev; + struct isp_ccdc_device isp_ccdc; + + struct iommu *iommu; +}; + +void isp_flush(struct device *dev); + +u32 isp_reg_readl(struct device *dev, enum isp_mem_resources isp_mmio_range, + u32 reg_offset); + +void isp_reg_writel(struct device *dev, u32 reg_value, + enum isp_mem_resources isp_mmio_range, u32 reg_offset); + +void isp_reg_and(struct device *dev, enum isp_mem_resources mmio_range, u32 reg, + u32 and_bits); + +void isp_reg_or(struct device *dev, enum isp_mem_resources mmio_range, u32 reg, + u32 or_bits); + +void isp_reg_and_or(struct device *dev, enum isp_mem_resources mmio_range, + u32 reg, u32 and_bits, u32 or_bits); + +void isp_start(struct device *dev); + +void isp_stop(struct device *dev); + +int isp_buf_queue(struct device *dev, struct videobuf_buffer *vb, + void (*complete)(struct videobuf_buffer *vb, void *priv), + void *priv); + +int isp_vbq_setup(struct device *dev, struct videobuf_queue *vbq, + unsigned int *cnt, unsigned int *size); + +int isp_vbq_prepare(struct device *dev, struct videobuf_queue *vbq, + struct videobuf_buffer *vb, enum v4l2_field field); + +void isp_vbq_release(struct device *dev, struct videobuf_queue *vbq, + struct videobuf_buffer *vb); + +int isp_set_callback(struct device *dev, enum isp_callback_type type, + isp_callback_t callback, isp_vbq_callback_ptr arg1, + void *arg2); + +int isp_unset_callback(struct device *dev, enum isp_callback_type type); + +u32 isp_set_xclk(struct device *dev, u32 xclk, u8 xclksel); + +int isp_configure_interface(struct device *dev, + struct isp_interface_config *config); + +struct device *isp_get(void); + +int isp_put(void); + +int isp_queryctrl(struct v4l2_queryctrl *a); + +int isp_querymenu(struct v4l2_querymenu *a); + +int isp_g_ctrl(struct device *dev, struct v4l2_control *a); + +int isp_s_ctrl(struct device *dev, struct v4l2_control *a); + +int isp_enum_fmt_cap(struct v4l2_fmtdesc *f); + +int isp_try_fmt_cap(struct device *dev, struct v4l2_pix_format *pix_input, + struct v4l2_pix_format *pix_output); + +void isp_g_fmt_cap(struct device *dev, struct v4l2_pix_format *pix); + +int isp_s_fmt_cap(struct device *dev, struct v4l2_pix_format *pix_input, + struct v4l2_pix_format *pix_output); + +int isp_g_crop(struct device *dev, struct v4l2_crop *a); + +int isp_s_crop(struct device *dev, struct v4l2_crop *a); + +int isp_try_fmt(struct device *dev, struct v4l2_pix_format *pix_input, + struct v4l2_pix_format *pix_output); + +int isp_handle_private(struct device *dev, int cmd, void *arg); + +void isp_save_context(struct device *dev, struct isp_reg *); + +void isp_restore_context(struct device *dev, struct isp_reg *); + +void isp_print_status(struct device *dev); + +int __init isp_ccdc_init(struct device *dev); +int __init isp_hist_init(struct device *dev); +int __init isph3a_aewb_init(struct device *dev); +int __init isp_preview_init(struct device *dev); +int __init isp_resizer_init(struct device *dev); +int __init isp_af_init(struct device *dev); +int __init isp_csi2_init(struct device *dev); + +void isp_ccdc_cleanup(struct device *dev); +void isp_hist_cleanup(struct device *dev); +void isph3a_aewb_cleanup(struct device *dev); +void isp_preview_cleanup(struct device *dev); +void isp_resizer_cleanup(struct device *dev); +void isp_af_exit(struct device *dev); +void isp_csi2_cleanup(struct device *dev); + +/* FIXME: Remove these when iommu supports these directly. */ +dma_addr_t ispmmu_vmap(struct device *dev, const struct scatterlist *sglist, + int sglen); +void ispmmu_vunmap(struct device *dev, dma_addr_t da); + +#endif /* OMAP_ISP_TOP_H */ diff --git a/drivers/media/video/isp/isp_af.c b/drivers/media/video/isp/isp_af.c new file mode 100644 index 00000000000..f0e607d0acc --- /dev/null +++ b/drivers/media/video/isp/isp_af.c @@ -0,0 +1,513 @@ +/* + * isp_af.c + * + * AF module for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * David Cohen + * + * 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. + */ + +/* Linux specific include files */ +#include + +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "isph3a.h" +#include "isp_af.h" + +#define IS_OUT_OF_BOUNDS(value, min, max) \ + (((value) < (min)) || ((value) > (max))) + +static void __isp_af_enable(struct isp_af_device *isp_af, int enable); + +static void isp_af_set_address(struct isp_af_device *isp_af, + unsigned long address) +{ + isp_reg_writel(isp_af->dev, address, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AFBUFST); +} + +/* Function to check paxel parameters */ +static int isp_af_check_params(struct isp_af_device *isp_af, + struct af_configuration *afconfig) +{ + struct af_paxel *paxel_cfg = &afconfig->paxel_config; + struct af_iir *iir_cfg = &afconfig->iir_config; + int index; + + /* Check horizontal Count */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->hz_cnt, AF_PAXEL_HORIZONTAL_COUNT_MIN, + AF_PAXEL_HORIZONTAL_COUNT_MAX)) { + DPRINTK_ISP_AF("Error : Horizontal Count is incorrect"); + return -AF_ERR_HZ_COUNT; + } + + /* Check Vertical Count */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->vt_cnt, AF_PAXEL_VERTICAL_COUNT_MIN, + AF_PAXEL_VERTICAL_COUNT_MAX)) { + DPRINTK_ISP_AF("Error : Vertical Count is incorrect"); + return -AF_ERR_VT_COUNT; + } + + /* Check Height */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->height, AF_PAXEL_HEIGHT_MIN, + AF_PAXEL_HEIGHT_MAX)) { + DPRINTK_ISP_AF("Error : Height is incorrect"); + return -AF_ERR_HEIGHT; + } + + /* Check width */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->width, AF_PAXEL_WIDTH_MIN, + AF_PAXEL_WIDTH_MAX)) { + DPRINTK_ISP_AF("Error : Width is incorrect"); + return -AF_ERR_WIDTH; + } + + /* Check Line Increment */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->line_incr, AF_PAXEL_INCREMENT_MIN, + AF_PAXEL_INCREMENT_MAX)) { + DPRINTK_ISP_AF("Error : Line Increment is incorrect"); + return -AF_ERR_INCR; + } + + /* Check Horizontal Start */ + if ((paxel_cfg->hz_start % 2 != 0) || + (paxel_cfg->hz_start < (iir_cfg->hz_start_pos + 2)) || + IS_OUT_OF_BOUNDS(paxel_cfg->hz_start, + AF_PAXEL_HZSTART_MIN, AF_PAXEL_HZSTART_MAX)) { + DPRINTK_ISP_AF("Error : Horizontal Start is incorrect"); + return -AF_ERR_HZ_START; + } + + /* Check Vertical Start */ + if (IS_OUT_OF_BOUNDS(paxel_cfg->vt_start, AF_PAXEL_VTSTART_MIN, + AF_PAXEL_VTSTART_MAX)) { + DPRINTK_ISP_AF("Error : Vertical Start is incorrect"); + return -AF_ERR_VT_START; + } + + /* Check IIR */ + for (index = 0; index < AF_NUMBER_OF_COEF; index++) { + if ((iir_cfg->coeff_set0[index]) > AF_COEF_MAX) { + DPRINTK_ISP_AF("Error : Coefficient for set 0 is " + "incorrect"); + return -AF_ERR_IIR_COEF; + } + + if ((iir_cfg->coeff_set1[index]) > AF_COEF_MAX) { + DPRINTK_ISP_AF("Error : Coefficient for set 1 is " + "incorrect"); + return -AF_ERR_IIR_COEF; + } + } + + if (IS_OUT_OF_BOUNDS(iir_cfg->hz_start_pos, AF_IIRSH_MIN, + AF_IIRSH_MAX)) { + DPRINTK_ISP_AF("Error : IIRSH is incorrect"); + return -AF_ERR_IIRSH; + } + + /* Check HMF Threshold Values */ + if (afconfig->hmf_config.threshold > AF_THRESHOLD_MAX) { + DPRINTK_ISP_AF("Error : HMF Threshold is incorrect"); + return -AF_ERR_THRESHOLD; + } + + return 0; +} + +static void isp_af_register_setup(struct isp_af_device *isp_af) +{ + unsigned int pcr = 0, pax1 = 0, pax2 = 0, paxstart = 0; + unsigned int coef = 0; + unsigned int base_coef_set0 = 0; + unsigned int base_coef_set1 = 0; + int index; + + if (!isp_af->update) + return; + + __isp_af_enable(isp_af, 0); + + if (isp_af_busy(isp_af)) + goto out; + + /* Configure Hardware Registers */ + pax1 |= isp_af->config.paxel_config.width << AF_PAXW_SHIFT; + /* Set height in AFPAX1 */ + pax1 |= isp_af->config.paxel_config.height; + isp_reg_writel(isp_af->dev, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1); + + /* Configure AFPAX2 Register */ + /* Set Line Increment in AFPAX2 Register */ + pax2 |= isp_af->config.paxel_config.line_incr << AF_LINE_INCR_SHIFT; + /* Set Vertical Count */ + pax2 |= isp_af->config.paxel_config.vt_cnt << AF_VT_COUNT_SHIFT; + /* Set Horizontal Count */ + pax2 |= isp_af->config.paxel_config.hz_cnt; + isp_reg_writel(isp_af->dev, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2); + + /* Configure PAXSTART Register */ + /*Configure Horizontal Start */ + paxstart |= isp_af->config.paxel_config.hz_start << AF_HZ_START_SHIFT; + /* Configure Vertical Start */ + paxstart |= isp_af->config.paxel_config.vt_start; + isp_reg_writel(isp_af->dev, paxstart, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AFPAXSTART); + + /*SetIIRSH Register */ + isp_reg_writel(isp_af->dev, isp_af->config.iir_config.hz_start_pos, + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH); + + base_coef_set0 = ISPH3A_AFCOEF010; + base_coef_set1 = ISPH3A_AFCOEF110; + for (index = 0; index <= 8; index += 2) { + /*Set IIR Filter0 Coefficients */ + coef = 0; + coef |= isp_af->config.iir_config.coeff_set0[index]; + coef |= isp_af->config.iir_config.coeff_set0[index + 1] << + AF_COEF_SHIFT; + isp_reg_writel(isp_af->dev, coef, OMAP3_ISP_IOMEM_H3A, + base_coef_set0); + base_coef_set0 += AFCOEF_OFFSET; + + /*Set IIR Filter1 Coefficients */ + coef = 0; + coef |= isp_af->config.iir_config.coeff_set1[index]; + coef |= isp_af->config.iir_config.coeff_set1[index + 1] << + AF_COEF_SHIFT; + isp_reg_writel(isp_af->dev, coef, OMAP3_ISP_IOMEM_H3A, + base_coef_set1); + base_coef_set1 += AFCOEF_OFFSET; + } + /* set AFCOEF0010 Register */ + isp_reg_writel(isp_af->dev, isp_af->config.iir_config.coeff_set0[10], + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010); + /* set AFCOEF1010 Register */ + isp_reg_writel(isp_af->dev, isp_af->config.iir_config.coeff_set1[10], + OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010); + + /* PCR Register */ + /* Set Accumulator Mode */ + if (isp_af->config.mode == ACCUMULATOR_PEAK) + pcr |= FVMODE; + /* Set A-law */ + if (isp_af->config.alaw_enable == H3A_AF_ALAW_ENABLE) + pcr |= AF_ALAW_EN; + /* Set RGB Position */ + pcr |= isp_af->config.rgb_pos << AF_RGBPOS_SHIFT; + /* HMF Configurations */ + if (isp_af->config.hmf_config.enable == H3A_AF_HMF_ENABLE) { + /* Enable HMF */ + pcr |= AF_MED_EN; + /* Set Median Threshold */ + pcr |= isp_af->config.hmf_config.threshold << AF_MED_TH_SHIFT; + } + /* Set PCR Register */ + isp_reg_and_or(isp_af->dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, + ~AF_PCR_MASK, pcr); + + isp_af->update = 0; + +out: + if (isp_af->pm_state) + __isp_af_enable(isp_af, 1); +} + +/* Update local parameters */ +static void isp_af_update_params(struct isp_af_device *isp_af, + struct af_configuration *afconfig) +{ + int update = 0; + int index; + + /* alaw */ + if (isp_af->config.alaw_enable != afconfig->alaw_enable) { + update = 1; + goto out; + } + + /* hmf */ + if (isp_af->config.hmf_config.enable != afconfig->hmf_config.enable) { + update = 1; + goto out; + } + if (isp_af->config.hmf_config.threshold != + afconfig->hmf_config.threshold) { + update = 1; + goto out; + } + + /* rgbpos */ + if (isp_af->config.rgb_pos != afconfig->rgb_pos) { + update = 1; + goto out; + } + + /* iir */ + if (isp_af->config.iir_config.hz_start_pos != + afconfig->iir_config.hz_start_pos) { + update = 1; + goto out; + } + for (index = 0; index < AF_NUMBER_OF_COEF; index++) { + if (isp_af->config.iir_config.coeff_set0[index] != + afconfig->iir_config.coeff_set0[index]) { + update = 1; + goto out; + } + if (isp_af->config.iir_config.coeff_set1[index] != + afconfig->iir_config.coeff_set1[index]) { + update = 1; + goto out; + } + } + + /* paxel */ + if ((isp_af->config.paxel_config.width != + afconfig->paxel_config.width) || + (isp_af->config.paxel_config.height != + afconfig->paxel_config.height) || + (isp_af->config.paxel_config.hz_start != + afconfig->paxel_config.hz_start) || + (isp_af->config.paxel_config.vt_start != + afconfig->paxel_config.vt_start) || + (isp_af->config.paxel_config.hz_cnt != + afconfig->paxel_config.hz_cnt) || + (isp_af->config.paxel_config.line_incr != + afconfig->paxel_config.line_incr)) { + update = 1; + goto out; + } + + /* af_mode */ + if (isp_af->config.mode != afconfig->mode) { + update = 1; + goto out; + } + + isp_af->config.af_config = afconfig->af_config; + +out: + if (update) { + memcpy(&isp_af->config, afconfig, sizeof(*afconfig)); + isp_af->update = 1; + } +} + +/* Function to perform hardware set up */ +int isp_af_configure(struct isp_af_device *isp_af, + struct af_configuration *afconfig) +{ + int result; + int buf_size; + struct ispstat_buffer *buf; + unsigned long irqflags; + + if (!afconfig) { + dev_err(isp_af->dev, "af: Null argument in configuration.\n"); + return -EINVAL; + } + + /* Check Parameters */ + result = isp_af_check_params(isp_af, afconfig); + if (result) { + dev_dbg(isp_af->dev, "af: wrong configure params received.\n"); + return result; + } + + /* Compute buffer size */ + buf_size = (afconfig->paxel_config.hz_cnt + 1) * + (afconfig->paxel_config.vt_cnt + 1) * AF_PAXEL_SIZE; + + result = ispstat_bufs_alloc(&isp_af->stat, buf_size); + if (result) + return result; + buf = ispstat_buf_next(&isp_af->stat); + + spin_lock_irqsave(isp_af->lock, irqflags); + isp_af_set_address(isp_af, buf->iommu_addr); + isp_af_update_params(isp_af, afconfig); + + if (!isp_af->pm_state) + isp_af_register_setup(isp_af); + + if (isp_af->config.af_config) { + __isp_af_enable(isp_af, 1); + isp_af->pm_state = 1; + } else { + __isp_af_enable(isp_af, 0); + isp_af->pm_state = 0; + } + spin_unlock_irqrestore(isp_af->lock, irqflags); + + /* Success */ + return 0; +} +EXPORT_SYMBOL(isp_af_configure); + +/* + * This API allows the user to update White Balance gains, as well as + * exposure time and analog gain. It is also used to request frame + * statistics. + */ +int isp_af_request_statistics(struct isp_af_device *isp_af, + struct isp_af_data *afdata) +{ + struct ispstat_buffer *buf; + + if (!isp_af->config.af_config) { + dev_err(isp_af->dev, "af: statistics requested while af engine" + " is not configured\n"); + return -EINVAL; + } + + if (afdata->update & REQUEST_STATISTICS) { + buf = ispstat_buf_get(&isp_af->stat, + (void *)afdata->af_statistics_buf, + afdata->frame_number); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + afdata->xtrastats.ts = buf->ts; + afdata->config_counter = buf->config_counter; + afdata->frame_number = buf->frame_number; + + ispstat_buf_release(&isp_af->stat); + } else + afdata->af_statistics_buf = NULL; + + afdata->curr_frame = isp_af->stat.frame_number; + + return 0; +} +EXPORT_SYMBOL(isp_af_request_statistics); + +/* This function will handle the H3A interrupt. */ +void isp_af_isr(struct isp_af_device *isp_af) +{ + struct ispstat_buffer *buf; + unsigned long irqflags; + + /* Exchange buffers */ + buf = ispstat_buf_next(&isp_af->stat); + + spin_lock_irqsave(isp_af->lock, irqflags); + + isp_af_set_address(isp_af, buf->iommu_addr); + isp_af_register_setup(isp_af); + + spin_unlock_irqrestore(isp_af->lock, irqflags); +} + +static void __isp_af_enable(struct isp_af_device *isp_af, int enable) +{ + unsigned int pcr; + + pcr = isp_reg_readl(isp_af->dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); + + /* Set AF_EN bit in PCR Register */ + if (enable) + pcr |= AF_EN; + else + pcr &= ~AF_EN; + + isp_reg_writel(isp_af->dev, pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); +} + +/* Function to Enable/Disable AF Engine */ +int isp_af_enable(struct isp_af_device *isp_af, int enable) +{ + int rval = 0; + unsigned long irqflags; + + spin_lock_irqsave(isp_af->lock, irqflags); + if (enable) { + if (!isp_af->config.af_config) { + rval = -EINVAL; + goto out; + } + if (isp_af->pm_state) + goto out; + isp_af_register_setup(isp_af); + __isp_af_enable(isp_af, 1); + isp_af->pm_state = 1; + } else { + if (!isp_af->pm_state) + goto out; + __isp_af_enable(isp_af, 0); + isp_af->pm_state = 0; + } + +out: + spin_unlock_irqrestore(isp_af->lock, irqflags); + + return rval; +} + +/* Function to Suspend AF Engine */ +void isp_af_suspend(struct isp_af_device *isp_af) +{ + unsigned long irqflags; + + spin_lock_irqsave(isp_af->lock, irqflags); + if (isp_af->pm_state) + __isp_af_enable(isp_af, 0); + spin_unlock_irqrestore(isp_af->lock, irqflags); +} + +/* Function to Resume AF Engine */ +void isp_af_resume(struct isp_af_device *isp_af) +{ + unsigned long irqflags; + + spin_lock_irqsave(isp_af->lock, irqflags); + if (isp_af->pm_state) { + isp_af->update = 1; + isp_af_register_setup(isp_af); + } + spin_unlock_irqrestore(isp_af->lock, irqflags); +} + +int isp_af_busy(struct isp_af_device *isp_af) +{ + return isp_reg_readl(isp_af->dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) + & ISPH3A_PCR_BUSYAF; +} + +/* Function to register the AF character device driver. */ +int __init isp_af_init(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_af_device *isp_af = &isp->isp_af; + + isp_af->dev = dev; + isp_af->lock = &isp->h3a_lock; + ispstat_init(dev, &isp_af->stat, H3A_MAX_BUFF, MAX_FRAME_COUNT); + + return 0; +} + +void isp_af_exit(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + /* Free buffers */ + ispstat_free(&isp->isp_af.stat); +} diff --git a/drivers/media/video/isp/isp_af.h b/drivers/media/video/isp/isp_af.h new file mode 100644 index 00000000000..860015a8595 --- /dev/null +++ b/drivers/media/video/isp/isp_af.h @@ -0,0 +1,134 @@ +/* + * isp_af.h + * + * Include file for AF module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * + * 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. + */ + +/* Device Constants */ +#ifndef OMAP_ISP_AF_H +#define OMAP_ISP_AF_H + +#include + +#include "isph3a.h" +#include "ispstat.h" + +#define AF_MAJOR_NUMBER 0 +#define ISPAF_NAME "OMAPISP_AF" +#define AF_NR_DEVS 1 +#define AF_TIMEOUT ((300 * HZ) / 1000) + + + +/* Print Macros */ +/*list of error code */ +#define AF_ERR_HZ_COUNT 800 /* Invalid Horizontal Count */ +#define AF_ERR_VT_COUNT 801 /* Invalid Vertical Count */ +#define AF_ERR_HEIGHT 802 /* Invalid Height */ +#define AF_ERR_WIDTH 803 /* Invalid width */ +#define AF_ERR_INCR 804 /* Invalid Increment */ +#define AF_ERR_HZ_START 805 /* Invalid horizontal Start */ +#define AF_ERR_VT_START 806 /* Invalud vertical Start */ +#define AF_ERR_IIRSH 807 /* Invalid IIRSH value */ +#define AF_ERR_IIR_COEF 808 /* Invalid Coefficient */ +#define AF_ERR_SETUP 809 /* Setup not done */ +#define AF_ERR_THRESHOLD 810 /* Invalid Threshold */ +#define AF_ERR_ENGINE_BUSY 811 /* Engine is busy */ + +#define AFPID 0x0 /* Peripheral Revision + * and Class Information + */ + +#define AFCOEF_OFFSET 0x00000004 /* COEFFICIENT BASE + * ADDRESS + */ + +/* + * PCR fields + */ +#define AF_BUSYAF (1 << 15) +#define FVMODE (1 << 14) +#define RGBPOS (0x7 << 11) +#define MED_TH (0xFF << 3) +#define AF_MED_EN (1 << 2) +#define AF_ALAW_EN (1 << 1) +#define AF_EN (1 << 0) +#define AF_PCR_MASK (FVMODE | RGBPOS | MED_TH | \ + AF_MED_EN | AF_ALAW_EN) + +/* + * AFPAX1 fields + */ +#define PAXW (0x7F << 16) +#define PAXH 0x7F + +/* + * AFPAX2 fields + */ +#define AFINCV (0xF << 13) +#define PAXVC (0x7F << 6) +#define PAXHC 0x3F + +/* + * AFPAXSTART fields + */ +#define PAXSH (0xFFF<<16) +#define PAXSV 0xFFF + +/* + * COEFFICIENT MASK + */ + +#define COEF_MASK0 0xFFF +#define COEF_MASK1 (0xFFF<<16) + +/* BIT SHIFTS */ +#define AF_RGBPOS_SHIFT 11 +#define AF_MED_TH_SHIFT 3 +#define AF_PAXW_SHIFT 16 +#define AF_LINE_INCR_SHIFT 13 +#define AF_VT_COUNT_SHIFT 6 +#define AF_HZ_START_SHIFT 16 +#define AF_COEF_SHIFT 16 + +#define AF_UPDATEXS_TS (1 << 0) +#define AF_UPDATEXS_FIELDCOUNT (1 << 1) +#define AF_UPDATEXS_LENSPOS (1 << 2) + +/** + * struct isp_af_status - AF status. + * @update: 1 - Update registers. + */ +struct isp_af_device { + u8 update; + int pm_state; + struct device *dev; + struct ispstat stat; + struct af_configuration config; /*Device configuration structure */ + spinlock_t *lock; +}; + +void isp_af_isr(struct isp_af_device *isp_af); +int isp_af_enable(struct isp_af_device *, int); +void isp_af_suspend(struct isp_af_device *); +void isp_af_resume(struct isp_af_device *); +int isp_af_busy(struct isp_af_device *); +int isp_af_request_statistics(struct isp_af_device *, + struct isp_af_data *afdata); +int isp_af_configure(struct isp_af_device *, struct af_configuration *afconfig); + +#endif /* OMAP_ISP_AF_H */ diff --git a/drivers/media/video/isp/ispccdc.c b/drivers/media/video/isp/ispccdc.c new file mode 100644 index 00000000000..ac8478b8859 --- /dev/null +++ b/drivers/media/video/isp/ispccdc.c @@ -0,0 +1,1821 @@ +/* + * ispccdc.c + * + * Driver Library for CCDC module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Senthilvadivu Guruswamy + * Pallavi Kulkarni + * Sergio Aguirre + * + * 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 +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispccdc.h" + +#define LSC_TABLE_INIT_SIZE 50052 + +/* Structure for saving/restoring CCDC module registers*/ +static struct isp_reg ispccdc_reg_list[] = { + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HD_VD_WID, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PIX_LINES, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CULLING, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC_ADDR, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR0, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR1, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR2, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR3, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR4, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR5, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR6, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR7, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN0, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN1, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD0, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD1, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_INITIAL, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE, 0}, + {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_OFFSET, 0}, + {0, ISP_TOK_TERM, 0} +}; + +/** + * omap34xx_isp_ccdc_config - Set CCDC configuration from userspace + * @isp_ccdc: Pointer to ISP CCDC device. + * @userspace_add: Structure containing CCDC configuration sent from userspace. + * + * Returns 0 if successful, -EINVAL if the pointer to the configuration + * structure is null, or the copy_from_user function fails to copy user space + * memory to kernel space memory. + **/ +int omap34xx_isp_ccdc_config(struct isp_ccdc_device *isp_ccdc, + void *userspace_add) +{ + struct isp_device *isp = + container_of(isp_ccdc, struct isp_device, isp_ccdc); + struct ispccdc_bclamp bclamp_t; + struct ispccdc_blcomp blcomp_t; + struct ispccdc_fpc fpc_t; + struct ispccdc_culling cull_t; + struct ispccdc_update_config *ccdc_struct; + + if (userspace_add == NULL) + return -EINVAL; + + ccdc_struct = userspace_add; + + if (ISP_ABS_CCDC_ALAW & ccdc_struct->flag) { + if (ISP_ABS_CCDC_ALAW & ccdc_struct->update) + ispccdc_config_alaw(isp_ccdc, ccdc_struct->alawip); + ispccdc_enable_alaw(isp_ccdc, 1); + } else if (ISP_ABS_CCDC_ALAW & ccdc_struct->update) + ispccdc_enable_alaw(isp_ccdc, 0); + + if (ISP_ABS_CCDC_LPF & ccdc_struct->flag) + ispccdc_enable_lpf(isp_ccdc, 1); + else + ispccdc_enable_lpf(isp_ccdc, 0); + + if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->flag) { + if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->update) { + if (copy_from_user(&bclamp_t, (struct ispccdc_bclamp *) + ccdc_struct->bclamp, + sizeof(struct ispccdc_bclamp))) + goto copy_from_user_err; + + ispccdc_enable_black_clamp(isp_ccdc, 1); + ispccdc_config_black_clamp(isp_ccdc, bclamp_t); + } else + ispccdc_enable_black_clamp(isp_ccdc, 1); + } else { + if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->update) { + if (copy_from_user(&bclamp_t, (struct ispccdc_bclamp *) + ccdc_struct->bclamp, + sizeof(struct ispccdc_bclamp))) + goto copy_from_user_err; + + ispccdc_enable_black_clamp(isp_ccdc, 0); + ispccdc_config_black_clamp(isp_ccdc, bclamp_t); + } + } + + if (ISP_ABS_CCDC_BCOMP & ccdc_struct->update) { + if (copy_from_user(&blcomp_t, (struct ispccdc_blcomp *) + ccdc_struct->blcomp, + sizeof(blcomp_t))) + goto copy_from_user_err; + + ispccdc_config_black_comp(isp_ccdc, blcomp_t); + } + + if (ISP_ABS_CCDC_FPC & ccdc_struct->flag) { + if (ISP_ABS_CCDC_FPC & ccdc_struct->update) { + if (copy_from_user(&fpc_t, (struct ispccdc_fpc *) + ccdc_struct->fpc, + sizeof(fpc_t))) + goto copy_from_user_err; + isp_ccdc->fpc_table_add = kmalloc(64 + fpc_t.fpnum * 4, + GFP_KERNEL | GFP_DMA); + if (!isp_ccdc->fpc_table_add) { + dev_err(isp_ccdc->dev, + "ccdc: Cannot allocate memory for" + " FPC table"); + return -ENOMEM; + } + while (((unsigned long)isp_ccdc->fpc_table_add + & 0xFFFFFFC0) + != (unsigned long)isp_ccdc->fpc_table_add) + isp_ccdc->fpc_table_add++; + + isp_ccdc->fpc_table_add_m = iommu_kmap( + isp->iommu, + 0, + virt_to_phys(isp_ccdc->fpc_table_add), + fpc_t.fpnum * 4, + IOMMU_FLAG); + /* FIXME: Correct unwinding */ + BUG_ON(IS_ERR_VALUE(isp_ccdc->fpc_table_add_m)); + + if (copy_from_user(isp_ccdc->fpc_table_add, + (u32 *)fpc_t.fpcaddr, + fpc_t.fpnum * 4)) + goto copy_from_user_err; + + fpc_t.fpcaddr = isp_ccdc->fpc_table_add_m; + ispccdc_config_fpc(isp_ccdc, fpc_t); + } + ispccdc_enable_fpc(isp_ccdc, 1); + } else if (ISP_ABS_CCDC_FPC & ccdc_struct->update) + ispccdc_enable_fpc(isp_ccdc, 0); + + if (ISP_ABS_CCDC_CULL & ccdc_struct->update) { + if (copy_from_user(&cull_t, (struct ispccdc_culling *) + ccdc_struct->cull, + sizeof(cull_t))) + goto copy_from_user_err; + ispccdc_config_culling(isp_ccdc, cull_t); + } + + if (is_isplsc_activated()) { + if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->flag) { + if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->update) { + if (copy_from_user( + &isp_ccdc->lsc_config, + (struct ispccdc_lsc_config *) + ccdc_struct->lsc_cfg, + sizeof(struct ispccdc_lsc_config))) + goto copy_from_user_err; + ispccdc_config_lsc(isp_ccdc, + &isp_ccdc->lsc_config); + } + ispccdc_enable_lsc(isp_ccdc, 1); + } else if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->update) { + ispccdc_enable_lsc(isp_ccdc, 0); + } + if (ISP_ABS_TBL_LSC & ccdc_struct->update) { + if (copy_from_user(isp_ccdc->lsc_gain_table, + ccdc_struct->lsc, + isp_ccdc->lsc_config.size)) + goto copy_from_user_err; + ispccdc_load_lsc(isp_ccdc, isp_ccdc->lsc_gain_table, + isp_ccdc->lsc_config.size); + } + } + + if (ISP_ABS_CCDC_COLPTN & ccdc_struct->update) + ispccdc_config_imgattr(isp_ccdc, ccdc_struct->colptn); + + return 0; + +copy_from_user_err: + dev_err(isp_ccdc->dev, "ccdc: Config: copy from user error"); + return -EINVAL ; +} +EXPORT_SYMBOL(omap34xx_isp_ccdc_config); + +/** + * ispccdc_set_wenlog - Set the CCDC Write Enable valid region. + * @isp_ccdc: Pointer to ISP CCDC device. + * @wenlog: Write enable logic to apply against valid area. 0 - AND, 1 - OR. + */ +void ispccdc_set_wenlog(struct isp_ccdc_device *isp_ccdc, u32 wenlog) +{ + isp_ccdc->wenlog = wenlog; +} +EXPORT_SYMBOL(ispccdc_set_wenlog); + +/** + * ispccdc_request - Reserve the CCDC module. + * @isp_ccdc: Pointer to ISP CCDC device. + * + * Returns 0 if successful, or -EBUSY if CCDC module is busy. + **/ +int ispccdc_request(struct isp_ccdc_device *isp_ccdc) +{ + mutex_lock(&isp_ccdc->mutexlock); + if (isp_ccdc->ccdc_inuse) { + mutex_unlock(&isp_ccdc->mutexlock); + DPRINTK_ISPCCDC("ISP_ERR : CCDC Module Busy\n"); + return -EBUSY; + } + + isp_ccdc->ccdc_inuse = 1; + mutex_unlock(&isp_ccdc->mutexlock); + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ISPCTRL_CCDC_RAM_EN | ISPCTRL_CCDC_CLK_EN | + ISPCTRL_SBL_WR1_RAM_EN); + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, + ISPCCDC_CFG_VDLC); + return 0; +} +EXPORT_SYMBOL(ispccdc_request); + +/** + * ispccdc_free - Free the CCDC module. + * @isp_ccdc: Pointer to ISP CCDC device. + * + * Frees the CCDC module so it can be used by another process. + * + * Returns 0 if successful, or -EINVAL if module has been already freed. + **/ +int ispccdc_free(struct isp_ccdc_device *isp_ccdc) +{ + mutex_lock(&isp_ccdc->mutexlock); + if (!isp_ccdc->ccdc_inuse) { + mutex_unlock(&isp_ccdc->mutexlock); + DPRINTK_ISPCCDC("ISP_ERR: CCDC Module already freed\n"); + return -EINVAL; + } + + isp_ccdc->ccdc_inuse = 0; + mutex_unlock(&isp_ccdc->mutexlock); + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ~(ISPCTRL_CCDC_CLK_EN | + ISPCTRL_CCDC_RAM_EN | + ISPCTRL_SBL_WR1_RAM_EN)); + return 0; +} +EXPORT_SYMBOL(ispccdc_free); + +/** + * ispccdc_free_lsc - Frees Lens Shading Compensation table + * @isp_ccdc: Pointer to ISP CCDC device. + * + * Always returns 0. + **/ +static int ispccdc_free_lsc(struct isp_ccdc_device *isp_ccdc) +{ + struct isp_device *isp = + container_of(isp_ccdc, struct isp_device, isp_ccdc); + + if (!isp_ccdc->lsc_ispmmu_addr) + return 0; + + ispccdc_enable_lsc(isp_ccdc, 0); + isp_ccdc->lsc_initialized = 0; + isp_reg_writel(isp_ccdc->dev, 0, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_TABLE_BASE); + iommu_kunmap(isp->iommu, isp_ccdc->lsc_ispmmu_addr); + kfree(isp_ccdc->lsc_gain_table); + return 0; +} + +/** + * ispccdc_allocate_lsc - Allocate space for Lens Shading Compensation table + * @isp_ccdc: Pointer to ISP CCDC device. + * @table_size: LSC gain table size. + * + * Returns 0 if successful, -ENOMEM of its no memory available, or -EINVAL if + * table_size is zero. + **/ +static int ispccdc_allocate_lsc(struct isp_ccdc_device *isp_ccdc, + u32 table_size) +{ + struct isp_device *isp = + container_of(isp_ccdc, struct isp_device, isp_ccdc); + + if (table_size == 0) + return -EINVAL; + + if ((isp_ccdc->lsc_config.size >= table_size) + && isp_ccdc->lsc_gain_table) + return 0; + + ispccdc_free_lsc(isp_ccdc); + + isp_ccdc->lsc_gain_table = kmalloc(table_size, GFP_KERNEL | GFP_DMA); + + if (!isp_ccdc->lsc_gain_table) { + dev_err(isp_ccdc->dev, + "ccdc: Cannot allocate memory for gain tables\n"); + return -ENOMEM; + } + + isp_ccdc->lsc_ispmmu_addr = + iommu_kmap(isp->iommu, + 0, + virt_to_phys(isp_ccdc->lsc_gain_table), + table_size, + IOMMU_FLAG); + if (IS_ERR_VALUE(isp_ccdc->lsc_ispmmu_addr)) { + dev_err(isp_ccdc->dev, + "ccdc: Cannot map memory for gain tables\n"); + kfree(isp_ccdc->lsc_gain_table); + return -ENOMEM; + } + + return 0; +} + +/** + * ispccdc_program_lsc - Program Lens Shading Compensation table. + * @isp_ccdc: Pointer to ISP CCDC device. + * + * Returns 0 if successful, or -EINVAL if there's no mapped address for the + * table yet. + **/ +static int ispccdc_program_lsc(struct isp_ccdc_device *isp_ccdc) +{ + if (!isp_ccdc->lsc_ispmmu_addr) + return -EINVAL; + + if (isp_ccdc->lsc_initialized) + return 0; + + isp_reg_writel(isp_ccdc->dev, isp_ccdc->lsc_ispmmu_addr, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); + isp_ccdc->lsc_initialized = 1; + return 0; +} + +/** + * ispccdc_load_lsc - Load Lens Shading Compensation table. + * @isp_ccdc: Pointer to ISP CCDC device. + * @table_addr: MMU Mapped address to LSC gain table. + * @table_size: LSC gain table size. + * + * Returns 0 if successful, -ENOMEM of its no memory available, or -EINVAL if + * table_size is zero. + **/ +int ispccdc_load_lsc(struct isp_ccdc_device *isp_ccdc, u8 *table_addr, + u32 table_size) +{ + int ret; + + if (!is_isplsc_activated()) + return 0; + + if (!table_addr) + return -EINVAL; + + ret = ispccdc_allocate_lsc(isp_ccdc, table_size); + if (ret) + return ret; + + if (table_addr != isp_ccdc->lsc_gain_table) + memcpy(isp_ccdc->lsc_gain_table, table_addr, table_size); + ret = ispccdc_program_lsc(isp_ccdc); + if (ret) + return ret; + return 0; +} +EXPORT_SYMBOL(ispccdc_load_lsc); + +/** + * ispccdc_config_lsc - Configures the lens shading compensation module + * @isp_ccdc: Pointer to ISP CCDC device. + * @lsc_cfg: Pointer to LSC configuration structure + **/ +void ispccdc_config_lsc(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_lsc_config *lsc_cfg) +{ + int reg; + + if (!is_isplsc_activated()) + return; + + ispccdc_enable_lsc(isp_ccdc, 0); + isp_reg_writel(isp_ccdc->dev, lsc_cfg->offset, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_TABLE_OFFSET); + + reg = 0; + reg |= lsc_cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; + reg |= lsc_cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; + reg |= lsc_cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; + isp_reg_writel(isp_ccdc->dev, reg, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_CONFIG); + + reg = 0; + reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; + reg |= lsc_cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; + reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; + reg |= lsc_cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; + isp_reg_writel(isp_ccdc->dev, reg, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_INITIAL); +} +EXPORT_SYMBOL(ispccdc_config_lsc); + +/** + * ispccdc_enable_lsc - Enables/Disables the Lens Shading Compensation module. + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables LSC, 1 Enables LSC. + **/ +void ispccdc_enable_lsc(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + if (!is_isplsc_activated()) + return; + + if (enable) { + if (!ispccdc_busy(isp_ccdc)) { + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL, ISPCTRL_SBL_SHARED_RPORTB + | ISPCTRL_SBL_RD_RAM_EN); + + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_CONFIG, 0x1); + + isp_ccdc->lsc_state = 1; + } else { + /* Postpone enabling LSC */ + isp_ccdc->lsc_enable = 1; + } + } else { + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_CONFIG, 0xFFFE); + isp_ccdc->lsc_state = 0; + isp_ccdc->lsc_enable = 0; + } +} +EXPORT_SYMBOL(ispccdc_enable_lsc); + +/** + * ispccdc_lsc_error_handler - Handle LSC prefetch error scenario. + * @isp_ccdc: Pointer to ISP CCDC device. + * + * Disables LSC, and defers enablement to shadow registers update time. + **/ +void ispccdc_lsc_error_handler(struct isp_ccdc_device *isp_ccdc) +{ + int lsc_enable = isp_ccdc->lsc_state; + + ispccdc_enable_lsc(isp_ccdc, 0); + + isp_ccdc->lsc_enable = lsc_enable; +} + +/** + * ispccdc_config_crop - Configures crop parameters for the ISP CCDC. + * @isp_ccdc: Pointer to ISP CCDC device. + * @left: Left offset of the crop area. + * @top: Top offset of the crop area. + * @height: Height of the crop area. + * @width: Width of the crop area. + * + * The following restrictions are applied for the crop settings. If incoming + * values do not follow these restrictions then we map the settings to the + * closest acceptable crop value. + * 1) Left offset is always odd. This can be avoided if we enable byte swap + * option for incoming data into CCDC. + * 2) Top offset is always even. + * 3) Crop height is always even. + * 4) Crop width is always a multiple of 16 pixels + **/ +void ispccdc_config_crop(struct isp_ccdc_device *isp_ccdc, u32 left, u32 top, + u32 height, u32 width) +{ + isp_ccdc->ccdcin_woffset = left + (left % 2); + isp_ccdc->ccdcin_hoffset = top + (top % 2); + + isp_ccdc->crop_w = width - (width % 16); + isp_ccdc->crop_h = height + (height % 2); + + DPRINTK_ISPCCDC("\n\tOffsets L %d T %d W %d H %d\n", + isp_ccdc->ccdcin_woffset, + isp_ccdc->ccdcin_hoffset, + isp_ccdc->crop_w, + isp_ccdc->crop_h); +} + +/** + * ispccdc_config_datapath - Specify the input and output modules for CCDC. + * @isp_ccdc: Pointer to ISP CCDC device. + * @pipe: Pointer to ISP pipeline structure to base on for config. + * + * Configures the default configuration for the CCDC to work with. + * + * The valid values for the input are CCDC_RAW (0), CCDC_YUV_SYNC (1), + * CCDC_YUV_BT (2), and CCDC_OTHERS (3). + * + * The valid values for the output are CCDC_YUV_RSZ (0), CCDC_YUV_MEM_RSZ (1), + * CCDC_OTHERS_VP (2), CCDC_OTHERS_MEM (3), CCDC_OTHERS_VP_MEM (4). + * + * Returns 0 if successful, or -EINVAL if wrong I/O combination or wrong input + * or output values. + **/ +static int ispccdc_config_datapath(struct isp_ccdc_device *isp_ccdc, + struct isp_pipeline *pipe) +{ + u32 syn_mode = 0; + struct ispccdc_vp vpcfg; + struct ispccdc_syncif syncif; + struct ispccdc_bclamp blkcfg; + + u32 colptn = ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | + ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | + ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | + ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | + ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; + + syn_mode = isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE); + + switch (pipe->ccdc_out) { + case CCDC_YUV_RSZ: + syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; + syn_mode &= ~ISPCCDC_SYN_MODE_WEN; + break; + + case CCDC_YUV_MEM_RSZ: + syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; + isp_ccdc->wen = 1; + syn_mode |= ISPCCDC_SYN_MODE_WEN; + break; + + case CCDC_OTHERS_VP: + syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; + syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; + syn_mode &= ~ISPCCDC_SYN_MODE_WEN; + vpcfg.bitshift_sel = BIT9_0; + vpcfg.freq_sel = PIXCLKBY2; + ispccdc_config_vp(isp_ccdc, vpcfg); + ispccdc_enable_vp(isp_ccdc, 1); + break; + + case CCDC_OTHERS_MEM: + syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; + syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; + syn_mode |= ISPCCDC_SYN_MODE_WEN; + if (pipe->ccdc_in == CCDC_YUV_BT) { + syn_mode &= ~ISPCCDC_SYN_MODE_EXWEN; + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CFG, ~ISPCCDC_CFG_WENLOG); + } else { + syn_mode |= ISPCCDC_SYN_MODE_EXWEN; + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CFG, ISPCCDC_CFG_WENLOG); + } + vpcfg.bitshift_sel = BIT11_2; + vpcfg.freq_sel = PIXCLKBY2; + ispccdc_config_vp(isp_ccdc, vpcfg); + ispccdc_enable_vp(isp_ccdc, 0); + break; + + case CCDC_OTHERS_VP_MEM: + syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; + syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; + syn_mode |= ISPCCDC_SYN_MODE_WEN; + syn_mode &= ~ISPCCDC_SYN_MODE_EXWEN; + + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CFG, ~ISPCCDC_CFG_WENLOG, + isp_ccdc->wenlog); + vpcfg.bitshift_sel = BIT9_0; + vpcfg.freq_sel = PIXCLKBY2; + ispccdc_config_vp(isp_ccdc, vpcfg); + ispccdc_enable_vp(isp_ccdc, 1); + break; + default: + DPRINTK_ISPCCDC("ISP_ERR: Wrong CCDC Output\n"); + return -EINVAL; + }; + + isp_reg_writel(isp_ccdc->dev, syn_mode, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE); + + switch (pipe->ccdc_in) { + case CCDC_RAW_GRBG: + case CCDC_RAW_RGGB: + case CCDC_RAW_BGGR: + case CCDC_RAW_GBRG: + syncif.ccdc_mastermode = 0; + syncif.datapol = 0; + syncif.datsz = DAT10; + syncif.fldmode = 0; + syncif.fldout = 0; + syncif.fldpol = 0; + syncif.fldstat = 0; + syncif.hdpol = 0; + syncif.ipmod = RAW; + syncif.vdpol = 0; + syncif.bt_r656_en = 0; + ispccdc_config_sync_if(isp_ccdc, syncif); + ispccdc_config_imgattr(isp_ccdc, colptn); + blkcfg.dcsubval = 64; + ispccdc_config_black_clamp(isp_ccdc, blkcfg); + if (is_isplsc_activated()) { + ispccdc_config_lsc(isp_ccdc, &isp_ccdc->lsc_config); + ispccdc_load_lsc(isp_ccdc, isp_ccdc->lsc_gain_table_tmp, + LSC_TABLE_INIT_SIZE); + } + + break; + case CCDC_YUV_SYNC: + syncif.ccdc_mastermode = 0; + syncif.datapol = 0; + syncif.datsz = DAT8; + syncif.fldmode = 0; + syncif.fldout = 0; + syncif.fldpol = 0; + syncif.fldstat = 0; + syncif.hdpol = 0; + syncif.ipmod = YUV16; + syncif.vdpol = 1; + syncif.bt_r656_en = 0; + ispccdc_config_imgattr(isp_ccdc, 0); + ispccdc_config_sync_if(isp_ccdc, syncif); + blkcfg.dcsubval = 0; + ispccdc_config_black_clamp(isp_ccdc, blkcfg); + break; + case CCDC_YUV_BT: + syncif.ccdc_mastermode = 0; + syncif.datapol = 0; + syncif.datsz = DAT8; + syncif.fldmode = 1; + syncif.fldout = 0; + syncif.fldpol = 0; + syncif.fldstat = 0; + syncif.hdpol = 0; + syncif.ipmod = YUV8; + syncif.vdpol = 1; + syncif.bt_r656_en = 1; + ispccdc_config_imgattr(isp_ccdc, 0); + ispccdc_config_sync_if(isp_ccdc, syncif); + blkcfg.dcsubval = 0; + ispccdc_config_black_clamp(isp_ccdc, blkcfg); + break; + case CCDC_OTHERS: + break; + default: + DPRINTK_ISPCCDC("ISP_ERR: Wrong CCDC Input\n"); + return -EINVAL; + } + + ispccdc_print_status(isp_ccdc, pipe); + isp_print_status(isp_ccdc->dev); + return 0; +} +EXPORT_SYMBOL(ispccdc_config_datapath); + +/** + * ispccdc_config_sync_if - Set CCDC sync interface params between sensor and CCDC. + * @isp_ccdc: Pointer to ISP CCDC device. + * @syncif: Structure containing the sync parameters like field state, CCDC in + * master/slave mode, raw/yuv data, polarity of data, field, hs, vs + * signals. + **/ +void ispccdc_config_sync_if(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_syncif syncif) +{ + u32 syn_mode = isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE); + + syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; + + if (syncif.fldstat) + syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; + else + syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; + + syn_mode &= ISPCCDC_SYN_MODE_INPMOD_MASK; + isp_ccdc->syncif_ipmod = syncif.ipmod; + + switch (syncif.ipmod) { + case RAW: + break; + case YUV16: + syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16; + break; + case YUV8: + syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8; + if (syncif.bt_r656_en) + syn_mode |= ISPCCDC_SYN_MODE_PACK8; + break; + }; + + syn_mode &= ISPCCDC_SYN_MODE_DATSIZ_MASK; + switch (syncif.datsz) { + case DAT8: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; + break; + case DAT10: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; + break; + case DAT11: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; + break; + case DAT12: + syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; + break; + }; + + if (syncif.fldmode) + syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; + else + syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; + + if (syncif.datapol) + syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; + + if (syncif.fldpol) + syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; + + if (syncif.hdpol) + syn_mode |= ISPCCDC_SYN_MODE_HDPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; + + if (syncif.vdpol) + syn_mode |= ISPCCDC_SYN_MODE_VDPOL; + else + syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; + + if (syncif.ccdc_mastermode) { + syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; + isp_reg_writel(isp_ccdc->dev, + syncif.hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT + | syncif.vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HD_VD_WID); + + isp_reg_writel(isp_ccdc->dev, + syncif.ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT + | syncif.hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_PIX_LINES); + } else + syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | + ISPCCDC_SYN_MODE_VDHDOUT); + + isp_reg_writel(isp_ccdc->dev, syn_mode, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE); + + if (!(syncif.bt_r656_en)) { + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_REC656IF, ~ISPCCDC_REC656IF_R656ON); + } else { + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_REC656IF, ISPCCDC_REC656IF_R656ON | + ISPCCDC_REC656IF_ECCFVH); + } +} +EXPORT_SYMBOL(ispccdc_config_sync_if); + +/** + * ispccdc_config_black_clamp - Configures the clamp parameters in CCDC. + * @isp_ccdc: Pointer to ISP CCDC device. + * @bclamp: Structure containing the optical black average gain, optical black + * sample length, sample lines, and the start pixel position of the + * samples w.r.t the HS pulse. + * + * Configures the clamp parameters in CCDC. Either if its being used the + * optical black clamp, or the digital clamp. If its a digital clamp, then + * assures to put a valid DC substraction level. + * + * Returns always 0 when completed. + **/ +int ispccdc_config_black_clamp(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_bclamp bclamp) +{ + u32 bclamp_val = 0; + + if (isp_ccdc->obclamp_en) { + bclamp_val |= bclamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; + bclamp_val |= bclamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; + bclamp_val |= bclamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; + bclamp_val |= bclamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; + isp_reg_writel(isp_ccdc->dev, bclamp_val, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP); + } else { + if (omap_rev() < OMAP3430_REV_ES2_0) + if (isp_ccdc->syncif_ipmod == YUV16 || + isp_ccdc->syncif_ipmod == YUV8 || + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_REC656IF) & + ISPCCDC_REC656IF_R656ON) + bclamp.dcsubval = 0; + isp_reg_writel(isp_ccdc->dev, bclamp.dcsubval, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB); + } + return 0; +} +EXPORT_SYMBOL(ispccdc_config_black_clamp); + +/** + * ispccdc_enable_black_clamp - Enables/Disables the optical black clamp. + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables optical black clamp, 1 Enables optical black clamp. + * + * Enables or disables the optical black clamp. When disabled, the digital + * clamp operates. + **/ +void ispccdc_enable_black_clamp(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, + ~ISPCCDC_CLAMP_CLAMPEN, + enable ? ISPCCDC_CLAMP_CLAMPEN : 0); + isp_ccdc->obclamp_en = enable; +} +EXPORT_SYMBOL(ispccdc_enable_black_clamp); + +/** + * ispccdc_config_fpc - Configures the Faulty Pixel Correction parameters. + * @isp_ccdc: Pointer to ISP CCDC device. + * @fpc: Structure containing the number of faulty pixels corrected in the + * frame, address of the FPC table. + * + * Returns 0 if successful, or -EINVAL if FPC Address is not on the 64 byte + * boundary. + **/ +int ispccdc_config_fpc(struct isp_ccdc_device *isp_ccdc, struct ispccdc_fpc fpc) +{ + u32 fpc_val = 0; + + fpc_val = isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FPC); + + if ((fpc.fpcaddr & 0xFFFFFFC0) == fpc.fpcaddr) { + isp_reg_writel(isp_ccdc->dev, fpc_val & (~ISPCCDC_FPC_FPCEN), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); + isp_reg_writel(isp_ccdc->dev, fpc.fpcaddr, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC_ADDR); + } else { + DPRINTK_ISPCCDC("FPC Address should be on 64byte boundary\n"); + return -EINVAL; + } + isp_reg_writel(isp_ccdc->dev, fpc_val | + (fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); + return 0; +} +EXPORT_SYMBOL(ispccdc_config_fpc); + +/** + * ispccdc_enable_fpc - Enable Faulty Pixel Correction. + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables FPC, 1 Enables FPC. + **/ +void ispccdc_enable_fpc(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, + ~ISPCCDC_FPC_FPCEN, + enable ? ISPCCDC_FPC_FPCEN : 0); +} +EXPORT_SYMBOL(ispccdc_enable_fpc); + +/** + * ispccdc_config_black_comp - Configure Black Level Compensation. + * @isp_ccdc: Pointer to ISP CCDC device. + * @blcomp: Structure containing the black level compensation value for RGrGbB + * pixels. in 2's complement. + **/ +void ispccdc_config_black_comp(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_blcomp blcomp) +{ + u32 blcomp_val = 0; + + blcomp_val |= blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; + blcomp_val |= blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; + blcomp_val |= blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; + blcomp_val |= blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; + + isp_reg_writel(isp_ccdc->dev, blcomp_val, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_BLKCMP); +} +EXPORT_SYMBOL(ispccdc_config_black_comp); + +/** + * ispccdc_config_vp - Configure the Video Port. + * @isp_ccdc: Pointer to ISP CCDC device. + * @vpcfg: Structure containing the Video Port input frequency, and the 10 bit + * format. + **/ +void ispccdc_config_vp(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_vp vpcfg) +{ + u32 fmtcfg_vp = isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMTCFG); + + fmtcfg_vp &= ISPCCDC_FMTCFG_VPIN_MASK & ISPCCDC_FMTCFG_VPIF_FRQ_MASK; + + switch (vpcfg.bitshift_sel) { + case BIT9_0: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; + break; + case BIT10_1: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; + break; + case BIT11_2: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; + break; + case BIT12_3: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; + break; + }; + switch (vpcfg.freq_sel) { + case PIXCLKBY2: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY2; + break; + case PIXCLKBY3_5: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY3; + break; + case PIXCLKBY4_5: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY4; + break; + case PIXCLKBY5_5: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY5; + break; + case PIXCLKBY6_5: + fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY6; + break; + }; + isp_reg_writel(isp_ccdc->dev, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMTCFG); +} +EXPORT_SYMBOL(ispccdc_config_vp); + +/** + * ispccdc_enable_vp - Enable Video Port. + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables VP, 1 Enables VP + * + * This is needed for outputting image to Preview, H3A and HIST ISP submodules. + **/ +void ispccdc_enable_vp(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, + ~ISPCCDC_FMTCFG_VPEN, + enable ? ISPCCDC_FMTCFG_VPEN : 0); +} +EXPORT_SYMBOL(ispccdc_enable_vp); + +/** + * ispccdc_config_reformatter - Configure Data Reformatter. + * @isp_ccdc: Pointer to ISP CCDC device. + * @refmt: Structure containing the memory address to format and the bit fields + * for the reformatter registers. + * + * Configures the Reformatter register values if line alternating is disabled. + * Else, just enabling line alternating is enough. + **/ +void ispccdc_config_reformatter(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_refmt refmt) +{ + u32 fmtcfg_val = 0; + + fmtcfg_val = isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMTCFG); + + if (refmt.lnalt) + fmtcfg_val |= ISPCCDC_FMTCFG_LNALT; + else { + fmtcfg_val &= ~ISPCCDC_FMTCFG_LNALT; + fmtcfg_val &= 0xFFFFF003; + fmtcfg_val |= refmt.lnum << ISPCCDC_FMTCFG_LNUM_SHIFT; + fmtcfg_val |= refmt.plen_even << + ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT; + fmtcfg_val |= refmt.plen_odd << ISPCCDC_FMTCFG_PLEN_ODD_SHIFT; + + isp_reg_writel(isp_ccdc->dev, refmt.prgeven0, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN0); + isp_reg_writel(isp_ccdc->dev, refmt.prgeven1, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN1); + isp_reg_writel(isp_ccdc->dev, refmt.prgodd0, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD0); + isp_reg_writel(isp_ccdc->dev, refmt.prgodd1, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD1); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr0, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR0); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr1, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR1); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr2, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR2); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr3, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR3); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr4, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR4); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr5, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR5); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr6, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR6); + isp_reg_writel(isp_ccdc->dev, refmt.fmtaddr7, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR7); + } + isp_reg_writel(isp_ccdc->dev, fmtcfg_val, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMTCFG); +} +EXPORT_SYMBOL(ispccdc_config_reformatter); + +/** + * ispccdc_enable_reformatter - Enable Data Reformatter. + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables Reformatter, 1- Enables Data Reformatter + **/ +void ispccdc_enable_reformatter(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, + ~ISPCCDC_FMTCFG_FMTEN, + enable ? ISPCCDC_FMTCFG_FMTEN : 0); + isp_ccdc->refmt_en = enable; +} +EXPORT_SYMBOL(ispccdc_enable_reformatter); + +/** + * ispccdc_config_culling - Configure culling parameters. + * @isp_ccdc: Pointer to ISP CCDC device. + * @cull: Structure containing the vertical culling pattern, and horizontal + * culling pattern for odd and even lines. + **/ +void ispccdc_config_culling(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_culling cull) +{ + u32 culling_val = 0; + + culling_val |= cull.v_pattern << ISPCCDC_CULLING_CULV_SHIFT; + culling_val |= cull.h_even << ISPCCDC_CULLING_CULHEVN_SHIFT; + culling_val |= cull.h_odd << ISPCCDC_CULLING_CULHODD_SHIFT; + + isp_reg_writel(isp_ccdc->dev, culling_val, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CULLING); +} +EXPORT_SYMBOL(ispccdc_config_culling); + +/** + * ispccdc_enable_lpf - Enable Low-Pass Filter (LPF). + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables LPF, 1 Enables LPF + **/ +void ispccdc_enable_lpf(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, + ~ISPCCDC_SYN_MODE_LPF, + enable ? ISPCCDC_SYN_MODE_LPF : 0); +} +EXPORT_SYMBOL(ispccdc_enable_lpf); + +/** + * ispccdc_config_alaw - Configure the input width for A-law compression. + * @isp_ccdc: Pointer to ISP CCDC device. + * @ipwidth: Input width for A-law + **/ +void ispccdc_config_alaw(struct isp_ccdc_device *isp_ccdc, + enum alaw_ipwidth ipwidth) +{ + isp_reg_writel(isp_ccdc->dev, ipwidth << ISPCCDC_ALAW_GWDI_SHIFT, + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); +} +EXPORT_SYMBOL(ispccdc_config_alaw); + +/** + * ispccdc_enable_alaw - Enable A-law compression. + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 - Disables A-law, 1 - Enables A-law + **/ +void ispccdc_enable_alaw(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW, + ~ISPCCDC_ALAW_CCDTBL, + enable ? ISPCCDC_ALAW_CCDTBL : 0); +} +EXPORT_SYMBOL(ispccdc_enable_alaw); + +/** + * ispccdc_config_imgattr - Configure sensor image specific attributes. + * @isp_ccdc: Pointer to ISP CCDC device. + * @colptn: Color pattern of the sensor. + **/ +void ispccdc_config_imgattr(struct isp_ccdc_device *isp_ccdc, u32 colptn) +{ + isp_reg_writel(isp_ccdc->dev, colptn, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_COLPTN); +} +EXPORT_SYMBOL(ispccdc_config_imgattr); + +/** + * ispccdc_config_shadow_registers - Configure CCDC during interframe time. + * @isp_ccdc: Pointer to ISP CCDC device. + * + * Executes LSC deferred enablement before next frame starts. + **/ +void ispccdc_config_shadow_registers(struct isp_ccdc_device *isp_ccdc) +{ + if (isp_ccdc->lsc_enable) { + ispccdc_enable_lsc(isp_ccdc, 1); + isp_ccdc->lsc_enable = 0; + } +} + +/** + * ispccdc_try_pipeline - Checks if requested Input/output dimensions are valid + * @isp_ccdc: Pointer to ISP CCDC device. + * @pipe: Pointer to ISP pipeline structure to fill back. + * + * Calculates the number of pixels cropped if the reformater is disabled, + * Fills up the output width and height variables in the isp_ccdc structure. + * + * Returns 0 if successful, or -EINVAL if the input width is less than 2 pixels + **/ +int ispccdc_try_pipeline(struct isp_ccdc_device *isp_ccdc, + struct isp_pipeline *pipe) +{ + struct isp_device *isp = + container_of(isp_ccdc, struct isp_device, isp_ccdc); + + if (pipe->ccdc_in_w < 32 || pipe->ccdc_in_h < 32) { + DPRINTK_ISPCCDC("ISP_ERR: CCDC cannot handle input width less" + " than 32 pixels or height less than 32\n"); + return -EINVAL; + } + + /* CCDC does not convert the image format */ + if ((pipe->ccdc_in == CCDC_RAW_GRBG || + pipe->ccdc_in == CCDC_RAW_RGGB || + pipe->ccdc_in == CCDC_RAW_BGGR || + pipe->ccdc_in == CCDC_RAW_GBRG || + pipe->ccdc_in == CCDC_OTHERS) && + pipe->ccdc_out == CCDC_YUV_RSZ) { + dev_info(isp->dev, "wrong CCDC I/O Combination\n"); + return -EINVAL; + } + + pipe->ccdc_in_h_st = 0; + pipe->ccdc_in_v_st = 0; + pipe->ccdc_out_w = pipe->ccdc_in_w; + pipe->ccdc_out_h = pipe->ccdc_in_h; + + if (!isp_ccdc->refmt_en + && pipe->ccdc_out != CCDC_OTHERS_MEM + && pipe->ccdc_out != CCDC_OTHERS_VP_MEM) + pipe->ccdc_out_h -= 1; + + if (pipe->ccdc_out == CCDC_OTHERS_VP) { + switch (pipe->ccdc_in) { + case CCDC_RAW_GRBG: + pipe->ccdc_in_h_st = 1; + pipe->ccdc_in_v_st = 0; + break; + case CCDC_RAW_BGGR: + pipe->ccdc_in_h_st = 1; + pipe->ccdc_in_v_st = 1; + break; + case CCDC_RAW_RGGB: + pipe->ccdc_in_h_st = 0; + pipe->ccdc_in_v_st = 0; + break; + case CCDC_RAW_GBRG: + pipe->ccdc_in_h_st = 0; + pipe->ccdc_in_v_st = 1; + break; + default: + break; + } + pipe->ccdc_out_h -= pipe->ccdc_in_v_st; + pipe->ccdc_out_w -= pipe->ccdc_in_h_st; + pipe->ccdc_out_h -= (pipe->ccdc_out_h % 2); + pipe->ccdc_out_w -= (pipe->ccdc_out_w % 2); + } + + pipe->ccdc_out_w_img = pipe->ccdc_out_w; + /* Round up to nearest 16 pixels. */ + pipe->ccdc_out_w = ALIGN(pipe->ccdc_out_w, 0x10); + + return 0; +} +EXPORT_SYMBOL(ispccdc_try_pipeline); + +/** + * ispccdc_s_pipeline - Configure the CCDC based on overall ISP pipeline. + * @isp_ccdc: Pointer to ISP CCDC device. + * @pipe: Pointer to ISP pipeline structure to configure. + * + * Configures the appropriate values stored in the isp_ccdc structure to + * HORZ/VERT_INFO registers and the VP_OUT depending on whether the image + * is stored in memory or given to the another module in the ISP pipeline. + * + * Returns 0 if successful, or -EINVAL if try_size was not called before to + * validate the requested dimensions. + **/ +int ispccdc_s_pipeline(struct isp_ccdc_device *isp_ccdc, + struct isp_pipeline *pipe) +{ + int rval; + + rval = ispccdc_config_datapath(isp_ccdc, pipe); + if (rval) + return rval; + + if (pipe->ccdc_out == CCDC_OTHERS_VP) { + isp_reg_writel(isp_ccdc->dev, (pipe->ccdc_in_h_st << + ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | + ((pipe->ccdc_in_w - pipe->ccdc_in_h_st) << + ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMT_HORZ); + isp_reg_writel(isp_ccdc->dev, (pipe->ccdc_in_v_st << + ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | + ((pipe->ccdc_in_h - pipe->ccdc_in_v_st) << + ISPCCDC_FMT_VERT_FMTLNV_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMT_VERT); + isp_reg_writel(isp_ccdc->dev, (pipe->ccdc_out_w << + ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | + (pipe->ccdc_out_h - 1) << + ISPCCDC_VP_OUT_VERT_NUM_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VP_OUT); + isp_reg_writel(isp_ccdc->dev, (((pipe->ccdc_out_h - 25) & + ISPCCDC_VDINT_0_MASK) << + ISPCCDC_VDINT_0_SHIFT) | + ((50 & ISPCCDC_VDINT_1_MASK) << + ISPCCDC_VDINT_1_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VDINT); + + } else if (pipe->ccdc_out == CCDC_OTHERS_MEM) { + isp_reg_writel(isp_ccdc->dev, 0, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VP_OUT); + if (pipe->ccdc_in == CCDC_RAW_GRBG || + pipe->ccdc_in == CCDC_RAW_RGGB || + pipe->ccdc_in == CCDC_RAW_BGGR || + pipe->ccdc_in == CCDC_RAW_GBRG) { + isp_reg_writel(isp_ccdc->dev, + pipe->ccdc_in_h_st << ISPCCDC_HORZ_INFO_SPH_SHIFT + | ((pipe->ccdc_out_w - 1) + << ISPCCDC_HORZ_INFO_NPH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HORZ_INFO); + } else if (pipe->ccdc_in == CCDC_YUV_BT) { + isp_reg_writel(isp_ccdc->dev, + 0 << ISPCCDC_HORZ_INFO_SPH_SHIFT | + (((pipe->ccdc_out_w << 1) - 1) << + ISPCCDC_HORZ_INFO_NPH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HORZ_INFO); + isp_reg_writel(isp_ccdc->dev, + 2 << ISPCCDC_VERT_START_SLV0_SHIFT | + 2 << ISPCCDC_VERT_START_SLV1_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_START); + isp_reg_writel(isp_ccdc->dev, + ((pipe->ccdc_out_h >> 1) - 1) << + ISPCCDC_VERT_LINES_NLV_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_LINES); + } else { + isp_reg_writel(isp_ccdc->dev, + 0 << ISPCCDC_HORZ_INFO_SPH_SHIFT + | ((pipe->ccdc_out_w - 1) + << ISPCCDC_HORZ_INFO_NPH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HORZ_INFO); + } + ispccdc_config_outlineoffset(isp_ccdc, pipe->ccdc_out_w * 2, + 0, 0); + if (pipe->ccdc_in != CCDC_YUV_BT) { + isp_reg_writel(isp_ccdc->dev, + 0 << ISPCCDC_VERT_START_SLV0_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_START); + isp_reg_writel(isp_ccdc->dev, (pipe->ccdc_out_h - 1) << + ISPCCDC_VERT_LINES_NLV_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_LINES); + isp_reg_writel(isp_ccdc->dev, (((pipe->ccdc_out_h - 2) & + ISPCCDC_VDINT_0_MASK) << + ISPCCDC_VDINT_0_SHIFT) | + ((100 & ISPCCDC_VDINT_1_MASK) << + ISPCCDC_VDINT_1_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VDINT); + } else { + ispccdc_config_outlineoffset(isp_ccdc, + pipe->ccdc_out_w * 2, EVENEVEN, 1); + ispccdc_config_outlineoffset(isp_ccdc, + pipe->ccdc_out_w * 2, ODDEVEN, 1); + ispccdc_config_outlineoffset(isp_ccdc, + pipe->ccdc_out_w * 2, EVENODD, 1); + ispccdc_config_outlineoffset(isp_ccdc, + pipe->ccdc_out_w * 2, ODDODD, 1); + isp_reg_writel(isp_ccdc->dev, + ((((pipe->ccdc_out_h >> 1) - 1) & + ISPCCDC_VDINT_0_MASK) << + ISPCCDC_VDINT_0_SHIFT) | + ((50 & ISPCCDC_VDINT_1_MASK) << + ISPCCDC_VDINT_1_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VDINT); + } + + } else if (pipe->ccdc_out == CCDC_OTHERS_VP_MEM) { + isp_reg_writel(isp_ccdc->dev, + (pipe->ccdc_in_h_st << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | + ((pipe->ccdc_in_w - pipe->ccdc_in_h_st) << + ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMT_HORZ); + isp_reg_writel(isp_ccdc->dev, + (pipe->ccdc_in_v_st << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | + ((pipe->ccdc_in_h - pipe->ccdc_in_v_st) << + ISPCCDC_FMT_VERT_FMTLNV_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMT_VERT); + isp_reg_writel(isp_ccdc->dev, (pipe->ccdc_out_w + << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | + ((pipe->ccdc_out_h - 1) << + ISPCCDC_VP_OUT_VERT_NUM_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VP_OUT); + isp_reg_writel(isp_ccdc->dev, + pipe->ccdc_in_h_st << ISPCCDC_HORZ_INFO_SPH_SHIFT | + ((pipe->ccdc_out_w - 1) << + ISPCCDC_HORZ_INFO_NPH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HORZ_INFO); + isp_reg_writel(isp_ccdc->dev, + pipe->ccdc_in_v_st << ISPCCDC_VERT_START_SLV0_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_START); + isp_reg_writel(isp_ccdc->dev, (pipe->ccdc_out_h - 1) << + ISPCCDC_VERT_LINES_NLV_SHIFT, + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_LINES); + ispccdc_config_outlineoffset(isp_ccdc, pipe->ccdc_out_w * 2, + 0, 0); + isp_reg_writel(isp_ccdc->dev, (((pipe->ccdc_out_h - 2) & + ISPCCDC_VDINT_0_MASK) << + ISPCCDC_VDINT_0_SHIFT) | + ((100 & ISPCCDC_VDINT_1_MASK) << + ISPCCDC_VDINT_1_SHIFT), + OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VDINT); + } + + if (is_isplsc_activated()) { + if (pipe->ccdc_in == CCDC_RAW_GRBG || + pipe->ccdc_in == CCDC_RAW_RGGB || + pipe->ccdc_in == CCDC_RAW_BGGR || + pipe->ccdc_in == CCDC_RAW_GBRG) { + ispccdc_config_lsc(isp_ccdc, &isp_ccdc->lsc_config); + ispccdc_load_lsc(isp_ccdc, isp_ccdc->lsc_gain_table, + isp_ccdc->lsc_config.size); + } + } + + return 0; +} +EXPORT_SYMBOL(ispccdc_s_pipeline); + +/** + * ispccdc_config_outlineoffset - Configure memory saving output line offset + * @isp_ccdc: Pointer to ISP CCDC device. + * @offset: Address offset to start a new line. Must be twice the + * Output width and aligned on 32 byte boundary + * @oddeven: Specifies the odd/even line pattern to be chosen to store the + * output. + * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. + * + * - Configures the output line offset when stored in memory + * - Sets the odd/even line pattern to store the output + * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) + * - Configures the number of even and odd line fields in case of rearranging + * the lines. + * + * Returns 0 if successful, or -EINVAL if the offset is not in 32 byte + * boundary. + **/ +int ispccdc_config_outlineoffset(struct isp_ccdc_device *isp_ccdc, u32 offset, + u8 oddeven, u8 numlines) +{ + if ((offset & ISP_32B_BOUNDARY_OFFSET) == offset) { + isp_reg_writel(isp_ccdc->dev, (offset & 0xFFFF), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); + } else { + DPRINTK_ISPCCDC("ISP_ERR : Offset should be in 32 byte" + " boundary\n"); + return -EINVAL; + } + + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + ~ISPCCDC_SDOFST_FINV); + + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, + ~ISPCCDC_SDOFST_FOFST_4L); + + switch (oddeven) { + case EVENEVEN: + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); + break; + case ODDEVEN: + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); + break; + case EVENODD: + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); + break; + case ODDODD: + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDOFST, + (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); + break; + default: + break; + } + return 0; +} +EXPORT_SYMBOL(ispccdc_config_outlineoffset); + +/** + * ispccdc_set_outaddr - Set memory address to save output image + * @isp_ccdc: Pointer to ISP CCDC device. + * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. + * + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte + * boundary. + **/ +int ispccdc_set_outaddr(struct isp_ccdc_device *isp_ccdc, u32 addr) +{ + if ((addr & ISP_32B_BOUNDARY_BUF) == addr) { + isp_reg_writel(isp_ccdc->dev, addr, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDR_ADDR); + return 0; + } else { + DPRINTK_ISPCCDC("ISP_ERR : Address should be in 32 byte" + " boundary\n"); + return -EINVAL; + } + +} +EXPORT_SYMBOL(ispccdc_set_outaddr); + +/** + * ispccdc_enable - Enable the CCDC module. + * @isp_ccdc: Pointer to ISP CCDC device. + * @enable: 0 Disables CCDC, 1 Enables CCDC + * + * Client should configure all the sub modules in CCDC before this. + **/ +void ispccdc_enable(struct isp_ccdc_device *isp_ccdc, u8 enable) +{ + struct isp_device *isp = + container_of(isp_ccdc, struct isp_device, isp_ccdc); + + if (enable) { + if (isp_ccdc->lsc_enable && + ((isp->pipeline.ccdc_in == CCDC_RAW_GRBG) || + (isp->pipeline.ccdc_in == CCDC_RAW_RGGB) || + (isp->pipeline.ccdc_in == CCDC_RAW_BGGR) || + (isp->pipeline.ccdc_in == CCDC_RAW_GBRG))) + ispccdc_enable_lsc(isp_ccdc, 1); + + } else { + int lsc_enable = isp_ccdc->lsc_state; + + ispccdc_enable_lsc(isp_ccdc, 0); + isp_ccdc->lsc_enable = lsc_enable; + } + + isp_reg_and_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, + ~ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); +} +EXPORT_SYMBOL(ispccdc_enable); + +/** + * ispccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits + * @_isp_ccdc: Pointer to ISP CCDC device. + * + * Returns zero if the CCDC is idle and the image has been written to + * memory, too. + **/ +int ispccdc_sbl_busy(void *_isp_ccdc) +{ + struct isp_ccdc_device *isp_ccdc = _isp_ccdc; + + return ispccdc_busy(isp_ccdc) + | (isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_SBL, + ISPSBL_CCDC_WR_0) & + ISPSBL_CCDC_WR_0_DATA_READY) + | (isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_SBL, + ISPSBL_CCDC_WR_1) & + ISPSBL_CCDC_WR_0_DATA_READY) + | (isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_SBL, + ISPSBL_CCDC_WR_2) & + ISPSBL_CCDC_WR_0_DATA_READY) + | (isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_SBL, + ISPSBL_CCDC_WR_3) & + ISPSBL_CCDC_WR_0_DATA_READY); +} +EXPORT_SYMBOL(ispccdc_sbl_busy); + +/** + * ispccdc_config_y8pos - Configures the location of Y color component + * @mode: Y8POS_EVEN Y pixel in even position, otherwise Y pixel in odd + * + * Configures the location of Y color componenent for YCbCr 8-bit data + */ +void ispccdc_config_y8pos(struct isp_ccdc_device *isp_ccdc, + enum y8pos_mode mode) +{ + if (mode == Y8POS_EVEN) { + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, + ~ISPCCDC_CFG_Y8POS); + } else { + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, + ISPCCDC_CFG_Y8POS); + } +} +EXPORT_SYMBOL(ispccdc_config_y8pos); + +/** + * ispccdc_config_byteswap - Configures byte swap data stored in memory + * @swap: 1 - swap bytes, 0 - normal + * + * Controls the order in which the Y and C pixels are stored in memory + */ +void ispccdc_config_byteswap(struct isp_ccdc_device *isp_ccdc, int swap) +{ + if (swap) { + isp_reg_or(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, + ISPCCDC_CFG_BSWD); + } else { + isp_reg_and(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, + ~ISPCCDC_CFG_BSWD); + } +} +EXPORT_SYMBOL(ispccdc_config_byteswap); + +/** + * ispccdc_busy - Get busy state of the CCDC. + * @isp_ccdc: Pointer to ISP CCDC device. + **/ +int ispccdc_busy(struct isp_ccdc_device *isp_ccdc) +{ + return isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_PCR) & + ISPCCDC_PCR_BUSY; +} +EXPORT_SYMBOL(ispccdc_busy); + +/** + * ispccdc_save_context - Save values of the CCDC module registers + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +void ispccdc_save_context(struct device *dev) +{ + DPRINTK_ISPCCDC("Saving context\n"); + isp_save_context(dev, ispccdc_reg_list); +} +EXPORT_SYMBOL(ispccdc_save_context); + +/** + * ispccdc_restore_context - Restore values of the CCDC module registers + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +void ispccdc_restore_context(struct device *dev) +{ + DPRINTK_ISPCCDC("Restoring context\n"); + isp_restore_context(dev, ispccdc_reg_list); +} +EXPORT_SYMBOL(ispccdc_restore_context); + +/** + * ispccdc_print_status - Print current CCDC Module register values. + * @isp_ccdc: Pointer to ISP CCDC device. + * @pipe: Pointer to current ISP pipeline structure. + * + * Also prints other debug information stored in the CCDC module. + **/ +void ispccdc_print_status(struct isp_ccdc_device *isp_ccdc, + struct isp_pipeline *pipe) +{ + if (!is_ispccdc_debug_enabled()) + return; + + DPRINTK_ISPCCDC("Module in use =%d\n", isp_ccdc->ccdc_inuse); + DPRINTK_ISPCCDC("Accepted CCDC Input (width = %d,Height = %d)\n", + isp_ccdc->ccdcin_w, + isp_ccdc->ccdcin_h); + DPRINTK_ISPCCDC("Accepted CCDC Output (width = %d,Height = %d)\n", + isp_ccdc->ccdcout_w, + isp_ccdc->ccdcout_h); + DPRINTK_ISPCCDC("###CCDC PCR=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_PCR)); + DPRINTK_ISPCCDC("ISP_CTRL =0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL)); + switch ((int)pipe->ccdc_in) { + case CCDC_RAW_GRBG: + case CCDC_RAW_RGGB: + case CCDC_RAW_BGGR: + case CCDC_RAW_GBRG: + DPRINTK_ISPCCDC("ccdc input format is CCDC_RAW\n"); + break; + case CCDC_YUV_SYNC: + DPRINTK_ISPCCDC("ccdc input format is CCDC_YUV_SYNC\n"); + break; + case CCDC_YUV_BT: + DPRINTK_ISPCCDC("ccdc input format is CCDC_YUV_BT\n"); + break; + } + + switch ((int)pipe->ccdc_out) { + case CCDC_OTHERS_VP: + DPRINTK_ISPCCDC("ccdc output format is CCDC_OTHERS_VP\n"); + break; + case CCDC_OTHERS_MEM: + DPRINTK_ISPCCDC("ccdc output format is CCDC_OTHERS_MEM\n"); + break; + case CCDC_YUV_RSZ: + DPRINTK_ISPCCDC("ccdc output format is CCDC_YUV_RSZ\n"); + break; + } + + DPRINTK_ISPCCDC("###ISP_CTRL in ccdc =0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL)); + DPRINTK_ISPCCDC("###ISP_IRQ0ENABLE in ccdc =0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_IRQ0ENABLE)); + DPRINTK_ISPCCDC("###ISP_IRQ0STATUS in ccdc =0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_IRQ0STATUS)); + DPRINTK_ISPCCDC("###CCDC SYN_MODE=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE)); + DPRINTK_ISPCCDC("###CCDC HORZ_INFO=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HORZ_INFO)); + DPRINTK_ISPCCDC("###CCDC VERT_START=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_START)); + DPRINTK_ISPCCDC("###CCDC VERT_LINES=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_LINES)); + DPRINTK_ISPCCDC("###CCDC CULLING=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CULLING)); + DPRINTK_ISPCCDC("###CCDC HSIZE_OFF=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HSIZE_OFF)); + DPRINTK_ISPCCDC("###CCDC SDOFST=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDOFST)); + DPRINTK_ISPCCDC("###CCDC SDR_ADDR=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDR_ADDR)); + DPRINTK_ISPCCDC("###CCDC CLAMP=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CLAMP)); + DPRINTK_ISPCCDC("###CCDC COLPTN=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_COLPTN)); + DPRINTK_ISPCCDC("###CCDC CFG=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CFG)); + DPRINTK_ISPCCDC("###CCDC VP_OUT=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VP_OUT)); + DPRINTK_ISPCCDC("###CCDC_SDR_ADDR= 0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDR_ADDR)); + DPRINTK_ISPCCDC("###CCDC FMTCFG=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMTCFG)); + DPRINTK_ISPCCDC("###CCDC FMT_HORZ=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMT_HORZ)); + DPRINTK_ISPCCDC("###CCDC FMT_VERT=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMT_VERT)); + DPRINTK_ISPCCDC("###CCDC LSC_CONFIG=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_CONFIG)); + DPRINTK_ISPCCDC("###CCDC LSC_INIT=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_INITIAL)); + DPRINTK_ISPCCDC("###CCDC LSC_TABLE BASE=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_TABLE_BASE)); + DPRINTK_ISPCCDC("###CCDC LSC TABLE OFFSET=0x%x\n", + isp_reg_readl(isp_ccdc->dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_LSC_TABLE_OFFSET)); +} +EXPORT_SYMBOL(ispccdc_print_status); + +/** + * isp_ccdc_init - CCDC module initialization. + * @dev: Device pointer specific to the OMAP3 ISP. + * + * Always returns 0 + **/ +int __init isp_ccdc_init(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_ccdc_device *isp_ccdc = &isp->isp_ccdc; + + isp_ccdc->ccdc_inuse = 0; + ispccdc_config_crop(isp_ccdc, 0, 0, 0, 0); + mutex_init(&isp_ccdc->mutexlock); + isp_ccdc->dev = dev; + + if (is_isplsc_activated()) { + isp_ccdc->lsc_gain_table_tmp = kmalloc(LSC_TABLE_INIT_SIZE, + GFP_KERNEL | GFP_DMA); + memset(isp_ccdc->lsc_gain_table_tmp, 0x40, LSC_TABLE_INIT_SIZE); + isp_ccdc->lsc_config.initial_x = 0; + isp_ccdc->lsc_config.initial_y = 0; + isp_ccdc->lsc_config.gain_mode_n = 0x6; + isp_ccdc->lsc_config.gain_mode_m = 0x6; + isp_ccdc->lsc_config.gain_format = 0x4; + isp_ccdc->lsc_config.offset = 0x60; + isp_ccdc->lsc_config.size = LSC_TABLE_INIT_SIZE; + isp_ccdc->lsc_enable = 1; + } + + return 0; +} + +/** + * isp_ccdc_cleanup - CCDC module cleanup. + * @dev: Device pointer specific to the OMAP3 ISP. + **/ +void isp_ccdc_cleanup(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_ccdc_device *isp_ccdc = &isp->isp_ccdc; + + if (is_isplsc_activated()) { + ispccdc_free_lsc(isp_ccdc); + kfree(isp_ccdc->lsc_gain_table_tmp); + } + + if (isp_ccdc->fpc_table_add_m != 0) { + iommu_kunmap(isp->iommu, isp_ccdc->fpc_table_add_m); + kfree(isp_ccdc->fpc_table_add); + } +} diff --git a/drivers/media/video/isp/ispccdc.h b/drivers/media/video/isp/ispccdc.h new file mode 100644 index 00000000000..07ba00dc416 --- /dev/null +++ b/drivers/media/video/isp/ispccdc.h @@ -0,0 +1,286 @@ +/* + * ispccdc.h + * + * Driver header file for CCDC module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Senthilvadivu Guruswamy + * Pallavi Kulkarni + * Sergio Aguirre + * + * 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 OMAP_ISP_CCDC_H +#define OMAP_ISP_CCDC_H + +#include + +#define is_isplsc_activated() 1 + +/* Enumeration constants for CCDC input output format */ +enum ccdc_input { + CCDC_RAW_GRBG, + CCDC_RAW_RGGB, + CCDC_RAW_BGGR, + CCDC_RAW_GBRG, + CCDC_YUV_SYNC, + CCDC_YUV_BT, + CCDC_OTHERS +}; + +enum ccdc_output { + CCDC_YUV_RSZ, + CCDC_YUV_MEM_RSZ, + CCDC_OTHERS_VP, + CCDC_OTHERS_MEM, + CCDC_OTHERS_VP_MEM +}; + +/* Enumeration constants for the sync interface parameters */ +enum inpmode { + RAW, + YUV16, + YUV8 +}; +enum datasize { + DAT8, + DAT10, + DAT11, + DAT12 +}; + +/* Enumeration constants for location of Y component in 8-bit YUV data */ +enum y8pos_mode { + Y8POS_EVEN = 0, + Y8POS_ODD = 1 +}; + +/** + * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC + * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave. + * @fldstat: Field state. 0 - Odd Field, 1 - Even Field. + * @ipmod: Input mode. + * @datsz: Data size. + * @fldmode: 0 - Progressive, 1 - Interlaced. + * @datapol: 0 - Positive, 1 - Negative. + * @fldpol: 0 - Positive, 1 - Negative. + * @hdpol: 0 - Positive, 1 - Negative. + * @vdpol: 0 - Positive, 1 - Negative. + * @fldout: 0 - Input, 1 - Output. + * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output. + * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output. + * @ppln: Number of pixels per line, used for HS/VS Output. + * @hlprf: Number of half lines per frame, used for HS/VS Output. + * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode. + */ +struct ispccdc_syncif { + u8 ccdc_mastermode; + u8 fldstat; + enum inpmode ipmod; + enum datasize datsz; + u8 fldmode; + u8 datapol; + u8 fldpol; + u8 hdpol; + u8 vdpol; + u8 fldout; + u8 hs_width; + u8 vs_width; + u8 ppln; + u8 hlprf; + u8 bt_r656_en; +}; + +/** + * ispccdc_refmt - Structure for Reformatter parameters + * @lnalt: Line alternating mode enable. 0 - Enable, 1 - Disable. + * @lnum: Number of output lines from 1 input line. 1 to 4 lines. + * @plen_even: Number of program entries in even line minus 1. + * @plen_odd: Number of program entries in odd line minus 1. + * @prgeven0: Program entries 0-7 for even lines register + * @prgeven1: Program entries 8-15 for even lines register + * @prgodd0: Program entries 0-7 for odd lines register + * @prgodd1: Program entries 8-15 for odd lines register + * @fmtaddr0: Output line in which the original pixel is to be placed + * @fmtaddr1: Output line in which the original pixel is to be placed + * @fmtaddr2: Output line in which the original pixel is to be placed + * @fmtaddr3: Output line in which the original pixel is to be placed + * @fmtaddr4: Output line in which the original pixel is to be placed + * @fmtaddr5: Output line in which the original pixel is to be placed + * @fmtaddr6: Output line in which the original pixel is to be placed + * @fmtaddr7: Output line in which the original pixel is to be placed + */ +struct ispccdc_refmt { + u8 lnalt; + u8 lnum; + u8 plen_even; + u8 plen_odd; + u32 prgeven0; + u32 prgeven1; + u32 prgodd0; + u32 prgodd1; + u32 fmtaddr0; + u32 fmtaddr1; + u32 fmtaddr2; + u32 fmtaddr3; + u32 fmtaddr4; + u32 fmtaddr5; + u32 fmtaddr6; + u32 fmtaddr7; +}; + +/** + * struct isp_ccdc_device - Structure for the CCDC module to store its own + * information + * @ccdc_inuse: Flag to determine if CCDC has been reserved or not (0 or 1). + * @ccdcin_woffset: CCDC input horizontal offset. + * @ccdcin_hoffset: CCDC input vertical offset. + * @crop_w: Crop width. + * @crop_h: Crop weight. + * @vpout_en: Video port output enable. + * @wen: Data write enable. + * @exwen: External data write enable. + * @refmt_en: Reformatter enable. + * @ccdcslave: CCDC slave mode enable. + * @syncif_ipmod: Image + * @obclamp_en: Data input format. + * @lsc_enable: Pending enable of the LSC. + * @lsc_initialized: Is LSC initialized? + * @lsc_state: Enable state of the LSC. + * @mutexlock: Mutex used to get access to the CCDC. + * @wenlog: Write Enable logic to use against valid data signal. + * @lsc_gain_table_tmp: Pointer to Virtual address of temporary LSC table. + * @lsc_ispmmu_addr: ISP MMU mapped address of the current used LSC table. + * @lsc_gain_table: Virtual address of the current used LSC table. + * @lsc_config: Pointer to LSC configuration structure. + * @fpc_table_add_m: ISP MMU mapped address of the current used FPC table. + * @fpc_table_add: Virtual address of the current used FPC table. + * @dev: Device pointer specific to the OMAP3 ISP. + */ +struct isp_ccdc_device { + u8 ccdc_inuse; + u32 ccdcin_woffset; + u32 ccdcin_hoffset; + u32 crop_w; + u32 crop_h; + u8 vpout_en; + u8 wen; + u8 exwen; + u8 refmt_en; + u8 ccdcslave; + u8 syncif_ipmod; + u8 obclamp_en; + u8 lsc_enable; + u8 lsc_initialized; + int lsc_state; + struct mutex mutexlock; /* For checking/modifying ccdc_inuse */ + u32 wenlog; + u8 *lsc_gain_table_tmp; + unsigned long lsc_ispmmu_addr; + u8 *lsc_gain_table; + struct ispccdc_lsc_config lsc_config; + unsigned long fpc_table_add_m; + u32 *fpc_table_add; + struct device *dev; +}; + +int ispccdc_request(struct isp_ccdc_device *isp_ccdc); + +int ispccdc_free(struct isp_ccdc_device *isp_ccdc); + +void ispccdc_config_crop(struct isp_ccdc_device *isp_ccdc, u32 left, u32 top, + u32 height, u32 width); + +void ispccdc_config_sync_if(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_syncif syncif); + +int ispccdc_config_black_clamp(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_bclamp bclamp); + +void ispccdc_enable_black_clamp(struct isp_ccdc_device *isp_ccdc, u8 enable); + +int ispccdc_config_fpc(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_fpc fpc); + +void ispccdc_enable_fpc(struct isp_ccdc_device *isp_ccdc, u8 enable); + +void ispccdc_config_black_comp(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_blcomp blcomp); + +void ispccdc_config_vp(struct isp_ccdc_device *isp_ccdc, struct ispccdc_vp vp); + +void ispccdc_enable_vp(struct isp_ccdc_device *isp_ccdc, u8 enable); + +void ispccdc_config_reformatter(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_refmt refmt); + +void ispccdc_enable_reformatter(struct isp_ccdc_device *isp_ccdc, u8 enable); + +void ispccdc_config_culling(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_culling culling); + +void ispccdc_enable_lpf(struct isp_ccdc_device *isp_ccdc, u8 enable); + +void ispccdc_config_alaw(struct isp_ccdc_device *isp_ccdc, + enum alaw_ipwidth ipwidth); + +void ispccdc_enable_alaw(struct isp_ccdc_device *isp_ccdc, u8 enable); + +int ispccdc_load_lsc(struct isp_ccdc_device *isp_ccdc, u8 *table_addr, + u32 table_size); + +void ispccdc_config_lsc(struct isp_ccdc_device *isp_ccdc, + struct ispccdc_lsc_config *lsc_cfg); + +void ispccdc_enable_lsc(struct isp_ccdc_device *isp_ccdc, u8 enable); + +void ispccdc_lsc_error_handler(struct isp_ccdc_device *isp_ccdc); + +void ispccdc_config_imgattr(struct isp_ccdc_device *isp_ccdc, u32 colptn); + +void ispccdc_config_shadow_registers(struct isp_ccdc_device *isp_ccdc); + +int ispccdc_try_pipeline(struct isp_ccdc_device *isp_ccdc, + struct isp_pipeline *pipe); + +int ispccdc_s_pipeline(struct isp_ccdc_device *isp_ccdc, + struct isp_pipeline *pipe); + +int ispccdc_config_outlineoffset(struct isp_ccdc_device *isp_ccdc, u32 offset, + u8 oddeven, u8 numlines); + +int ispccdc_set_outaddr(struct isp_ccdc_device *isp_ccdc, u32 addr); + +void ispccdc_enable(struct isp_ccdc_device *isp_ccdc, u8 enable); + +int ispccdc_sbl_busy(void *_isp_ccdc); + +int ispccdc_busy(struct isp_ccdc_device *isp_ccdc); + +void ispccdc_save_context(struct device *dev); + +void ispccdc_restore_context(struct device *dev); + +void ispccdc_print_status(struct isp_ccdc_device *isp_ccdc, + struct isp_pipeline *pipe); + +int omap34xx_isp_ccdc_config(struct isp_ccdc_device *isp_ccdc, + void *userspace_add); + +void ispccdc_set_wenlog(struct isp_ccdc_device *isp_ccdc, u32 wenlog); + +void ispccdc_config_y8pos(struct isp_ccdc_device *isp_ccdc, + enum y8pos_mode mode); + +void ispccdc_config_byteswap(struct isp_ccdc_device *isp_ccdc, int swap); + +#endif /* OMAP_ISP_CCDC_H */ diff --git a/drivers/media/video/isp/ispcsi2.c b/drivers/media/video/isp/ispcsi2.c new file mode 100644 index 00000000000..d65460d5a6d --- /dev/null +++ b/drivers/media/video/isp/ispcsi2.c @@ -0,0 +1,2276 @@ +/* + * ispcsi2.c + * + * Driver Library for ISP CSI Control module in TI's OMAP3 Camera ISP + * ISP CSI interface and IRQ related APIs are defined here. + * + * Copyright (C) 2009 Texas Instruments. + * + * Contributors: + * Sergio Aguirre + * Dominic Curran + * + * 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 +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispcsi2.h" + +static struct isp_csi2_cfg current_csi2_cfg; +static struct isp_csi2_cfg_update current_csi2_cfg_update; + +static bool update_complexio_cfg1; +static bool update_phy_cfg0; +static bool update_phy_cfg1; +static bool update_ctx_ctrl1[8]; +static bool update_ctx_ctrl2[8]; +static bool update_ctx_ctrl3[8]; +static bool update_timing; +static bool update_ctrl; +static bool uses_videoport; + +/* Structure for saving/restoring CSI2 module registers*/ +static struct isp_reg ispcsi2_reg_list[] = { + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SYSCONFIG, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SYSSTATUS, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_IRQSTATUS, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_IRQENABLE, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTRL, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_DBG_H, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_GNQ, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO1_IRQSTATUS, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SHORT_PACKET, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO1_IRQENABLE, 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_CTRL1(0), 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_CTRL2(0), 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_DAT_OFST(0), 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_DAT_PING_ADDR(0), 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_DAT_PONG_ADDR(0), 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_IRQENABLE(0), 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_IRQSTATUS(0), 0}, + {OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_CTRL3(0), 0}, + {OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG0, 0}, + {OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG1, 0}, + {0, ISP_TOK_TERM, 0} +}; + +/** + * isp_csi2_complexio_lanes_config - Configuration of CSI2 ComplexIO lanes. + * @reqcfg: Pointer to structure containing desired lane configuration + * + * Validates and saves to internal driver memory the passed configuration. + * Returns 0 if successful, or -EINVAL if null pointer is passed, invalid + * lane position or polarity is set, and if 2 lanes try to occupy the same + * position. To apply this settings, use the isp_csi2_complexio_lanes_update() + * function just after calling this function. + **/ +int isp_csi2_complexio_lanes_config(struct isp_csi2_lanes_cfg *reqcfg) +{ + int i; + bool pos_occupied[5] = {false, false, false, false, false}; + struct isp_csi2_lanes_cfg *currlanes = ¤t_csi2_cfg.lanes; + struct isp_csi2_lanes_cfg_update *currlanes_u = + ¤t_csi2_cfg_update.lanes; + + /* Validating parameters sent by driver */ + if (reqcfg == NULL) { + printk(KERN_ERR "Invalid Complex IO Configuration sent by" + " sensor\n"); + goto err_einval; + } + + /* Data lanes verification */ + for (i = 0; i < 4; i++) { + if ((reqcfg->data[i].pol > 1) || (reqcfg->data[i].pos > 5)) { + printk(KERN_ERR "Invalid CSI-2 Complex IO configuration" + " parameters for data lane #%d\n", i); + goto err_einval; + } + if (pos_occupied[reqcfg->data[i].pos - 1] && + reqcfg->data[i].pos > 0) { + printk(KERN_ERR "Lane #%d already occupied\n", + reqcfg->data[i].pos); + goto err_einval; + } else + pos_occupied[reqcfg->data[i].pos - 1] = true; + } + + /* Clock lane verification */ + if ((reqcfg->clk.pol > 1) || (reqcfg->clk.pos > 5) || + (reqcfg->clk.pos == 0)) { + printk(KERN_ERR "Invalid CSI-2 Complex IO configuration" + " parameters for clock lane\n"); + goto err_einval; + } + if (pos_occupied[reqcfg->clk.pos - 1]) { + printk(KERN_ERR "Lane #%d already occupied", + reqcfg->clk.pos); + goto err_einval; + } else + pos_occupied[reqcfg->clk.pos - 1] = true; + + for (i = 0; i < 4; i++) { + if (currlanes->data[i].pos != reqcfg->data[i].pos) { + currlanes->data[i].pos = reqcfg->data[i].pos; + currlanes_u->data[i] = true; + update_complexio_cfg1 = true; + } + if (currlanes->data[i].pol != reqcfg->data[i].pol) { + currlanes->data[i].pol = reqcfg->data[i].pol; + currlanes_u->data[i] = true; + update_complexio_cfg1 = true; + } + /* If the lane position is non zero then we can assume that + * the initial lane state is on. + */ + if (currlanes->data[i].pos) + currlanes->data[i].state = ISP_CSI2_LANE_ON; + } + + if (currlanes->clk.pos != reqcfg->clk.pos) { + currlanes->clk.pos = reqcfg->clk.pos; + currlanes_u->clk = true; + update_complexio_cfg1 = true; + } + if (currlanes->clk.pol != reqcfg->clk.pol) { + currlanes->clk.pol = reqcfg->clk.pol; + currlanes_u->clk = true; + update_complexio_cfg1 = true; + } + return 0; +err_einval: + return -EINVAL; +} + +/** + * isp_csi2_complexio_lanes_update - Applies CSI2 ComplexIO lanes configuration. + * @force_update: Flag to force rewrite of registers, even if they haven't been + * updated with the isp_csi2_complexio_lanes_config() function. + * + * It only saves settings when they were previously updated using the + * isp_csi2_complexio_lanes_config() function, unless the force_update flag is + * set to true. + * Always returns 0. + **/ +int isp_csi2_complexio_lanes_update(bool force_update) +{ + struct isp_csi2_lanes_cfg *currlanes = ¤t_csi2_cfg.lanes; + struct isp_csi2_lanes_cfg_update *currlanes_u = + ¤t_csi2_cfg_update.lanes; + u32 reg; + int i; + + if (!update_complexio_cfg1 && !force_update) + return 0; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1); + for (i = 0; i < 4; i++) { + if (currlanes_u->data[i] || force_update) { + reg &= ~(ISPCSI2_COMPLEXIO_CFG1_DATA_POL_MASK(i + 1) | + ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_MASK(i + + 1)); + reg |= (currlanes->data[i].pol << + ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(i + 1)); + if (currlanes->data[i].state == ISP_CSI2_LANE_ON) + reg |= (currlanes->data[i].pos << + ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(i + + 1)); + currlanes_u->data[i] = false; + } + } + + if (currlanes_u->clk || force_update) { + reg &= ~(ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_MASK | + ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_MASK); + reg |= (currlanes->clk.pol << + ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT); + reg |= (currlanes->clk.pos << + ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT); + currlanes_u->clk = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1); + + update_complexio_cfg1 = false; + return 0; +} + +/** + * isp_csi2_complexio_lanes_count - Turn data lanes on/off dynamically. + * @ cnt: Number of data lanes to enable. + * + * Always returns 0. + **/ +int isp_csi2_complexio_lanes_count(int cnt) +{ + struct isp_csi2_lanes_cfg *currlanes = ¤t_csi2_cfg.lanes; + int i; + + for (i = 0; i < 4; i++) { + if (i < cnt) + currlanes->data[i].state = ISP_CSI2_LANE_ON; + else + currlanes->data[i].state = ISP_CSI2_LANE_OFF; + } + + isp_csi2_complexio_lanes_update(true); + return 0; +} +EXPORT_SYMBOL(isp_csi2_complexio_lanes_count); + +/** + * isp_csi2_complexio_lanes_get - Gets CSI2 ComplexIO lanes configuration. + * + * Gets settings from HW registers and fills in the internal driver memory + * Always returns 0. + **/ +int isp_csi2_complexio_lanes_get(void) +{ + struct isp_csi2_lanes_cfg *currlanes = ¤t_csi2_cfg.lanes; + struct isp_csi2_lanes_cfg_update *currlanes_u = + ¤t_csi2_cfg_update.lanes; + u32 reg; + int i; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1); + for (i = 0; i < 4; i++) { + currlanes->data[i].pol = (reg & + ISPCSI2_COMPLEXIO_CFG1_DATA_POL_MASK(i + 1)) >> + ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(i + 1); + currlanes->data[i].pos = (reg & + ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_MASK(i + 1)) >> + ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(i + 1); + currlanes_u->data[i] = false; + } + currlanes->clk.pol = (reg & ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_MASK) >> + ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT; + currlanes->clk.pos = (reg & + ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_MASK) >> + ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT; + currlanes_u->clk = false; + + update_complexio_cfg1 = false; + return 0; +} + +/** + * isp_csi2_complexio_power_status - Gets CSI2 ComplexIO power status. + * + * Returns 3 possible valid states: ISP_CSI2_POWER_OFF, ISP_CSI2_POWER_ON, + * and ISP_CSI2_POWER_ULPW. + **/ +static enum isp_csi2_power_cmds isp_csi2_complexio_power_status(void) +{ + enum isp_csi2_power_cmds ret; + u32 reg; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1) & + ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_MASK; + switch (reg) { + case ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_OFF: + ret = ISP_CSI2_POWER_OFF; + break; + case ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ON: + ret = ISP_CSI2_POWER_ON; + break; + case ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ULPW: + ret = ISP_CSI2_POWER_ULPW; + break; + default: + return -EINVAL; + } + return ret; +} + +/** + * isp_csi2_complexio_power_autoswitch - Sets CSI2 ComplexIO power autoswitch. + * @enable: Sets or clears the autoswitch function enable flag. + * + * Always returns 0. + **/ +int isp_csi2_complexio_power_autoswitch(bool enable) +{ + u32 reg; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1); + reg &= ~ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_MASK; + + if (enable) + reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_ENABLE; + else + reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_DISABLE; + + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1); + return 0; +} + +/** + * isp_csi2_complexio_power - Sets the desired power command for CSI2 ComplexIO. + * @power_cmd: Power command to be set. + * + * Returns 0 if successful, or -EBUSY if the retry count is exceeded. + **/ +int isp_csi2_complexio_power(enum isp_csi2_power_cmds power_cmd) +{ + enum isp_csi2_power_cmds current_state; + u32 reg; + u8 retry_count; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1) & + ~ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_MASK; + switch (power_cmd) { + case ISP_CSI2_POWER_OFF: + reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_OFF; + break; + case ISP_CSI2_POWER_ON: + reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ON; + break; + case ISP_CSI2_POWER_ULPW: + reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ULPW; + break; + default: + printk(KERN_ERR "CSI2: ERROR - Wrong Power command!\n"); + return -EINVAL; + } + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1); + + retry_count = 0; + do { + udelay(50); + current_state = isp_csi2_complexio_power_status(); + + if (current_state != power_cmd) { + printk(KERN_DEBUG "CSI2: Complex IO power command not" + " yet taken."); + if (++retry_count < 100) { + printk(KERN_DEBUG " Retrying...\n"); + udelay(50); + } else { + printk(KERN_DEBUG " Retry count exceeded!\n"); + } + } + } while ((current_state != power_cmd) && (retry_count < 100)); + + if (retry_count == 100) + return -EBUSY; + + return 0; +} + +/** + * isp_csi2_ctrl_config_frame_mode - Configure if_en behaviour for CSI2 + * @frame_mode: Desired action for IF_EN switch off. 0 - disable IF immediately + * 1 - disable after all Frame end Code is received in all + * contexts. + * + * Validates and saves to internal driver memory the passed configuration. + * Always returns 0. + **/ +int isp_csi2_ctrl_config_frame_mode(enum isp_csi2_frame_mode frame_mode) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if (currctrl->frame_mode != frame_mode) { + currctrl->frame_mode = frame_mode; + currctrl_u->frame_mode = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_vp_clk_enable - Enables/disables CSI2 Videoport clock. + * @vp_clk_enable: Boolean value to specify the Videoport clock state. + * + * Validates and saves to internal driver memory the passed configuration. + * Always returns 0. + **/ +int isp_csi2_ctrl_config_vp_clk_enable(bool vp_clk_enable) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if (currctrl->vp_clk_enable != vp_clk_enable) { + currctrl->vp_clk_enable = vp_clk_enable; + currctrl_u->vp_clk_enable = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_vp_only_enable - Sets CSI2 Videoport clock as exclusive + * @vp_only_enable: Boolean value to specify if the Videoport clock is + * exclusive, setting the OCP port as disabled. + * + * Validates and saves to internal driver memory the passed configuration. + * Always returns 0. + **/ +int isp_csi2_ctrl_config_vp_only_enable(bool vp_only_enable) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if (currctrl->vp_only_enable != vp_only_enable) { + currctrl->vp_only_enable = vp_only_enable; + currctrl_u->vp_only_enable = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_vp_out_ctrl - Sets CSI2 Videoport clock divider + * @vp_out_ctrl: Divider value for setting videoport clock frequency based on + * OCP port frequency, valid dividers are between 1 and 4. + * + * Validates and saves to internal driver memory the passed configuration. + * Returns 0 if successful, or -EINVAL if wrong divider value is passed. + **/ +int isp_csi2_ctrl_config_vp_out_ctrl(u8 vp_out_ctrl) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if ((vp_out_ctrl == 0) || (vp_out_ctrl > 4)) { + printk(KERN_ERR "CSI2: Wrong divisor value. Must be between" + " 1 and 4"); + return -EINVAL; + } + + if (currctrl->vp_out_ctrl != vp_out_ctrl) { + currctrl->vp_out_ctrl = vp_out_ctrl; + currctrl_u->vp_out_ctrl = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_debug_enable - Sets CSI2 debug + * @debug_enable: Boolean for setting debug configuration on CSI2. + * + * Always returns 0. + **/ +int isp_csi2_ctrl_config_debug_enable(bool debug_enable) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if (currctrl->debug_enable != debug_enable) { + currctrl->debug_enable = debug_enable; + currctrl_u->debug_enable = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_burst_size - Sets CSI2 burst size. + * @burst_size: Burst size of the memory saving capability of receiver. + * + * Returns 0 if successful, or -EINVAL if burst size is wrong. + **/ +int isp_csi2_ctrl_config_burst_size(u8 burst_size) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + if (burst_size > 3) { + printk(KERN_ERR "CSI2: Wrong burst size. Must be between" + " 0 and 3"); + return -EINVAL; + } + + if (currctrl->burst_size != burst_size) { + currctrl->burst_size = burst_size; + currctrl_u->burst_size = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_ecc_enable - Enables ECC on CSI2 Receiver + * @ecc_enable: Boolean to enable/disable the CSI2 receiver ECC handling. + * + * Always returns 0. + **/ +int isp_csi2_ctrl_config_ecc_enable(bool ecc_enable) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if (currctrl->ecc_enable != ecc_enable) { + currctrl->ecc_enable = ecc_enable; + currctrl_u->ecc_enable = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_ecc_enable - Enables ECC on CSI2 Receiver + * @ecc_enable: Boolean to enable/disable the CSI2 receiver ECC handling. + * + * Always returns 0. + **/ +int isp_csi2_ctrl_config_secure_mode(bool secure_mode) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if (currctrl->secure_mode != secure_mode) { + currctrl->secure_mode = secure_mode; + currctrl_u->secure_mode = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_config_if_enable - Enables CSI2 Receiver interface. + * @if_enable: Boolean to enable/disable the CSI2 receiver interface. + * + * Always returns 0. + **/ +int isp_csi2_ctrl_config_if_enable(bool if_enable) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + + if (currctrl->if_enable != if_enable) { + currctrl->if_enable = if_enable; + currctrl_u->if_enable = true; + update_ctrl = true; + } + return 0; +} + +/** + * isp_csi2_ctrl_update - Applies CSI2 control configuration. + * @force_update: Flag to force rewrite of registers, even if they haven't been + * updated with the isp_csi2_ctrl_config_*() functions. + * + * It only saves settings when they were previously updated using the + * isp_csi2_ctrl_config_*() functions, unless the force_update flag is + * set to true. + * Always returns 0. + **/ +int isp_csi2_ctrl_update(bool force_update) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + u32 reg; + + if (update_ctrl || force_update) { + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTRL); + if (currctrl_u->frame_mode || force_update) { + reg &= ~ISPCSI2_CTRL_FRAME_MASK; + if (currctrl->frame_mode) + reg |= ISPCSI2_CTRL_FRAME_DISABLE_FEC; + else + reg |= ISPCSI2_CTRL_FRAME_DISABLE_IMM; + currctrl_u->frame_mode = false; + } + if (currctrl_u->vp_clk_enable || force_update) { + reg &= ~ISPCSI2_CTRL_VP_CLK_EN_MASK; + if (currctrl->vp_clk_enable) + reg |= ISPCSI2_CTRL_VP_CLK_EN_ENABLE; + else + reg |= ISPCSI2_CTRL_VP_CLK_EN_DISABLE; + currctrl_u->vp_clk_enable = false; + } + if (currctrl_u->vp_only_enable || force_update) { + reg &= ~ISPCSI2_CTRL_VP_ONLY_EN_MASK; + uses_videoport = currctrl->vp_only_enable; + if (currctrl->vp_only_enable) + reg |= ISPCSI2_CTRL_VP_ONLY_EN_ENABLE; + else + reg |= ISPCSI2_CTRL_VP_ONLY_EN_DISABLE; + currctrl_u->vp_only_enable = false; + } + if (currctrl_u->vp_out_ctrl || force_update) { + reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK; + reg |= (currctrl->vp_out_ctrl - 1) << + ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT; + currctrl_u->vp_out_ctrl = false; + } + if (currctrl_u->debug_enable || force_update) { + reg &= ~ISPCSI2_CTRL_DBG_EN_MASK; + if (currctrl->debug_enable) + reg |= ISPCSI2_CTRL_DBG_EN_ENABLE; + else + reg |= ISPCSI2_CTRL_DBG_EN_DISABLE; + currctrl_u->debug_enable = false; + } + if (currctrl_u->burst_size || force_update) { + reg &= ~ISPCSI2_CTRL_BURST_SIZE_MASK; + reg |= currctrl->burst_size << + ISPCSI2_CTRL_BURST_SIZE_SHIFT; + currctrl_u->burst_size = false; + } + if (currctrl_u->ecc_enable || force_update) { + reg &= ~ISPCSI2_CTRL_ECC_EN_MASK; + if (currctrl->ecc_enable) + reg |= ISPCSI2_CTRL_ECC_EN_ENABLE; + else + reg |= ISPCSI2_CTRL_ECC_EN_DISABLE; + currctrl_u->ecc_enable = false; + } + if (currctrl_u->secure_mode || force_update) { + reg &= ~ISPCSI2_CTRL_SECURE_MASK; + if (currctrl->secure_mode) + reg |= ISPCSI2_CTRL_SECURE_ENABLE; + else + reg |= ISPCSI2_CTRL_SECURE_DISABLE; + currctrl_u->secure_mode = false; + } + if (currctrl_u->if_enable || force_update) { + reg &= ~ISPCSI2_CTRL_IF_EN_MASK; + if (currctrl->if_enable) + reg |= ISPCSI2_CTRL_IF_EN_ENABLE; + else + reg |= ISPCSI2_CTRL_IF_EN_DISABLE; + currctrl_u->if_enable = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTRL); + update_ctrl = false; + } + return 0; +} + +/** + * isp_csi2_ctrl_get - Gets CSI2 control configuration + * + * Always returns 0. + **/ +int isp_csi2_ctrl_get(void) +{ + struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; + struct isp_csi2_ctrl_cfg_update *currctrl_u = + ¤t_csi2_cfg_update.ctrl; + u32 reg; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTRL); + currctrl->frame_mode = (reg & ISPCSI2_CTRL_FRAME_MASK) >> + ISPCSI2_CTRL_FRAME_SHIFT; + currctrl_u->frame_mode = false; + + if ((reg & ISPCSI2_CTRL_VP_CLK_EN_MASK) == + ISPCSI2_CTRL_VP_CLK_EN_ENABLE) + currctrl->vp_clk_enable = true; + else + currctrl->vp_clk_enable = false; + currctrl_u->vp_clk_enable = false; + + if ((reg & ISPCSI2_CTRL_VP_ONLY_EN_MASK) == + ISPCSI2_CTRL_VP_ONLY_EN_ENABLE) + currctrl->vp_only_enable = true; + else + currctrl->vp_only_enable = false; + uses_videoport = currctrl->vp_only_enable; + currctrl_u->vp_only_enable = false; + + currctrl->vp_out_ctrl = ((reg & ISPCSI2_CTRL_VP_OUT_CTRL_MASK) >> + ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) + 1; + currctrl_u->vp_out_ctrl = false; + + if ((reg & ISPCSI2_CTRL_DBG_EN_MASK) == ISPCSI2_CTRL_DBG_EN_ENABLE) + currctrl->debug_enable = true; + else + currctrl->debug_enable = false; + currctrl_u->debug_enable = false; + + currctrl->burst_size = (reg & ISPCSI2_CTRL_BURST_SIZE_MASK) >> + ISPCSI2_CTRL_BURST_SIZE_SHIFT; + currctrl_u->burst_size = false; + + if ((reg & ISPCSI2_CTRL_ECC_EN_MASK) == ISPCSI2_CTRL_ECC_EN_ENABLE) + currctrl->ecc_enable = true; + else + currctrl->ecc_enable = false; + currctrl_u->ecc_enable = false; + + if ((reg & ISPCSI2_CTRL_SECURE_MASK) == ISPCSI2_CTRL_SECURE_ENABLE) + currctrl->secure_mode = true; + else + currctrl->secure_mode = false; + currctrl_u->secure_mode = false; + + if ((reg & ISPCSI2_CTRL_IF_EN_MASK) == ISPCSI2_CTRL_IF_EN_ENABLE) + currctrl->if_enable = true; + else + currctrl->if_enable = false; + currctrl_u->if_enable = false; + + update_ctrl = false; + return 0; +} + +/** + * isp_csi2_ctx_validate - Validates the context number value + * @ctxnum: Pointer to variable containing context number. + * + * If the value is not in range (3 bits), it is being ANDed with 0x7 to force + * it to be on range. + **/ +static void isp_csi2_ctx_validate(u8 *ctxnum) +{ + if (*ctxnum > 7) { + printk(KERN_ERR "Invalid context number. Forcing valid" + " value...\n"); + *ctxnum &= ~(0x7); + } +} + +/** + * isp_csi2_ctx_config_virtual_id - Maps a virtual ID with a CSI2 Rx context + * @ctxnum: Context number, valid between 0 and 7 values. + * @virtual_id: CSI2 Virtual ID to associate with specified context number. + * + * Returns 0 if successful, or -EINVAL if Virtual ID is not in range (0-3). + **/ +int isp_csi2_ctx_config_virtual_id(u8 ctxnum, u8 virtual_id) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + if (virtual_id > 3) { + printk(KERN_ERR "Wrong requested virtual_id\n"); + return -EINVAL; + } + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->virtual_id != virtual_id) { + selected_ctx->virtual_id = virtual_id; + selected_ctx_u->virtual_id = true; + update_ctx_ctrl2[ctxnum] = true; + } + + return 0; +} + +/** + * isp_csi2_ctx_config_frame_count - Sets frame count to be received in CSI2 Rx. + * @ctxnum: Context number, valid between 0 and 7 values. + * @frame_count: Number of frames to acquire. + * + * Always returns 0. + **/ +int isp_csi2_ctx_config_frame_count(u8 ctxnum, u8 frame_count) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->frame_count != frame_count) { + selected_ctx->frame_count = frame_count; + selected_ctx_u->frame_count = true; + update_ctx_ctrl1[ctxnum] = true; + } + + return 0; +} + +/** + * isp_csi2_ctx_config_format - Maps a pixel format to a specified context. + * @ctxnum: Context number, valid between 0 and 7 values. + * @pixformat: V4L2 structure for pixel format. + * + * Returns 0 if successful, or -EINVAL if the format is not supported by the + * receiver. + **/ +int isp_csi2_ctx_config_format(u8 ctxnum, u32 pixformat) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + struct v4l2_pix_format pix; + + isp_csi2_ctx_validate(&ctxnum); + + pix.pixelformat = pixformat; + switch (pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_SGRBG10: + break; + default: + printk(KERN_ERR "Context config pixel format unsupported\n"); + return -EINVAL; + } + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + selected_ctx->format = pix; + selected_ctx_u->format = true; + update_ctx_ctrl2[ctxnum] = true; + + return 0; +} + +/** + * isp_csi2_ctx_config_alpha - Sets the alpha value for pixel format + * @ctxnum: Context number, valid between 0 and 7 values. + * @alpha: Alpha value. + * + * Returns 0 if successful, or -EINVAL if the alpha value is bigger than 16383. + **/ +int isp_csi2_ctx_config_alpha(u8 ctxnum, u16 alpha) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + if (alpha > 0x3FFF) { + printk(KERN_ERR "Wrong alpha value\n"); + return -EINVAL; + } + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->alpha != alpha) { + selected_ctx->alpha = alpha; + selected_ctx_u->alpha = true; + update_ctx_ctrl3[ctxnum] = true; + } + return 0; +} + +/** + * isp_csi2_ctx_config_data_offset - Sets the offset between received lines + * @ctxnum: Context number, valid between 0 and 7 values. + * @data_offset: Offset between first pixel of each 2 contiguous lines. + * + * Returns 0 if successful, or -EINVAL if the line offset is bigger than 1023. + **/ +int isp_csi2_ctx_config_data_offset(u8 ctxnum, u16 data_offset) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + if (data_offset > 0x3FF) { + printk(KERN_ERR "Wrong line offset\n"); + return -EINVAL; + } + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->data_offset != data_offset) { + selected_ctx->data_offset = data_offset; + selected_ctx_u->data_offset = true; + } + return 0; +} + +/** + * isp_csi2_ctx_config_ping_addr - Sets Ping address for CSI2 Rx. buffer saving + * @ctxnum: Context number, valid between 0 and 7 values. + * @ping_addr: 32 bit ISP MMU mapped address. + * + * Always returns 0. + **/ +int isp_csi2_ctx_config_ping_addr(u8 ctxnum, u32 ping_addr) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + ping_addr &= ~(0x1F); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->ping_addr != ping_addr) { + selected_ctx->ping_addr = ping_addr; + selected_ctx_u->ping_addr = true; + } + return 0; +} + +/** + * isp_csi2_ctx_config_pong_addr - Sets Pong address for CSI2 Rx. buffer saving + * @ctxnum: Context number, valid between 0 and 7 values. + * @pong_addr: 32 bit ISP MMU mapped address. + * + * Always returns 0. + **/ +int isp_csi2_ctx_config_pong_addr(u8 ctxnum, u32 pong_addr) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + pong_addr &= ~(0x1F); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->pong_addr != pong_addr) { + selected_ctx->pong_addr = pong_addr; + selected_ctx_u->pong_addr = true; + } + return 0; +} + +/** + * isp_csi2_ctx_config_eof_enabled - Enables EOF signal assertion + * @ctxnum: Context number, valid between 0 and 7 values. + * @eof_enabled: Boolean to enable/disable EOF signal assertion on received + * packets. + * + * Always returns 0. + **/ +int isp_csi2_ctx_config_eof_enabled(u8 ctxnum, bool eof_enabled) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->eof_enabled != eof_enabled) { + selected_ctx->eof_enabled = eof_enabled; + selected_ctx_u->eof_enabled = true; + update_ctx_ctrl1[ctxnum] = true; + } + return 0; +} + +/** + * isp_csi2_ctx_config_eol_enabled - Enables EOL signal assertion + * @ctxnum: Context number, valid between 0 and 7 values. + * @eol_enabled: Boolean to enable/disable EOL signal assertion on received + * packets. + * + * Always returns 0. + **/ +int isp_csi2_ctx_config_eol_enabled(u8 ctxnum, bool eol_enabled) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->eol_enabled != eol_enabled) { + selected_ctx->eol_enabled = eol_enabled; + selected_ctx_u->eol_enabled = true; + update_ctx_ctrl1[ctxnum] = true; + } + return 0; +} + +/** + * isp_csi2_ctx_config_checksum_enabled - Enables Checksum check in rcvd packets + * @ctxnum: Context number, valid between 0 and 7 values. + * @checksum_enabled: Boolean to enable/disable Checksum check on received + * packets + * + * Always returns 0. + **/ +int isp_csi2_ctx_config_checksum_enabled(u8 ctxnum, bool checksum_enabled) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->checksum_enabled != checksum_enabled) { + selected_ctx->checksum_enabled = checksum_enabled; + selected_ctx_u->checksum_enabled = true; + update_ctx_ctrl1[ctxnum] = true; + } + return 0; +} + +/** + * isp_csi2_ctx_config_enabled - Enables specified CSI2 context + * @ctxnum: Context number, valid between 0 and 7 values. + * @enabled: Boolean to enable/disable specified context. + * + * Always returns 0. + **/ +int isp_csi2_ctx_config_enabled(u8 ctxnum, bool enabled) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + + isp_csi2_ctx_validate(&ctxnum); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (selected_ctx->enabled != enabled) { + selected_ctx->enabled = enabled; + selected_ctx_u->enabled = true; + update_ctx_ctrl1[ctxnum] = true; + } + return 0; +} + +/** + * isp_csi2_ctx_update - Applies CSI2 context configuration. + * @ctxnum: Context number, valid between 0 and 7 values. + * @force_update: Flag to force rewrite of registers, even if they haven't been + * updated with the isp_csi2_ctx_config_*() functions. + * + * It only saves settings when they were previously updated using the + * isp_csi2_ctx_config_*() functions, unless the force_update flag is + * set to true. + * Always returns 0. + **/ +int isp_csi2_ctx_update(u8 ctxnum, bool force_update) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + u32 reg; + + isp_csi2_ctx_validate(&ctxnum); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + if (update_ctx_ctrl1[ctxnum] || force_update) { + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL1(ctxnum)); + if (selected_ctx_u->frame_count || force_update) { + reg &= ~(ISPCSI2_CTX_CTRL1_COUNT_MASK); + reg |= selected_ctx->frame_count << + ISPCSI2_CTX_CTRL1_COUNT_SHIFT; + selected_ctx_u->frame_count = false; + } + if (selected_ctx_u->eof_enabled || force_update) { + reg &= ~(ISPCSI2_CTX_CTRL1_EOF_EN_MASK); + if (selected_ctx->eof_enabled) + reg |= ISPCSI2_CTX_CTRL1_EOF_EN_ENABLE; + else + reg |= ISPCSI2_CTX_CTRL1_EOF_EN_DISABLE; + selected_ctx_u->eof_enabled = false; + } + if (selected_ctx_u->eol_enabled || force_update) { + reg &= ~(ISPCSI2_CTX_CTRL1_EOL_EN_MASK); + if (selected_ctx->eol_enabled) + reg |= ISPCSI2_CTX_CTRL1_EOL_EN_ENABLE; + else + reg |= ISPCSI2_CTX_CTRL1_EOL_EN_DISABLE; + selected_ctx_u->eol_enabled = false; + } + if (selected_ctx_u->checksum_enabled || force_update) { + reg &= ~(ISPCSI2_CTX_CTRL1_CS_EN_MASK); + if (selected_ctx->checksum_enabled) + reg |= ISPCSI2_CTX_CTRL1_CS_EN_ENABLE; + else + reg |= ISPCSI2_CTX_CTRL1_CS_EN_DISABLE; + selected_ctx_u->checksum_enabled = false; + } + if (selected_ctx_u->enabled || force_update) { + reg &= ~(ISPCSI2_CTX_CTRL1_CTX_EN_MASK); + if (selected_ctx->enabled) + reg |= ISPCSI2_CTX_CTRL1_CTX_EN_ENABLE; + else + reg |= ISPCSI2_CTX_CTRL1_CTX_EN_DISABLE; + selected_ctx_u->enabled = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL1(ctxnum)); + update_ctx_ctrl1[ctxnum] = false; + } + + if (update_ctx_ctrl2[ctxnum] || force_update) { + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL2(ctxnum)); + if (selected_ctx_u->virtual_id || force_update) { + reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK); + reg |= selected_ctx->virtual_id << + ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; + selected_ctx_u->virtual_id = false; + } + + if (selected_ctx_u->format || force_update) { + struct v4l2_pix_format *pix; + u16 new_format = 0; + + reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK); + pix = &selected_ctx->format; + switch (pix->pixelformat) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + new_format = 0x22; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + if (uses_videoport) + new_format = 0x9E; + else + new_format = 0x1E; + break; + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + new_format = 0xA1; + break; + case V4L2_PIX_FMT_SGRBG10: + if (uses_videoport) + new_format = 0x12F; + else + new_format = 0xAB; + break; + } + reg |= (new_format << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT); + selected_ctx_u->format = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL2(ctxnum)); + update_ctx_ctrl2[ctxnum] = false; + } + + if (update_ctx_ctrl3[ctxnum] || force_update) { + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL3(ctxnum)); + if (selected_ctx_u->alpha || force_update) { + reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK); + reg |= (selected_ctx->alpha << + ISPCSI2_CTX_CTRL3_ALPHA_SHIFT); + selected_ctx_u->alpha = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL3(ctxnum)); + update_ctx_ctrl3[ctxnum] = false; + } + + if (selected_ctx_u->data_offset) { + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_OFST(ctxnum)); + reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK; + reg |= selected_ctx->data_offset << + ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_OFST(ctxnum)); + selected_ctx_u->data_offset = false; + } + + if (selected_ctx_u->ping_addr) { + reg = selected_ctx->ping_addr; + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_PING_ADDR(ctxnum)); + selected_ctx_u->ping_addr = false; + } + + if (selected_ctx_u->pong_addr) { + reg = selected_ctx->pong_addr; + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_PONG_ADDR(ctxnum)); + selected_ctx_u->pong_addr = false; + } + return 0; +} + +/** + * isp_csi2_ctx_get - Gets specific CSI2 Context configuration + * @ctxnum: Context number, valid between 0 and 7 values. + * + * Always returns 0. + **/ +int isp_csi2_ctx_get(u8 ctxnum) +{ + struct isp_csi2_ctx_cfg *selected_ctx; + struct isp_csi2_ctx_cfg_update *selected_ctx_u; + u32 reg; + + isp_csi2_ctx_validate(&ctxnum); + + selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; + selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL1(ctxnum)); + selected_ctx->frame_count = (reg & ISPCSI2_CTX_CTRL1_COUNT_MASK) >> + ISPCSI2_CTX_CTRL1_COUNT_SHIFT; + selected_ctx_u->frame_count = false; + + if ((reg & ISPCSI2_CTX_CTRL1_EOF_EN_MASK) == + ISPCSI2_CTX_CTRL1_EOF_EN_ENABLE) + selected_ctx->eof_enabled = true; + else + selected_ctx->eof_enabled = false; + selected_ctx_u->eof_enabled = false; + + if ((reg & ISPCSI2_CTX_CTRL1_EOL_EN_MASK) == + ISPCSI2_CTX_CTRL1_EOL_EN_ENABLE) + selected_ctx->eol_enabled = true; + else + selected_ctx->eol_enabled = false; + selected_ctx_u->eol_enabled = false; + + if ((reg & ISPCSI2_CTX_CTRL1_CS_EN_MASK) == + ISPCSI2_CTX_CTRL1_CS_EN_ENABLE) + selected_ctx->checksum_enabled = true; + else + selected_ctx->checksum_enabled = false; + selected_ctx_u->checksum_enabled = false; + + if ((reg & ISPCSI2_CTX_CTRL1_CTX_EN_MASK) == + ISPCSI2_CTX_CTRL1_CTX_EN_ENABLE) + selected_ctx->enabled = true; + else + selected_ctx->enabled = false; + selected_ctx_u->enabled = false; + update_ctx_ctrl1[ctxnum] = false; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL2(ctxnum)); + + selected_ctx->virtual_id = (reg & ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK) >> + ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; + selected_ctx_u->virtual_id = false; + + switch ((reg & ISPCSI2_CTX_CTRL2_FORMAT_MASK) >> + ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) { + case 0x22: + selected_ctx->format.pixelformat = V4L2_PIX_FMT_RGB565; + break; + case 0x9E: + case 0x1E: + selected_ctx->format.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case 0xA1: + selected_ctx->format.pixelformat = V4L2_PIX_FMT_RGB555; + break; + case 0xAB: + case 0x12F: + selected_ctx->format.pixelformat = V4L2_PIX_FMT_SGRBG10; + break; + } + selected_ctx_u->format = false; + update_ctx_ctrl2[ctxnum] = false; + + selected_ctx->alpha = (isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL3(ctxnum)) & + ISPCSI2_CTX_CTRL3_ALPHA_MASK) >> + ISPCSI2_CTX_CTRL3_ALPHA_SHIFT; + selected_ctx_u->alpha = false; + update_ctx_ctrl3[ctxnum] = false; + + selected_ctx->data_offset = (isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_OFST(ctxnum)) & + ISPCSI2_CTX_DAT_OFST_OFST_MASK) >> + ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; + selected_ctx_u->data_offset = false; + + selected_ctx->ping_addr = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_PING_ADDR(ctxnum)); + selected_ctx_u->ping_addr = false; + + selected_ctx->pong_addr = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_PONG_ADDR(ctxnum)); + selected_ctx_u->pong_addr = false; + return 0; +} + +/** + * isp_csi2_ctx_update_all - Applies all CSI2 context configuration. + * @force_update: Flag to force rewrite of registers, even if they haven't been + * updated with the isp_csi2_ctx_config_*() functions. + * + * It only saves settings when they were previously updated using the + * isp_csi2_ctx_config_*() functions, unless the force_update flag is + * set to true. + * Always returns 0. + **/ +int isp_csi2_ctx_update_all(bool force_update) +{ + u8 ctxnum; + + for (ctxnum = 0; ctxnum < 8; ctxnum++) + isp_csi2_ctx_update(ctxnum, force_update); + + return 0; +} + +/** + * isp_csi2_ctx_get_all - Gets all CSI2 Context configurations + * + * Always returns 0. + **/ +int isp_csi2_ctx_get_all(void) +{ + u8 ctxnum; + + for (ctxnum = 0; ctxnum < 8; ctxnum++) + isp_csi2_ctx_get(ctxnum); + + return 0; +} + +int isp_csi2_phy_config(struct isp_csi2_phy_cfg *desiredphyconfig) +{ + struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; + struct isp_csi2_phy_cfg_update *currphy_u = + ¤t_csi2_cfg_update.phy; + + if ((desiredphyconfig->tclk_term > 0x7f) || + (desiredphyconfig->tclk_miss > 0x3)) { + printk(KERN_ERR "Invalid PHY configuration sent by the" + " driver\n"); + return -EINVAL; + } + + if (currphy->ths_term != desiredphyconfig->ths_term) { + currphy->ths_term = desiredphyconfig->ths_term; + currphy_u->ths_term = true; + update_phy_cfg0 = true; + } + if (currphy->ths_settle != desiredphyconfig->ths_settle) { + currphy->ths_settle = desiredphyconfig->ths_settle; + currphy_u->ths_settle = true; + update_phy_cfg0 = true; + } + if (currphy->tclk_term != desiredphyconfig->tclk_term) { + currphy->tclk_term = desiredphyconfig->tclk_term; + currphy_u->tclk_term = true; + update_phy_cfg1 = true; + } + if (currphy->tclk_miss != desiredphyconfig->tclk_miss) { + currphy->tclk_miss = desiredphyconfig->tclk_miss; + currphy_u->tclk_miss = true; + update_phy_cfg1 = true; + } + if (currphy->tclk_settle != desiredphyconfig->tclk_settle) { + currphy->tclk_settle = desiredphyconfig->tclk_settle; + currphy_u->tclk_settle = true; + update_phy_cfg1 = true; + } + return 0; +} + +/** + * isp_csi2_calc_phy_cfg0 - Calculates D-PHY config based on the MIPIClk speed. + * @mipiclk: MIPI clock frequency being used with CSI2 sensor. + * @lbound_hs_settle: Lower bound for CSI2 High Speed Settle transition. + * @ubound_hs_settle: Upper bound for CSI2 High Speed Settle transition. + * + * From TRM, we have the same calculation for HS Termination signal. + * THS_TERM = ceil( 12.5ns / DDRCLK period ) - 1 + * But for Settle, we use the mid value between the two passed boundaries from + * sensor: + * THS_SETTLE = (Upper bound + Lower bound) / 2 + * + * Always returns 0. + */ +int isp_csi2_calc_phy_cfg0(u32 mipiclk, u32 lbound_hs_settle, + u32 ubound_hs_settle) +{ + struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; + struct isp_csi2_phy_cfg_update *currphy_u = + ¤t_csi2_cfg_update.phy; + u32 tmp, ddrclk = mipiclk >> 1; + + /* Calculate THS_TERM */ + tmp = ddrclk / 80000000; + if ((ddrclk % 80000000) > 0) + tmp++; + currphy->ths_term = tmp - 1; + currphy_u->ths_term = true; + + /* Calculate THS_SETTLE */ + currphy->ths_settle = (ubound_hs_settle + lbound_hs_settle) / 2; + + currphy_u->ths_settle = true; + isp_csi2_phy_update(true); + return 0; +} +EXPORT_SYMBOL(isp_csi2_calc_phy_cfg0); + +/** + * isp_csi2_phy_update - Applies CSI2 D-PHY configuration. + * @force_update: Flag to force rewrite of registers, even if they haven't been + * updated with the isp_csi2_phy_config_*() functions. + * + * It only saves settings when they were previously updated using the + * isp_csi2_phy_config_*() functions, unless the force_update flag is + * set to true. + * Always returns 0. + **/ +int isp_csi2_phy_update(bool force_update) +{ + struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; + struct isp_csi2_phy_cfg_update *currphy_u = + ¤t_csi2_cfg_update.phy; + u32 reg; + + if (update_phy_cfg0 || force_update) { + reg = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2PHY, + ISPCSI2PHY_CFG0); + if (currphy_u->ths_term || force_update) { + reg &= ~ISPCSI2PHY_CFG0_THS_TERM_MASK; + reg |= (currphy->ths_term << + ISPCSI2PHY_CFG0_THS_TERM_SHIFT); + currphy_u->ths_term = false; + } + if (currphy_u->ths_settle || force_update) { + reg &= ~ISPCSI2PHY_CFG0_THS_SETTLE_MASK; + reg |= (currphy->ths_settle << + ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT); + currphy_u->ths_settle = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, + OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG0); + update_phy_cfg0 = false; + } + + if (update_phy_cfg1 || force_update) { + reg = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG1); + if (currphy_u->tclk_term || force_update) { + reg &= ~ISPCSI2PHY_CFG1_TCLK_TERM_MASK; + reg |= (currphy->tclk_term << + ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT); + currphy_u->tclk_term = false; + } + if (currphy_u->tclk_miss || force_update) { + reg &= ~ISPCSI2PHY_CFG1_TCLK_MISS_MASK; + reg |= (currphy->tclk_miss << + ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT); + currphy_u->tclk_miss = false; + } + if (currphy_u->tclk_settle || force_update) { + reg &= ~ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK; + reg |= (currphy->tclk_settle << + ISPCSI2PHY_CFG1_TCLK_SETTLE_SHIFT); + currphy_u->tclk_settle = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, + OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG1); + update_phy_cfg1 = false; + } + return 0; +} + +/** + * isp_csi2_phy_get - Gets CSI2 D-PHY configuration + * + * Gets settings from HW registers and fills in the internal driver memory + * Always returns 0. + **/ +int isp_csi2_phy_get(void) +{ + struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; + struct isp_csi2_phy_cfg_update *currphy_u = + ¤t_csi2_cfg_update.phy; + u32 reg; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2PHY, + ISPCSI2PHY_CFG0); + currphy->ths_term = (reg & ISPCSI2PHY_CFG0_THS_TERM_MASK) >> + ISPCSI2PHY_CFG0_THS_TERM_SHIFT; + currphy_u->ths_term = false; + + currphy->ths_settle = (reg & ISPCSI2PHY_CFG0_THS_SETTLE_MASK) >> + ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT; + currphy_u->ths_settle = false; + update_phy_cfg0 = false; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2PHY, + ISPCSI2PHY_CFG1); + + currphy->tclk_term = (reg & ISPCSI2PHY_CFG1_TCLK_TERM_MASK) >> + ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT; + currphy_u->tclk_term = false; + + currphy->tclk_miss = (reg & ISPCSI2PHY_CFG1_TCLK_MISS_MASK) >> + ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT; + currphy_u->tclk_miss = false; + + currphy->tclk_settle = (reg & ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK) >> + ISPCSI2PHY_CFG1_TCLK_SETTLE_SHIFT; + currphy_u->tclk_settle = false; + + update_phy_cfg1 = false; + return 0; +} + +/** + * isp_csi2_timings_config_forcerxmode - Sets Force Rx mode on stop state count + * @force_rx_mode: Boolean to enable/disable forcing Rx mode in CSI2 receiver + * + * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. + **/ +int isp_csi2_timings_config_forcerxmode(u8 io, bool force_rx_mode) +{ + struct isp_csi2_timings_cfg *currtimings; + struct isp_csi2_timings_cfg_update *currtimings_u; + + if (io < 1 || io > 2) { + printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); + return -EINVAL; + } + + currtimings = ¤t_csi2_cfg.timings[io - 1]; + currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; + if (currtimings->force_rx_mode != force_rx_mode) { + currtimings->force_rx_mode = force_rx_mode; + currtimings_u->force_rx_mode = true; + update_timing = true; + } + return 0; +} + +/** + * isp_csi2_timings_config_stopstate_16x - Sets 16x factor for L3 cycles + * @stop_state_16x: Boolean to use or not use the 16x multiplier for stop count + * + * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. + **/ +int isp_csi2_timings_config_stopstate_16x(u8 io, bool stop_state_16x) +{ + struct isp_csi2_timings_cfg *currtimings; + struct isp_csi2_timings_cfg_update *currtimings_u; + + if (io < 1 || io > 2) { + printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); + return -EINVAL; + } + + currtimings = ¤t_csi2_cfg.timings[io - 1]; + currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; + if (currtimings->stop_state_16x != stop_state_16x) { + currtimings->stop_state_16x = stop_state_16x; + currtimings_u->stop_state_16x = true; + update_timing = true; + } + return 0; +} + +/** + * isp_csi2_timings_config_stopstate_4x - Sets 4x factor for L3 cycles + * @stop_state_4x: Boolean to use or not use the 4x multiplier for stop count + * + * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. + **/ +int isp_csi2_timings_config_stopstate_4x(u8 io, bool stop_state_4x) +{ + struct isp_csi2_timings_cfg *currtimings; + struct isp_csi2_timings_cfg_update *currtimings_u; + + if (io < 1 || io > 2) { + printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); + return -EINVAL; + } + + currtimings = ¤t_csi2_cfg.timings[io - 1]; + currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; + if (currtimings->stop_state_4x != stop_state_4x) { + currtimings->stop_state_4x = stop_state_4x; + currtimings_u->stop_state_4x = true; + update_timing = true; + } + return 0; +} + +/** + * isp_csi2_timings_config_stopstate_cnt - Sets L3 cycles + * @stop_state_counter: Stop state counter value for L3 cycles + * + * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. + **/ +int isp_csi2_timings_config_stopstate_cnt(u8 io, u16 stop_state_counter) +{ + struct isp_csi2_timings_cfg *currtimings; + struct isp_csi2_timings_cfg_update *currtimings_u; + + if (io < 1 || io > 2) { + printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); + return -EINVAL; + } + + currtimings = ¤t_csi2_cfg.timings[io - 1]; + currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; + if (currtimings->stop_state_counter != stop_state_counter) { + currtimings->stop_state_counter = (stop_state_counter & 0x1FFF); + currtimings_u->stop_state_counter = true; + update_timing = true; + } + return 0; +} + +/** + * isp_csi2_timings_update - Applies specified CSI2 timing configuration. + * @io: IO number (1 or 2) which specifies which ComplexIO are we updating + * @force_update: Flag to force rewrite of registers, even if they haven't been + * updated with the isp_csi2_timings_config_*() functions. + * + * It only saves settings when they were previously updated using the + * isp_csi2_timings_config_*() functions, unless the force_update flag is + * set to true. + * Returns 0 if successful, or -EINVAL if invalid IO number is passed. + **/ +int isp_csi2_timings_update(u8 io, bool force_update) +{ + struct isp_csi2_timings_cfg *currtimings; + struct isp_csi2_timings_cfg_update *currtimings_u; + u32 reg; + + if (io < 1 || io > 2) { + printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); + return -EINVAL; + } + + currtimings = ¤t_csi2_cfg.timings[io - 1]; + currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; + + if (update_timing || force_update) { + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_TIMING); + if (currtimings_u->force_rx_mode || force_update) { + reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO_MASK(io); + if (currtimings->force_rx_mode) + reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO_ENABLE + (io); + else + reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO_DISABLE + (io); + currtimings_u->force_rx_mode = false; + } + if (currtimings_u->stop_state_16x || force_update) { + reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO_MASK(io); + if (currtimings->stop_state_16x) + reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO_ENABLE + (io); + else + reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO_DISABLE + (io); + currtimings_u->stop_state_16x = false; + } + if (currtimings_u->stop_state_4x || force_update) { + reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO_MASK(io); + if (currtimings->stop_state_4x) { + reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO_ENABLE + (io); + } else { + reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO_DISABLE + (io); + } + currtimings_u->stop_state_4x = false; + } + if (currtimings_u->stop_state_counter || force_update) { + reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(io); + reg |= currtimings->stop_state_counter << + ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(io); + currtimings_u->stop_state_counter = false; + } + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_TIMING); + update_timing = false; + } + return 0; +} + +/** + * isp_csi2_timings_get - Gets specific CSI2 ComplexIO timing configuration + * @io: IO number (1 or 2) which specifies which ComplexIO are we getting + * + * Gets settings from HW registers and fills in the internal driver memory + * Returns 0 if successful, or -EINVAL if invalid IO number is passed. + **/ +int isp_csi2_timings_get(u8 io) +{ + struct isp_csi2_timings_cfg *currtimings; + struct isp_csi2_timings_cfg_update *currtimings_u; + u32 reg; + + if (io < 1 || io > 2) { + printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); + return -EINVAL; + } + + currtimings = ¤t_csi2_cfg.timings[io - 1]; + currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_TIMING); + if ((reg & ISPCSI2_TIMING_FORCE_RX_MODE_IO_MASK(io)) == + ISPCSI2_TIMING_FORCE_RX_MODE_IO_ENABLE(io)) + currtimings->force_rx_mode = true; + else + currtimings->force_rx_mode = false; + currtimings_u->force_rx_mode = false; + + if ((reg & ISPCSI2_TIMING_STOP_STATE_X16_IO_MASK(io)) == + ISPCSI2_TIMING_STOP_STATE_X16_IO_ENABLE(io)) + currtimings->stop_state_16x = true; + else + currtimings->stop_state_16x = false; + currtimings_u->stop_state_16x = false; + + if ((reg & ISPCSI2_TIMING_STOP_STATE_X4_IO_MASK(io)) == + ISPCSI2_TIMING_STOP_STATE_X4_IO_ENABLE(io)) + currtimings->stop_state_4x = true; + else + currtimings->stop_state_4x = false; + currtimings_u->stop_state_4x = false; + + currtimings->stop_state_counter = (reg & + ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(io)) >> + ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(io); + currtimings_u->stop_state_counter = false; + update_timing = false; + return 0; +} + +/** + * isp_csi2_timings_update_all - Applies specified CSI2 timing configuration. + * @force_update: Flag to force rewrite of registers, even if they haven't been + * updated with the isp_csi2_timings_config_*() functions. + * + * It only saves settings when they were previously updated using the + * isp_csi2_timings_config_*() functions, unless the force_update flag is + * set to true. + * Always returns 0. + **/ +int isp_csi2_timings_update_all(bool force_update) +{ + int i; + + for (i = 1; i < 3; i++) + isp_csi2_timings_update(i, force_update); + return 0; +} + +/** + * isp_csi2_timings_get_all - Gets all CSI2 ComplexIO timing configurations + * + * Always returns 0. + **/ +int isp_csi2_timings_get_all(void) +{ + int i; + + for (i = 1; i < 3; i++) + isp_csi2_timings_get(i); + return 0; +} + +/** + * isp_csi2_isr - CSI2 interrupt handling. + * + * Return -EIO on Transmission error + **/ +int isp_csi2_isr(void) +{ + int retval = 0; + u32 csi2_irqstatus, cpxio1_irqstatus, ctxirqstatus; + + csi2_irqstatus = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_IRQSTATUS); + isp_reg_writel(current_csi2_cfg.dev, csi2_irqstatus, + OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_IRQSTATUS); + + /* Failure Cases */ + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) { + cpxio1_irqstatus = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO1_IRQSTATUS); + isp_reg_writel(current_csi2_cfg.dev, cpxio1_irqstatus, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO1_IRQSTATUS); + dev_dbg(current_csi2_cfg.dev, "CSI2: ComplexIO Error IRQ %x\n", + cpxio1_irqstatus); + retval = -EIO; + } + + if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) { + dev_dbg(current_csi2_cfg.dev, "CSI2 Err:" + " OCP:%d," + " Short_pack:%d," + " ECC:%d," + " CPXIO2:%d," + " FIFO_OVF:%d," + "\n", + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0, + (csi2_irqstatus & + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0); + retval = -EIO; + } + + /* Successful cases */ + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0)) { + ctxirqstatus = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_IRQSTATUS(0)); + isp_reg_writel(current_csi2_cfg.dev, ctxirqstatus, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_IRQSTATUS(0)); + } + + if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ) + dev_dbg(current_csi2_cfg.dev, "CSI2: ECC correction done\n"); + + return retval; +} +EXPORT_SYMBOL(isp_csi2_isr); + +/** + * isp_csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts + **/ +void isp_csi2_irq_complexio1_set(int enable) +{ + u32 reg; + reg = ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMEXIT | + ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMENTER | + ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM5 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL5 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC5 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS5 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS5 | + ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM4 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL4 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC4 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS4 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS4 | + ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM3 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL3 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC3 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS3 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS3 | + ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM2 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL2 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC2 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS2 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS2 | + ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM1 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL1 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC1 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS1 | + ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS1; + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO1_IRQSTATUS); + if (enable) { + reg |= isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO1_IRQENABLE); + } else + reg = 0; + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO1_IRQENABLE); +} +EXPORT_SYMBOL(isp_csi2_irq_complexio1_set); + +/** + * isp_csi2_irq_ctx_set - Enables CSI2 Context IRQs. + * @enable: Enable/disable CSI2 Context interrupts + **/ +void isp_csi2_irq_ctx_set(int enable) +{ + u32 reg; + int i; + + reg = ISPCSI2_CTX_IRQSTATUS_FS_IRQ | ISPCSI2_CTX_IRQSTATUS_FE_IRQ; + for (i = 0; i < 8; i++) { + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_IRQSTATUS(i)); + if (enable) { + isp_reg_or(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_IRQENABLE(i), reg); + } else { + isp_reg_writel(current_csi2_cfg.dev, 0, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_IRQENABLE(i)); + } + } + +} +EXPORT_SYMBOL(isp_csi2_irq_ctx_set); + +/** + * isp_csi2_irq_status_set - Enables CSI2 Status IRQs. + * @enable: Enable/disable CSI2 Status interrupts + **/ +void isp_csi2_irq_status_set(int enable) +{ + u32 reg; + reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | + ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | + ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ | + ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | + ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | + ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ | + ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ | + ISPCSI2_IRQSTATUS_CONTEXT(0); + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_IRQSTATUS); + if (enable) + reg |= isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_IRQENABLE); + else + reg = 0; + + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_IRQENABLE); +} +EXPORT_SYMBOL(isp_csi2_irq_status_set); + +/** + * isp_csi2_irq_all_set - Enable/disable CSI2 interrupts. + * @enable: 0-Disable, 1-Enable. + **/ +void isp_csi2_irq_all_set(int enable) +{ + if (enable) { + isp_csi2_irq_complexio1_set(enable); + isp_csi2_irq_ctx_set(enable); + isp_csi2_irq_status_set(enable); + } else { + isp_csi2_irq_status_set(enable); + isp_csi2_irq_ctx_set(enable); + isp_csi2_irq_complexio1_set(enable); + } + return; +} +EXPORT_SYMBOL(isp_csi2_irq_all_set); + +/** + * isp_csi2_reset - Resets the CSI2 module. + * + * Returns 0 if successful, or -EBUSY if power command didn't respond. + **/ +int isp_csi2_reset(void) +{ + u32 reg; + u8 soft_reset_retries = 0; + int i; + struct device *temp_dev; + + temp_dev = current_csi2_cfg.dev; + memset(¤t_csi2_cfg, 0, sizeof(current_csi2_cfg)); + memset(¤t_csi2_cfg_update, 0, sizeof(current_csi2_cfg_update)); + current_csi2_cfg.dev = temp_dev; + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_SYSCONFIG); + reg |= ISPCSI2_SYSCONFIG_SOFT_RESET_RESET; + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_SYSCONFIG); + + do { + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_SYSSTATUS) & + ISPCSI2_SYSSTATUS_RESET_DONE_MASK; + if (reg == ISPCSI2_SYSSTATUS_RESET_DONE_DONE) + break; + soft_reset_retries++; + if (soft_reset_retries < 5) + udelay(100); + } while (soft_reset_retries < 5); + + if (soft_reset_retries == 5) { + printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); + return -EBUSY; + } + + i = 100; + do { + reg = isp_reg_readl(current_csi2_cfg.dev, + OMAP3_ISP_IOMEM_CSI2PHY, + ISPCSI2PHY_CFG1) & + ISPCSI2PHY_CFG1_RESETDONECTRLCLK_MASK; + if (reg == ISPCSI2PHY_CFG1_RESETDONECTRLCLK_MASK) + break; + udelay(100); + } while (--i > 0); + + if (i == 0) { + printk(KERN_ERR + "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); + return -EBUSY; + } + + reg = isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_SYSCONFIG); + reg &= ~ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK; + reg |= ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART; + reg &= ~ISPCSI2_SYSCONFIG_AUTO_IDLE_MASK; + reg |= ISPCSI2_SYSCONFIG_AUTO_IDLE_AUTO; + isp_reg_writel(current_csi2_cfg.dev, reg, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_SYSCONFIG); + + uses_videoport = false; + update_complexio_cfg1 = false; + update_phy_cfg0 = false; + update_phy_cfg1 = false; + for (i = 0; i < 8; i++) { + update_ctx_ctrl1[i] = false; + update_ctx_ctrl2[i] = false; + update_ctx_ctrl3[i] = false; + } + update_timing = false; + update_ctrl = false; + + isp_csi2_complexio_lanes_get(); + isp_csi2_ctrl_get(); + isp_csi2_ctx_get_all(); + isp_csi2_phy_get(); + isp_csi2_timings_get_all(); + + isp_csi2_complexio_power(ISP_CSI2_POWER_ON); + isp_csi2_complexio_power_autoswitch(true); + + isp_csi2_timings_config_forcerxmode(1, true); + isp_csi2_timings_config_stopstate_cnt(1, 0x1FF); + isp_csi2_timings_update_all(true); + + return 0; +} + +/** + * isp_csi2_enable - Enables the CSI2 module. + * @enable: Enables/disables the CSI2 module. + **/ +void isp_csi2_enable(int enable) +{ + if (enable) { + isp_csi2_ctx_config_enabled(0, true); + isp_csi2_ctx_config_eof_enabled(0, true); + isp_csi2_ctx_config_checksum_enabled(0, true); + isp_csi2_ctx_update(0, false); + + isp_csi2_ctrl_config_ecc_enable(true); + isp_csi2_ctrl_config_if_enable(true); + isp_csi2_ctrl_update(false); + } else { + isp_csi2_ctx_config_enabled(0, false); + isp_csi2_ctx_config_eof_enabled(0, false); + isp_csi2_ctx_config_checksum_enabled(0, false); + isp_csi2_ctx_update(0, false); + + isp_csi2_ctrl_config_ecc_enable(false); + isp_csi2_ctrl_config_if_enable(false); + isp_csi2_ctrl_update(false); + } +} +EXPORT_SYMBOL(isp_csi2_enable); + +/** + * isp_csi2_regdump - Prints CSI2 debug information. + **/ +void isp_csi2_regdump(void) +{ + printk(KERN_DEBUG "-------------Register dump-------------\n"); + + printk(KERN_DEBUG "ISP_CTRL: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL)); + printk(KERN_DEBUG "ISP_TCTRL_CTRL: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_MAIN, + ISP_TCTRL_CTRL)); + printk(KERN_DEBUG "ISPCCDC_SDR_ADDR: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SDR_ADDR)); + printk(KERN_DEBUG "ISPCCDC_SYN_MODE: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_SYN_MODE)); + printk(KERN_DEBUG "ISPCCDC_CFG: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_CFG)); + printk(KERN_DEBUG "ISPCCDC_FMTCFG: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_FMTCFG)); + printk(KERN_DEBUG "ISPCCDC_HSIZE_OFF: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HSIZE_OFF)); + printk(KERN_DEBUG "ISPCCDC_HORZ_INFO: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HORZ_INFO)); + printk(KERN_DEBUG "ISPCCDC_VERT_START: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_START)); + printk(KERN_DEBUG "ISPCCDC_VERT_LINES: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_VERT_LINES)); + + printk(KERN_DEBUG "ISPCSI2_COMPLEXIO_CFG1: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_COMPLEXIO_CFG1)); + printk(KERN_DEBUG "ISPCSI2_SYSSTATUS: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_SYSSTATUS)); + printk(KERN_DEBUG "ISPCSI2_SYSCONFIG: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_SYSCONFIG)); + printk(KERN_DEBUG "ISPCSI2_IRQENABLE: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_IRQENABLE)); + printk(KERN_DEBUG "ISPCSI2_IRQSTATUS: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_IRQSTATUS)); + + printk(KERN_DEBUG "ISPCSI2_CTX_IRQENABLE(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_IRQENABLE(0))); + printk(KERN_DEBUG "ISPCSI2_CTX_IRQSTATUS(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_IRQSTATUS(0))); + printk(KERN_DEBUG "ISPCSI2_TIMING: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_TIMING)); + printk(KERN_DEBUG "ISPCSI2PHY_CFG0: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2PHY, + ISPCSI2PHY_CFG0)); + printk(KERN_DEBUG "ISPCSI2PHY_CFG1: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2PHY, + ISPCSI2PHY_CFG1)); + printk(KERN_DEBUG "ISPCSI2_CTX_CTRL1(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL1(0))); + printk(KERN_DEBUG "ISPCSI2_CTX_CTRL2(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL2(0))); + printk(KERN_DEBUG "ISPCSI2_CTX_CTRL3(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_CTRL3(0))); + printk(KERN_DEBUG "ISPCSI2_CTX_DAT_OFST(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_OFST(0))); + printk(KERN_DEBUG "ISPCSI2_CTX_DAT_PING_ADDR(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_PING_ADDR(0))); + printk(KERN_DEBUG "ISPCSI2_CTX_DAT_PONG_ADDR(0): %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTX_DAT_PONG_ADDR(0))); + printk(KERN_DEBUG "ISPCSI2_CTRL: %x\n", + isp_reg_readl(current_csi2_cfg.dev, OMAP3_ISP_IOMEM_CSI2A, + ISPCSI2_CTRL)); + printk(KERN_DEBUG "---------------------------------------\n"); +} + +/** + * ispcsi2_save_context - Saves the values of the CSI1 module registers + **/ +void ispcsi2_save_context(struct device *dev) +{ + printk(KERN_DEBUG "Saving csi2 context\n"); + isp_save_context(dev, ispcsi2_reg_list); +} +EXPORT_SYMBOL(ispcsi2_save_context); + +/** + * ispcsi2_restore_context - Restores the values of the CSI2 module registers + **/ +void ispcsi2_restore_context(struct device *dev) +{ + printk(KERN_DEBUG "Restoring csi2 context\n"); + isp_restore_context(dev, ispcsi2_reg_list); +} +EXPORT_SYMBOL(ispcsi2_restore_context); + +/** + * isp_csi2_cleanup - Routine for module driver cleanup + **/ +void isp_csi2_cleanup(struct device *dev) +{ + return; +} + +/** + * isp_csi2_init - Routine for module driver init + **/ +int __init isp_csi2_init(struct device *dev) +{ + int i; + + update_complexio_cfg1 = false; + update_phy_cfg0 = false; + update_phy_cfg1 = false; + for (i = 0; i < 8; i++) { + update_ctx_ctrl1[i] = false; + update_ctx_ctrl2[i] = false; + update_ctx_ctrl3[i] = false; + } + update_timing = false; + update_ctrl = false; + + memset(¤t_csi2_cfg, 0, sizeof(current_csi2_cfg)); + memset(¤t_csi2_cfg_update, 0, sizeof(current_csi2_cfg_update)); + current_csi2_cfg.dev = dev; + return 0; +} + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("ISP CSI2 Receiver Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/isp/ispcsi2.h b/drivers/media/video/isp/ispcsi2.h new file mode 100644 index 00000000000..5982e8c8fe7 --- /dev/null +++ b/drivers/media/video/isp/ispcsi2.h @@ -0,0 +1,240 @@ +/* + * ispcsi2.h + * + * Copyright (C) 2009 Texas Instruments. + * + * Contributors: + * Sergio Aguirre + * Dominic Curran + * + * 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 OMAP_ISP_CSI2_API_H +#define OMAP_ISP_CSI2_API_H +#include + +#define ISP_CSI2_LANE_OFF 0 +#define ISP_CSI2_LANE_ON 1 + +enum isp_csi2_irqevents { + OCP_ERR_IRQ = 0x4000, + SHORT_PACKET_IRQ = 0x2000, + ECC_CORRECTION_IRQ = 0x1000, + ECC_NO_CORRECTION_IRQ = 0x800, + COMPLEXIO2_ERR_IRQ = 0x400, + COMPLEXIO1_ERR_IRQ = 0x200, + FIFO_OVF_IRQ = 0x100, + CONTEXT7 = 0x80, + CONTEXT6 = 0x40, + CONTEXT5 = 0x20, + CONTEXT4 = 0x10, + CONTEXT3 = 0x8, + CONTEXT2 = 0x4, + CONTEXT1 = 0x2, + CONTEXT0 = 0x1, +}; + +enum isp_csi2_ctx_irqevents { + CTX_ECC_CORRECTION = 0x100, + CTX_LINE_NUMBER = 0x80, + CTX_FRAME_NUMBER = 0x40, + CTX_CS = 0x20, + CTX_LE = 0x8, + CTX_LS = 0x4, + CTX_FE = 0x2, + CTX_FS = 0x1, +}; + +enum isp_csi2_power_cmds { + ISP_CSI2_POWER_OFF, + ISP_CSI2_POWER_ON, + ISP_CSI2_POWER_ULPW, +}; + +enum isp_csi2_frame_mode { + ISP_CSI2_FRAME_IMMEDIATE, + ISP_CSI2_FRAME_AFTERFEC, +}; + +struct csi2_lanecfg { + u8 pos; + u8 pol; + u8 state; /*Current state - 1-Used 0-Unused */ +}; + +struct isp_csi2_lanes_cfg { + struct csi2_lanecfg data[4]; + struct csi2_lanecfg clk; +}; + +struct isp_csi2_lanes_cfg_update { + bool data[4]; + bool clk; +}; + +struct isp_csi2_phy_cfg { + u8 ths_term; + u8 ths_settle; + u8 tclk_term; + unsigned tclk_miss:1; + u8 tclk_settle; +}; + +struct isp_csi2_phy_cfg_update { + bool ths_term; + bool ths_settle; + bool tclk_term; + bool tclk_miss; + bool tclk_settle; +}; + +struct isp_csi2_ctx_cfg { + u8 virtual_id; + u8 frame_count; + struct v4l2_pix_format format; + u16 alpha; + u16 data_offset; + u32 ping_addr; + u32 pong_addr; + bool eof_enabled; + bool eol_enabled; + bool checksum_enabled; + bool enabled; +}; + +struct isp_csi2_ctx_cfg_update { + bool virtual_id; + bool frame_count; + bool format; + bool alpha; + bool data_offset; + bool ping_addr; + bool pong_addr; + bool eof_enabled; + bool eol_enabled; + bool checksum_enabled; + bool enabled; +}; + +struct isp_csi2_timings_cfg { + bool force_rx_mode; + bool stop_state_16x; + bool stop_state_4x; + u16 stop_state_counter; +}; + +struct isp_csi2_timings_cfg_update { + bool force_rx_mode; + bool stop_state_16x; + bool stop_state_4x; + bool stop_state_counter; +}; + +struct isp_csi2_ctrl_cfg { + bool vp_clk_enable; + bool vp_only_enable; + u8 vp_out_ctrl; + bool debug_enable; + u8 burst_size; + enum isp_csi2_frame_mode frame_mode; + bool ecc_enable; + bool secure_mode; + bool if_enable; +}; + +struct isp_csi2_ctrl_cfg_update { + bool vp_clk_enable; + bool vp_only_enable; + bool vp_out_ctrl; + bool debug_enable; + bool burst_size; + bool frame_mode; + bool ecc_enable; + bool secure_mode; + bool if_enable; +}; + +struct isp_csi2_cfg { + struct isp_csi2_lanes_cfg lanes; + struct isp_csi2_phy_cfg phy; + struct isp_csi2_ctx_cfg contexts[8]; + struct isp_csi2_timings_cfg timings[2]; + struct isp_csi2_ctrl_cfg ctrl; + struct device *dev; +}; + +struct isp_csi2_cfg_update { + struct isp_csi2_lanes_cfg_update lanes; + struct isp_csi2_phy_cfg_update phy; + struct isp_csi2_ctx_cfg_update contexts[8]; + struct isp_csi2_timings_cfg_update timings[2]; + struct isp_csi2_ctrl_cfg_update ctrl; +}; + +int isp_csi2_complexio_lanes_config(struct isp_csi2_lanes_cfg *reqcfg); +int isp_csi2_complexio_lanes_update(bool force_update); +int isp_csi2_complexio_lanes_count(int cnt); +int isp_csi2_complexio_lanes_get(void); +int isp_csi2_complexio_power_autoswitch(bool enable); +int isp_csi2_complexio_power(enum isp_csi2_power_cmds power_cmd); +int isp_csi2_ctrl_config_frame_mode(enum isp_csi2_frame_mode frame_mode); +int isp_csi2_ctrl_config_vp_clk_enable(bool vp_clk_enable); +int isp_csi2_ctrl_config_vp_only_enable(bool vp_only_enable); +int isp_csi2_ctrl_config_debug_enable(bool debug_enable); +int isp_csi2_ctrl_config_burst_size(u8 burst_size); +int isp_csi2_ctrl_config_ecc_enable(bool ecc_enable); +int isp_csi2_ctrl_config_secure_mode(bool secure_mode); +int isp_csi2_ctrl_config_if_enable(bool if_enable); +int isp_csi2_ctrl_config_vp_out_ctrl(u8 vp_out_ctrl); +int isp_csi2_ctrl_update(bool force_update); +int isp_csi2_ctrl_get(void); +int isp_csi2_ctx_config_virtual_id(u8 ctxnum, u8 virtual_id); +int isp_csi2_ctx_config_frame_count(u8 ctxnum, u8 frame_count); +int isp_csi2_ctx_config_format(u8 ctxnum, u32 pixformat); +int isp_csi2_ctx_config_alpha(u8 ctxnum, u16 alpha); +int isp_csi2_ctx_config_data_offset(u8 ctxnum, u16 data_offset); +int isp_csi2_ctx_config_ping_addr(u8 ctxnum, u32 ping_addr); +int isp_csi2_ctx_config_pong_addr(u8 ctxnum, u32 pong_addr); +int isp_csi2_ctx_config_eof_enabled(u8 ctxnum, bool eof_enabled); +int isp_csi2_ctx_config_eol_enabled(u8 ctxnum, bool eol_enabled); +int isp_csi2_ctx_config_checksum_enabled(u8 ctxnum, bool checksum_enabled); +int isp_csi2_ctx_config_enabled(u8 ctxnum, bool enabled); +int isp_csi2_ctx_update(u8 ctxnum, bool force_update); +int isp_csi2_ctx_get(u8 ctxnum); +int isp_csi2_ctx_update_all(bool force_update); +int isp_csi2_ctx_get_all(void); +int isp_csi2_phy_config(struct isp_csi2_phy_cfg *desiredphyconfig); +int isp_csi2_calc_phy_cfg0(u32 mipiclk, u32 lbound_hs_settle, + u32 ubound_hs_settle); +int isp_csi2_phy_update(bool force_update); +int isp_csi2_phy_get(void); +int isp_csi2_timings_config_forcerxmode(u8 io, bool force_rx_mode); +int isp_csi2_timings_config_stopstate_16x(u8 io, bool stop_state_16x); +int isp_csi2_timings_config_stopstate_4x(u8 io, bool stop_state_4x); +int isp_csi2_timings_config_stopstate_cnt(u8 io, u16 stop_state_counter); +int isp_csi2_timings_update(u8 io, bool force_update); +int isp_csi2_timings_get(u8 io); +int isp_csi2_timings_update_all(bool force_update); +int isp_csi2_timings_get_all(void); +void isp_csi2_irq_complexio1_set(int enable); +void isp_csi2_irq_ctx_set(int enable); +void isp_csi2_irq_status_set(int enable); +void isp_csi2_irq_all_set(int enable); + +int isp_csi2_isr(void); +int isp_csi2_reset(void); +void isp_csi2_enable(int enable); +void isp_csi2_regdump(void); + +void ispcsi2_save_context(struct device *dev); +void ispcsi2_restore_context(struct device *dev); + +#endif /* OMAP_ISP_CSI2_H */ + diff --git a/drivers/media/video/isp/isph3a.c b/drivers/media/video/isp/isph3a.c new file mode 100644 index 00000000000..a7b5eee65d0 --- /dev/null +++ b/drivers/media/video/isp/isph3a.c @@ -0,0 +1,618 @@ +/* + * isph3a.c + * + * H3A module for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * + * 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 +#include + +#include "isp.h" + +/* Structure for saving/restoring h3a module registers */ +static struct isp_reg isph3a_reg_list[] = { + {OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, 0}, /* Should be the first one */ + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINSTART, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWSUBWIN, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAXSTART, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFBUFST, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF010, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF032, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF054, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF076, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF098, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF110, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF132, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF154, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF176, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF198, 0}, + {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010, 0}, + {0, ISP_TOK_TERM, 0} +}; + +static void isph3a_aewb_update_regs(struct isp_h3a_device *isp_h3a); +static void isph3a_print_status(struct isp_h3a_device *isp_h3a); + +void __isph3a_aewb_enable(struct isp_h3a_device *isp_h3a, u8 enable) +{ + if (enable) { + isp_h3a->regs.pcr |= ISPH3A_PCR_AEW_EN; + DPRINTK_ISPH3A(" H3A enabled \n"); + } else { + isp_h3a->regs.pcr &= ~ISPH3A_PCR_AEW_EN; + DPRINTK_ISPH3A(" H3A disabled \n"); + } + isp_h3a->update = 1; + isp_h3a->aewb_config_local.aewb_enable = enable; + isph3a_aewb_update_regs(isp_h3a); +} + +/** + * isph3a_aewb_enable - Enables AE, AWB engine in the H3A module. + * @enable: 1 - Enables the AE & AWB engine. + * + * Client should configure all the AE & AWB registers in H3A before this. + **/ +void isph3a_aewb_enable(struct isp_h3a_device *isp_h3a, u8 enable) +{ + unsigned long irqflags; + + spin_lock_irqsave(isp_h3a->lock, irqflags); + + __isph3a_aewb_enable(isp_h3a, enable); + isp_h3a->pm_state = enable; + + spin_unlock_irqrestore(isp_h3a->lock, irqflags); +} + +/** + * isph3a_aewb_suspend - Suspend AE, AWB engine in the H3A module. + **/ +void isph3a_aewb_suspend(struct isp_h3a_device *isp_h3a) +{ + unsigned long flags; + + spin_lock_irqsave(isp_h3a->lock, flags); + + if (isp_h3a->pm_state) + __isph3a_aewb_enable(isp_h3a, 0); + + spin_unlock_irqrestore(isp_h3a->lock, flags); +} + +/** + * isph3a_aewb_resume - Resume AE, AWB engine in the H3A module. + **/ +void isph3a_aewb_resume(struct isp_h3a_device *isp_h3a) +{ + unsigned long flags; + + spin_lock_irqsave(isp_h3a->lock, flags); + + if (isp_h3a->pm_state) + __isph3a_aewb_enable(isp_h3a, 1); + + spin_unlock_irqrestore(isp_h3a->lock, flags); +} + +int isph3a_aewb_busy(struct isp_h3a_device *isp_h3a) +{ + return isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) + & ISPH3A_PCR_BUSYAEAWB; +} + +/** + * isph3a_update_wb - Updates WB parameters. + * + * Needs to be called when no ISP Preview processing is taking place. + **/ +void isph3a_update_wb(struct isp_h3a_device *isp_h3a) +{ + struct isp_device *isp = dev_get_drvdata(isp_h3a->dev); + + if (isp_h3a->wb_update) { + /* FIXME: Get the preview crap out of here!!! */ + isppreview_config_whitebalance(&isp->isp_prev, + isp_h3a->h3awb_update); + isp_h3a->wb_update = 0; + } + return; +} +EXPORT_SYMBOL(isph3a_update_wb); + +/** + * isph3a_aewb_update_regs - Helper function to update h3a registers. + **/ +static void isph3a_aewb_update_regs(struct isp_h3a_device *isp_h3a) +{ + u32 pcr; + + if (!isp_h3a->update) + return; + + pcr = isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); + isp_reg_writel(isp_h3a->dev, (pcr & ~ISPH3A_PCR_AEW_MASK) | + (isp_h3a->regs.pcr & ~ISPH3A_PCR_AEW_EN), + OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); + if (!isp_h3a->aewb_config_local.aewb_enable) + return; + + if (isph3a_aewb_busy(isp_h3a)) { + isp_reg_writel(isp_h3a->dev, (pcr & ~ISPH3A_PCR_AEW_MASK) | + isp_h3a->regs.pcr, + OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); + return; + } + + isp_reg_writel(isp_h3a->dev, isp_h3a->regs.win1, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWWIN1); + isp_reg_writel(isp_h3a->dev, isp_h3a->regs.start, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWINSTART); + isp_reg_writel(isp_h3a->dev, isp_h3a->regs.blk, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWINBLK); + isp_reg_writel(isp_h3a->dev, isp_h3a->regs.subwin, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWSUBWIN); + isp_reg_writel(isp_h3a->dev, (pcr & ~ISPH3A_PCR_AEW_MASK) | + isp_h3a->regs.pcr, + OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); + + isp_h3a->update = 0; +} + +/** + * isph3a_aewb_stats_available - Check for stats available of specified frame. + * @aewbdata: Pointer to return AE AWB statistics data + * + * Returns 0 if successful, or -1 if statistics are unavailable. + **/ +static int isph3a_aewb_get_stats(struct isp_h3a_device *isp_h3a, + struct isph3a_aewb_data *aewbdata) +{ + unsigned long irqflags; + struct ispstat_buffer *buf; + + buf = ispstat_buf_get(&isp_h3a->stat, + (void *)aewbdata->h3a_aewb_statistics_buf, + aewbdata->frame_number); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + spin_lock_irqsave(isp_h3a->lock, irqflags); + + aewbdata->ts = buf->ts; + aewbdata->config_counter = buf->config_counter; + aewbdata->frame_number = buf->frame_number; + + spin_unlock_irqrestore(isp_h3a->lock, irqflags); + + ispstat_buf_release(&isp_h3a->stat); + + return 0; +} + +/** + * isph3a_aewb_isr - Callback from ISP driver for H3A AEWB interrupt. + * @status: IRQ0STATUS in case of MMU error, 0 for H3A interrupt. + * @arg1: Not used as of now. + * @arg2: Not used as of now. + */ +void isph3a_aewb_isr(struct isp_h3a_device *isp_h3a) +{ + unsigned long irqflags; + struct ispstat_buffer *buf; + + buf = ispstat_buf_next(&isp_h3a->stat); + + isp_reg_writel(isp_h3a->dev, buf->iommu_addr, + OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); + + spin_lock_irqsave(isp_h3a->lock, irqflags); + isph3a_aewb_update_regs(isp_h3a); + spin_unlock_irqrestore(isp_h3a->lock, irqflags); + + isph3a_update_wb(isp_h3a); +} + +static int isph3a_aewb_validate_params(struct isp_h3a_device *isp_h3a, + struct isph3a_aewb_config *user_cfg) +{ + if (unlikely(user_cfg->saturation_limit > MAX_SATURATION_LIM)) { + dev_info(isp_h3a->dev, "h3a: Invalid Saturation_limit: %d\n", + user_cfg->saturation_limit); + return -EINVAL; + } + if (unlikely(user_cfg->win_height < MIN_WIN_H || + user_cfg->win_height > MAX_WIN_H || + user_cfg->win_height & 0x01)) { + dev_info(isp_h3a->dev, "h3a: Invalid window height: %d\n", + user_cfg->win_height); + return -EINVAL; + } + if (unlikely(user_cfg->win_width < MIN_WIN_W || + user_cfg->win_width > MAX_WIN_W || + user_cfg->win_width & 0x01)) { + dev_info(isp_h3a->dev, "h3a: Invalid window width: %d\n", + user_cfg->win_width); + return -EINVAL; + } + if (unlikely(user_cfg->ver_win_count < 1 || + user_cfg->ver_win_count > MAX_WINVC)) { + dev_info(isp_h3a->dev, + "h3a: Invalid vertical window count: %d\n", + user_cfg->ver_win_count); + return -EINVAL; + } + if (unlikely(user_cfg->hor_win_count < 1 || + user_cfg->hor_win_count > MAX_WINHC)) { + dev_info(isp_h3a->dev, + "h3a: Invalid horizontal window count: %d\n", + user_cfg->hor_win_count); + return -EINVAL; + } + if (unlikely(user_cfg->ver_win_start > MAX_WINSTART)) { + dev_info(isp_h3a->dev, + "h3a: Invalid vertical window start: %d\n", + user_cfg->ver_win_start); + return -EINVAL; + } + if (unlikely(user_cfg->hor_win_start > MAX_WINSTART)) { + dev_info(isp_h3a->dev, + "h3a: Invalid horizontal window start: %d\n", + user_cfg->hor_win_start); + return -EINVAL; + } + if (unlikely(user_cfg->blk_ver_win_start > MAX_WINSTART)) { + dev_info(isp_h3a->dev, + "h3a: Invalid black vertical window start: %d\n", + user_cfg->blk_ver_win_start); + return -EINVAL; + } + if (unlikely(user_cfg->blk_win_height < MIN_WIN_H || + user_cfg->blk_win_height > MAX_WIN_H || + user_cfg->blk_win_height & 0x01)) { + dev_info(isp_h3a->dev, "h3a: Invalid black window height: %d\n", + user_cfg->blk_win_height); + return -EINVAL; + } + if (unlikely(user_cfg->subsample_ver_inc < MIN_SUB_INC || + user_cfg->subsample_ver_inc > MAX_SUB_INC || + user_cfg->subsample_ver_inc & 0x01)) { + dev_info(isp_h3a->dev, + "h3a: Invalid vertical subsample increment: %d\n", + user_cfg->subsample_ver_inc); + return -EINVAL; + } + if (unlikely(user_cfg->subsample_hor_inc < MIN_SUB_INC || + user_cfg->subsample_hor_inc > MAX_SUB_INC || + user_cfg->subsample_hor_inc & 0x01)) { + dev_info(isp_h3a->dev, + "h3a: Invalid horizontal subsample increment: %d\n", + user_cfg->subsample_hor_inc); + return -EINVAL; + } + + return 0; +} + +/** + * isph3a_aewb_set_params - Helper function to check & store user given params. + * @user_cfg: Pointer to AE and AWB parameters struct. + * + * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to + * program them during ISR. + * + * Returns 0 if successful, or -EINVAL if any of the parameters are invalid. + **/ +static void isph3a_aewb_set_params(struct isp_h3a_device *isp_h3a, + struct isph3a_aewb_config *user_cfg) +{ + if (isp_h3a->aewb_config_local.saturation_limit != + user_cfg->saturation_limit) { + WRITE_SAT_LIM(isp_h3a->regs.pcr, user_cfg->saturation_limit); + isp_h3a->aewb_config_local.saturation_limit = + user_cfg->saturation_limit; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.alaw_enable != user_cfg->alaw_enable) { + WRITE_ALAW(isp_h3a->regs.pcr, user_cfg->alaw_enable); + isp_h3a->aewb_config_local.alaw_enable = user_cfg->alaw_enable; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.win_height != user_cfg->win_height) { + WRITE_WIN_H(isp_h3a->regs.win1, user_cfg->win_height); + isp_h3a->aewb_config_local.win_height = user_cfg->win_height; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.win_width != user_cfg->win_width) { + WRITE_WIN_W(isp_h3a->regs.win1, user_cfg->win_width); + isp_h3a->aewb_config_local.win_width = user_cfg->win_width; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.ver_win_count != + user_cfg->ver_win_count) { + WRITE_VER_C(isp_h3a->regs.win1, user_cfg->ver_win_count); + isp_h3a->aewb_config_local.ver_win_count = + user_cfg->ver_win_count; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.hor_win_count != + user_cfg->hor_win_count) { + WRITE_HOR_C(isp_h3a->regs.win1, user_cfg->hor_win_count); + isp_h3a->aewb_config_local.hor_win_count = + user_cfg->hor_win_count; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.ver_win_start != + user_cfg->ver_win_start) { + WRITE_VER_WIN_ST(isp_h3a->regs.start, user_cfg->ver_win_start); + isp_h3a->aewb_config_local.ver_win_start = + user_cfg->ver_win_start; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.hor_win_start != + user_cfg->hor_win_start) { + WRITE_HOR_WIN_ST(isp_h3a->regs.start, user_cfg->hor_win_start); + isp_h3a->aewb_config_local.hor_win_start = + user_cfg->hor_win_start; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.blk_ver_win_start != + user_cfg->blk_ver_win_start) { + WRITE_BLK_VER_WIN_ST(isp_h3a->regs.blk, + user_cfg->blk_ver_win_start); + isp_h3a->aewb_config_local.blk_ver_win_start = + user_cfg->blk_ver_win_start; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.blk_win_height != + user_cfg->blk_win_height) { + WRITE_BLK_WIN_H(isp_h3a->regs.blk, user_cfg->blk_win_height); + isp_h3a->aewb_config_local.blk_win_height = + user_cfg->blk_win_height; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.subsample_ver_inc != + user_cfg->subsample_ver_inc) { + WRITE_SUB_VER_INC(isp_h3a->regs.subwin, + user_cfg->subsample_ver_inc); + isp_h3a->aewb_config_local.subsample_ver_inc = + user_cfg->subsample_ver_inc; + isp_h3a->update = 1; + } + + if (isp_h3a->aewb_config_local.subsample_hor_inc != + user_cfg->subsample_hor_inc) { + WRITE_SUB_HOR_INC(isp_h3a->regs.subwin, + user_cfg->subsample_hor_inc); + isp_h3a->aewb_config_local.subsample_hor_inc = + user_cfg->subsample_hor_inc; + isp_h3a->update = 1; + } +} + +/** + * isph3a_aewb_configure - Configure AEWB regs, enable/disable H3A engine. + * @aewbcfg: Pointer to AEWB config structure. + * + * Returns 0 if successful, -EINVAL if aewbcfg pointer is NULL, -ENOMEM if + * was unable to allocate memory for the buffer, of other errors if H3A + * callback is not set or the parameters for AEWB are invalid. + **/ +int isph3a_aewb_configure(struct isp_h3a_device *isp_h3a, + struct isph3a_aewb_config *aewbcfg) +{ + int ret = 0; + int win_count = 0; + unsigned int buf_size; + unsigned long irqflags; + struct ispstat_buffer *buf; + + if (NULL == aewbcfg) { + dev_info(isp_h3a->dev, "h3a: Null argument in configuration\n"); + return -EINVAL; + } + + ret = isph3a_aewb_validate_params(isp_h3a, aewbcfg); + if (ret) + return ret; + + /* FIXME: This win_count handling looks really fishy. */ + win_count = aewbcfg->ver_win_count * aewbcfg->hor_win_count; + win_count += aewbcfg->hor_win_count; + ret = win_count / 8; + win_count += win_count % 8 ? 1 : 0; + win_count += ret; + + buf_size = win_count * AEWB_PACKET_SIZE; + + ret = ispstat_bufs_alloc(&isp_h3a->stat, buf_size); + if (ret) + return ret; + + buf = ispstat_buf_next(&isp_h3a->stat); + + spin_lock_irqsave(isp_h3a->lock, irqflags); + + isp_h3a->win_count = win_count; + + isph3a_aewb_set_params(isp_h3a, aewbcfg); + isp_reg_writel(isp_h3a->dev, buf->iommu_addr, + OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); + spin_unlock_irqrestore(isp_h3a->lock, irqflags); + isph3a_aewb_enable(isp_h3a, aewbcfg->aewb_enable); + isph3a_print_status(isp_h3a); + + + return 0; +} +EXPORT_SYMBOL(isph3a_aewb_configure); + +/** + * isph3a_aewb_request_statistics - REquest statistics and update gains in AEWB + * @aewbdata: Pointer to return AE AWB statistics data. + * + * This API allows the user to update White Balance gains, as well as + * exposure time and analog gain. It is also used to request frame + * statistics. + * + * Returns 0 if successful, -EINVAL when H3A engine is not enabled, or other + * errors when setting gains. + **/ +int isph3a_aewb_request_statistics(struct isp_h3a_device *isp_h3a, + struct isph3a_aewb_data *aewbdata) +{ + unsigned long irqflags; + int ret = 0; + + if (!isp_h3a->aewb_config_local.aewb_enable) { + dev_err(isp_h3a->dev, "h3a: engine not enabled\n"); + return -EINVAL; + } + + DPRINTK_ISPH3A("isph3a_aewb_request_statistics: Enter " + "(frame req. => %d, current frame => %d," + "update => %d)\n", + aewbdata->frame_number, isp_h3a->stat.frame_number, + aewbdata->update); + DPRINTK_ISPH3A("User data received: \n"); + DPRINTK_ISPH3A("Digital gain = 0x%04x\n", aewbdata->dgain); + DPRINTK_ISPH3A("WB gain b *= 0x%04x\n", aewbdata->wb_gain_b); + DPRINTK_ISPH3A("WB gain r *= 0x%04x\n", aewbdata->wb_gain_r); + DPRINTK_ISPH3A("WB gain gb = 0x%04x\n", aewbdata->wb_gain_gb); + DPRINTK_ISPH3A("WB gain gr = 0x%04x\n", aewbdata->wb_gain_gr); + + spin_lock_irqsave(isp_h3a->lock, irqflags); + + if (aewbdata->update & SET_DIGITAL_GAIN) + isp_h3a->h3awb_update.dgain = (u16)aewbdata->dgain; + if (aewbdata->update & SET_COLOR_GAINS) { + isp_h3a->h3awb_update.coef0 = (u8)aewbdata->wb_gain_r; + isp_h3a->h3awb_update.coef1 = (u8)aewbdata->wb_gain_gr; + isp_h3a->h3awb_update.coef2 = (u8)aewbdata->wb_gain_gb; + isp_h3a->h3awb_update.coef3 = (u8)aewbdata->wb_gain_b; + } + if (aewbdata->update & (SET_COLOR_GAINS | SET_DIGITAL_GAIN)) + isp_h3a->wb_update = 1; + + spin_unlock_irqrestore(isp_h3a->lock, irqflags); + + if (aewbdata->update & REQUEST_STATISTICS) + ret = isph3a_aewb_get_stats(isp_h3a, aewbdata); + aewbdata->curr_frame = isp_h3a->stat.frame_number; + + DPRINTK_ISPH3A("isph3a_aewb_request_statistics: " + "aewbdata->h3a_aewb_statistics_buf => %p\n", + aewbdata->h3a_aewb_statistics_buf); + + return ret; +} +EXPORT_SYMBOL(isph3a_aewb_request_statistics); + +/** + * isph3a_aewb_init - Module Initialisation. + * + * Always returns 0. + **/ +int __init isph3a_aewb_init(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_h3a_device *isp_h3a = &isp->isp_h3a; + + isp_h3a->dev = dev; + isp_h3a->lock = &isp->h3a_lock; + isp_h3a->aewb_config_local.saturation_limit = AEWB_SATURATION_LIMIT; + ispstat_init(dev, &isp_h3a->stat, H3A_MAX_BUFF, MAX_FRAME_COUNT); + + return 0; +} + +/** + * isph3a_aewb_cleanup - Module exit. + **/ +void isph3a_aewb_cleanup(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + ispstat_free(&isp->isp_h3a.stat); +} + +/** + * isph3a_print_status - Debug print. Values of H3A related registers. + **/ +static void isph3a_print_status(struct isp_h3a_device *isp_h3a) +{ + DPRINTK_ISPH3A("ISPH3A_PCR = 0x%08x\n", + isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, + ISPH3A_PCR)); + DPRINTK_ISPH3A("ISPH3A_AEWWIN1 = 0x%08x\n", + isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWWIN1)); + DPRINTK_ISPH3A("ISPH3A_AEWINSTART = 0x%08x\n", + isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWINSTART)); + DPRINTK_ISPH3A("ISPH3A_AEWINBLK = 0x%08x\n", + isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWINBLK)); + DPRINTK_ISPH3A("ISPH3A_AEWSUBWIN = 0x%08x\n", + isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWSUBWIN)); + DPRINTK_ISPH3A("ISPH3A_AEWBUFST = 0x%08x\n", + isp_reg_readl(isp_h3a->dev, OMAP3_ISP_IOMEM_H3A, + ISPH3A_AEWBUFST)); + DPRINTK_ISPH3A("stats windows = %d\n", isp_h3a->win_count); + DPRINTK_ISPH3A("stats buf size = %d\n", isp_h3a->stat.buf_size); +} + +/** + * isph3a_save_context - Saves the values of the h3a module registers. + **/ +void isph3a_save_context(struct device *dev) +{ + DPRINTK_ISPH3A(" Saving context\n"); + isp_save_context(dev, isph3a_reg_list); + /* Avoid enable during restore ctx */ + isph3a_reg_list[0].val &= ~ISPH3A_PCR_AEW_EN; +} +EXPORT_SYMBOL(isph3a_save_context); + +/** + * isph3a_restore_context - Restores the values of the h3a module registers. + **/ +void isph3a_restore_context(struct device *dev) +{ + DPRINTK_ISPH3A(" Restoring context\n"); + isp_restore_context(dev, isph3a_reg_list); +} +EXPORT_SYMBOL(isph3a_restore_context); diff --git a/drivers/media/video/isp/isph3a.h b/drivers/media/video/isp/isph3a.h new file mode 100644 index 00000000000..6528ced24b8 --- /dev/null +++ b/drivers/media/video/isp/isph3a.h @@ -0,0 +1,154 @@ +/* + * isph3a.h + * + * Include file for H3A module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * + * 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 OMAP_ISP_H3A_H +#define OMAP_ISP_H3A_H + +#include + +#define AEWB_PACKET_SIZE 16 +#define H3A_MAX_BUFF 5 +#define AEWB_SATURATION_LIMIT 0x3FF + +/* Flags for changed registers */ +#define PCR_CHNG (1 << 0) +#define AEWWIN1_CHNG (1 << 1) +#define AEWINSTART_CHNG (1 << 2) +#define AEWINBLK_CHNG (1 << 3) +#define AEWSUBWIN_CHNG (1 << 4) +#define PRV_WBDGAIN_CHNG (1 << 5) +#define PRV_WBGAIN_CHNG (1 << 6) + +/* ISPH3A REGISTERS bits */ +#define ISPH3A_PCR_AF_EN (1 << 0) +#define ISPH3A_PCR_AF_ALAW_EN (1 << 1) +#define ISPH3A_PCR_AF_MED_EN (1 << 2) +#define ISPH3A_PCR_AF_BUSY (1 << 15) +#define ISPH3A_PCR_AEW_EN (1 << 16) +#define ISPH3A_PCR_AEW_ALAW_EN (1 << 17) +#define ISPH3A_PCR_AEW_BUSY (1 << 18) +#define ISPH3A_PCR_AEW_MASK (ISPH3A_PCR_AEW_EN | ISPH3A_PCR_AEW_ALAW_EN | \ + ISPH3A_PCR_AEW_AVE2LMT_MASK) + +#define WRITE_SAT_LIM(reg, sat_limit) \ + (reg = (reg & (~(ISPH3A_PCR_AEW_AVE2LMT_MASK))) \ + | (sat_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT)) + +#define WRITE_ALAW(reg, alaw_en) \ + (reg = (reg & (~(ISPH3A_PCR_AEW_ALAW_EN))) \ + | ((alaw_en & ISPH3A_PCR_AF_ALAW_EN) \ + << ISPH3A_PCR_AEW_ALAW_EN_SHIFT)) + +#define WRITE_WIN_H(reg, height) \ + (reg = (reg & (~(ISPH3A_AEWWIN1_WINH_MASK))) \ + | (((height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT)) + +#define WRITE_WIN_W(reg, width) \ + (reg = (reg & (~(ISPH3A_AEWWIN1_WINW_MASK))) \ + | (((width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT)) + +#define WRITE_VER_C(reg, ver_count) \ + (reg = (reg & ~(ISPH3A_AEWWIN1_WINVC_MASK)) \ + | ((ver_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT)) + +#define WRITE_HOR_C(reg, hor_count) \ + (reg = (reg & ~(ISPH3A_AEWWIN1_WINHC_MASK)) \ + | ((hor_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT)) + +#define WRITE_VER_WIN_ST(reg, ver_win_st) \ + (reg = (reg & ~(ISPH3A_AEWINSTART_WINSV_MASK)) \ + | (ver_win_st << ISPH3A_AEWINSTART_WINSV_SHIFT)) + +#define WRITE_HOR_WIN_ST(reg, hor_win_st) \ + (reg = (reg & ~(ISPH3A_AEWINSTART_WINSH_MASK)) \ + | (hor_win_st << ISPH3A_AEWINSTART_WINSH_SHIFT)) + +#define WRITE_BLK_VER_WIN_ST(reg, blk_win_st) \ + (reg = (reg & ~(ISPH3A_AEWINBLK_WINSV_MASK)) \ + | (blk_win_st << ISPH3A_AEWINBLK_WINSV_SHIFT)) + +#define WRITE_BLK_WIN_H(reg, height) \ + (reg = (reg & ~(ISPH3A_AEWINBLK_WINH_MASK)) \ + | (((height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT)) + +#define WRITE_SUB_VER_INC(reg, sub_ver_inc) \ + (reg = (reg & ~(ISPH3A_AEWSUBWIN_AEWINCV_MASK)) \ + | (((sub_ver_inc >> 1) - 1) << ISPH3A_AEWSUBWIN_AEWINCV_SHIFT)) + +#define WRITE_SUB_HOR_INC(reg, sub_hor_inc) \ + (reg = (reg & ~(ISPH3A_AEWSUBWIN_AEWINCH_MASK)) \ + | (((sub_hor_inc >> 1) - 1) << ISPH3A_AEWSUBWIN_AEWINCH_SHIFT)) + +/** + * struct isph3a_aewb_regs - Current value of AE, AWB configuration registers. + * pcr: Peripheral control register. + * win1: Control register. + * start: Start position register. + * blk: Black line register. + * subwin: Configuration register. + */ +struct isph3a_aewb_regs { + u32 pcr; + u32 win1; + u32 start; + u32 blk; + u32 subwin; +}; + +struct isp_h3a_device { + spinlock_t *lock; /* Lock for this struct */ + + u8 update; + int pm_state; + int wb_update; + + struct isph3a_aewb_regs regs; + struct ispprev_wbal h3awb_update; + struct isph3a_aewb_config aewb_config_local; + u16 win_count; + + struct device *dev; + + struct ispstat stat; +}; + +int isph3a_aewb_configure(struct isp_h3a_device *isp_h3a, + struct isph3a_aewb_config *aewbcfg); + +int isph3a_aewb_request_statistics(struct isp_h3a_device *isp_h3a, + struct isph3a_aewb_data *aewbdata); + +void isph3a_save_context(struct device *dev); + +void isph3a_restore_context(struct device *dev); + +void isph3a_aewb_enable(struct isp_h3a_device *isp_h3a, u8 enable); + +int isph3a_aewb_busy(struct isp_h3a_device *isp_h3a); + +void isph3a_aewb_suspend(struct isp_h3a_device *isp_h3a); + +void isph3a_aewb_resume(struct isp_h3a_device *isp_h3a); + +void isph3a_update_wb(struct isp_h3a_device *isp_h3a); + +void isph3a_aewb_isr(struct isp_h3a_device *isp_h3a); + +#endif /* OMAP_ISP_H3A_H */ diff --git a/drivers/media/video/isp/isphist.c b/drivers/media/video/isp/isphist.c new file mode 100644 index 00000000000..d4ad266536c --- /dev/null +++ b/drivers/media/video/isp/isphist.c @@ -0,0 +1,577 @@ +/* + * isphist.c + * + * HISTOGRAM module for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * + * 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 + +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "isphist.h" + +/* Structure for saving/restoring histogram module registers */ +struct isp_reg isphist_reg_list[] = { + {OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD_OFF, 0}, + {OMAP3_ISP_IOMEM_HIST, ISPHIST_H_V_INFO, 0}, + {0, ISP_TOK_TERM, 0} +}; + +static void isp_hist_print_status(struct isp_hist_device *isp_hist); + +void __isp_hist_enable(struct isp_hist_device *isp_hist, u8 enable) +{ + if (enable) + DPRINTK_ISPHIST(" histogram enabled \n"); + else + DPRINTK_ISPHIST(" histogram disabled \n"); + + isp_reg_and_or(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, + ~ISPHIST_PCR_EN, (enable ? ISPHIST_PCR_EN : 0)); + isp_hist->hist_enable = enable; +} + +/** + * isp_hist_enable - Enables ISP Histogram submodule operation. + * @enable: 1 - Enables the histogram submodule. + * + * Client should configure all the Histogram registers before calling this + * function. + **/ +void isp_hist_enable(struct isp_hist_device *isp_hist, u8 enable) +{ + __isp_hist_enable(isp_hist, enable); + isp_hist->pm_state = enable; +} + +/** + * isp_hist_suspend - Suspend ISP Histogram submodule. + **/ +void isp_hist_suspend(struct isp_hist_device *isp_hist) +{ + if (isp_hist->pm_state) + __isp_hist_enable(isp_hist, 0); +} + +/** + * isp_hist_resume - Resume ISP Histogram submodule. + **/ +void isp_hist_resume(struct isp_hist_device *isp_hist) +{ + if (isp_hist->pm_state) + __isp_hist_enable(isp_hist, 1); +} + +int isp_hist_busy(struct isp_hist_device *isp_hist) +{ + return isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) + & ISPHIST_PCR_BUSY; +} + + +/** + * isp_hist_update_regs - Helper function to update Histogram registers. + **/ +static void isp_hist_update_regs(struct isp_hist_device *isp_hist) +{ + isp_reg_writel(isp_hist->dev, isp_hist->regs.pcr, OMAP3_ISP_IOMEM_HIST, + ISPHIST_PCR); + isp_reg_writel(isp_hist->dev, isp_hist->regs.cnt, OMAP3_ISP_IOMEM_HIST, + ISPHIST_CNT); + isp_reg_writel(isp_hist->dev, isp_hist->regs.wb_gain, + OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r0_h, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R0_HORZ); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r0_v, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R0_VERT); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r1_h, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R1_HORZ); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r1_v, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R1_VERT); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r2_h, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R2_HORZ); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r2_v, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R2_VERT); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r3_h, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R3_HORZ); + isp_reg_writel(isp_hist->dev, isp_hist->regs.r3_v, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R3_VERT); + isp_reg_writel(isp_hist->dev, isp_hist->regs.hist_addr, + OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); + isp_reg_writel(isp_hist->dev, isp_hist->regs.hist_data, + OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); + isp_reg_writel(isp_hist->dev, isp_hist->regs.hist_radd, + OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD); + isp_reg_writel(isp_hist->dev, isp_hist->regs.hist_radd_off, + OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD_OFF); + isp_reg_writel(isp_hist->dev, isp_hist->regs.h_v_info, + OMAP3_ISP_IOMEM_HIST, ISPHIST_H_V_INFO); +} + +/** + * isp_hist_isr - Callback from ISP driver for HIST interrupt. + * @status: IRQ0STATUS in case of MMU error, 0 for hist interrupt. + * arg1 and arg2 Not used as of now. + **/ +static void isp_hist_isr(unsigned long status, isp_vbq_callback_ptr arg1, + void *arg2) +{ + struct isp_hist_device *isp_hist = arg2; + + isp_hist_enable(isp_hist, 0); + + if (!(status & HIST_DONE)) + return; + + if (!isp_hist->completed) { + if (isp_hist->frame_req == isp_hist->frame_cnt) { + isp_hist->frame_cnt = 0; + isp_hist->frame_req = 0; + isp_hist->completed = 1; + } else { + isp_hist_enable(isp_hist, 1); + isp_hist->frame_cnt++; + } + } +} + +/** + * isp_hist_reset_mem - clear Histogram memory before start stats engine. + * + * Returns 0 after histogram memory was cleared. + **/ +static int isp_hist_reset_mem(struct isp_hist_device *isp_hist) +{ + int i; + + isp_reg_or(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, + ISPHIST_CNT_CLR_EN); + + for (i = 0; i < HIST_MEM_SIZE; i++) + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_DATA); + + isp_reg_and(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, + ~ISPHIST_CNT_CLR_EN); + + return 0; +} + +/** + * isp_hist_set_params - Helper function to check and store user given params. + * @user_cfg: Pointer to user configuration structure. + * + * Returns 0 on success configuration. + **/ +static int isp_hist_set_params(struct isp_hist_device *isp_hist, + struct isp_hist_config *user_cfg) +{ + + int reg_num = 0; + int bit_shift = 0; + + + if (isp_hist_busy(isp_hist)) + return -EINVAL; + + if (user_cfg->input_bit_width > MIN_BIT_WIDTH) + WRITE_DATA_SIZE(isp_hist->regs.cnt, 0); + else + WRITE_DATA_SIZE(isp_hist->regs.cnt, 1); + + WRITE_SOURCE(isp_hist->regs.cnt, user_cfg->hist_source); + + if (user_cfg->hist_source) { + WRITE_HV_INFO(isp_hist->regs.h_v_info, user_cfg->hist_h_v_info); + + if ((user_cfg->hist_radd & ISP_32B_BOUNDARY_BUF) == + user_cfg->hist_radd) { + WRITE_RADD(isp_hist->regs.hist_radd, + user_cfg->hist_radd); + } else { + dev_err(isp_hist->dev, + "hist: Address should be in 32 byte boundary" + "\n"); + return -EINVAL; + } + + if ((user_cfg->hist_radd_off & ISP_32B_BOUNDARY_OFFSET) == + user_cfg->hist_radd_off) { + WRITE_RADD_OFF(isp_hist->regs.hist_radd_off, + user_cfg->hist_radd_off); + } else { + dev_err(isp_hist->dev, + "hist: Offset should be in 32 byte boundary\n"); + return -EINVAL; + } + + } + + isp_hist_reset_mem(isp_hist); + DPRINTK_ISPHIST("ISPHIST: Memory Cleared\n"); + isp_hist->frame_req = user_cfg->hist_frames; + + if (unlikely(user_cfg->wb_gain_R > MAX_WB_GAIN || + user_cfg->wb_gain_RG > MAX_WB_GAIN || + user_cfg->wb_gain_B > MAX_WB_GAIN || + user_cfg->wb_gain_BG > MAX_WB_GAIN)) { + dev_err(isp_hist->dev, "hist: Invalid WB gain\n"); + return -EINVAL; + } else { + WRITE_WB_R(isp_hist->regs.wb_gain, user_cfg->wb_gain_R); + WRITE_WB_RG(isp_hist->regs.wb_gain, user_cfg->wb_gain_RG); + WRITE_WB_B(isp_hist->regs.wb_gain, user_cfg->wb_gain_B); + WRITE_WB_BG(isp_hist->regs.wb_gain, user_cfg->wb_gain_BG); + } + + /* Regions size and position */ + + if (user_cfg->num_regions > MAX_REGIONS) + return -EINVAL; + + if (likely((user_cfg->reg0_hor & ISPHIST_REGHORIZ_HEND_MASK) - + ((user_cfg->reg0_hor & ISPHIST_REGHORIZ_HSTART_MASK) >> + ISPHIST_REGHORIZ_HSTART_SHIFT))) { + WRITE_REG_HORIZ(isp_hist->regs.r0_h, user_cfg->reg0_hor); + reg_num++; + } else { + dev_err(isp_hist->dev, "hist: Invalid Region parameters\n"); + return -EINVAL; + } + + if (likely((user_cfg->reg0_ver & ISPHIST_REGVERT_VEND_MASK) - + ((user_cfg->reg0_ver & ISPHIST_REGVERT_VSTART_MASK) >> + ISPHIST_REGVERT_VSTART_SHIFT))) { + WRITE_REG_VERT(isp_hist->regs.r0_v, user_cfg->reg0_ver); + } else { + dev_err(isp_hist->dev, "hist: Invalid Region parameters\n"); + return -EINVAL; + } + + if (user_cfg->num_regions >= 1) { + if (likely((user_cfg->reg1_hor & ISPHIST_REGHORIZ_HEND_MASK) - + ((user_cfg->reg1_hor & + ISPHIST_REGHORIZ_HSTART_MASK) >> + ISPHIST_REGHORIZ_HSTART_SHIFT))) { + WRITE_REG_HORIZ(isp_hist->regs.r1_h, + user_cfg->reg1_hor); + } else { + dev_err(isp_hist->dev, + "hist: Invalid Region parameters\n"); + return -EINVAL; + } + + if (likely((user_cfg->reg1_ver & ISPHIST_REGVERT_VEND_MASK) - + ((user_cfg->reg1_ver & + ISPHIST_REGVERT_VSTART_MASK) >> + ISPHIST_REGVERT_VSTART_SHIFT))) { + WRITE_REG_VERT(isp_hist->regs.r1_v, + user_cfg->reg1_ver); + } else { + dev_err(isp_hist->dev, + "hist: Invalid Region parameters\n"); + return -EINVAL; + } + } + + if (user_cfg->num_regions >= 2) { + if (likely((user_cfg->reg2_hor & ISPHIST_REGHORIZ_HEND_MASK) - + ((user_cfg->reg2_hor & + ISPHIST_REGHORIZ_HSTART_MASK) >> + ISPHIST_REGHORIZ_HSTART_SHIFT))) { + WRITE_REG_HORIZ(isp_hist->regs.r2_h, + user_cfg->reg2_hor); + } else { + dev_err(isp_hist->dev, + "hist: Invalid Region parameters\n"); + return -EINVAL; + } + + if (likely((user_cfg->reg2_ver & ISPHIST_REGVERT_VEND_MASK) - + ((user_cfg->reg2_ver & + ISPHIST_REGVERT_VSTART_MASK) >> + ISPHIST_REGVERT_VSTART_SHIFT))) { + WRITE_REG_VERT(isp_hist->regs.r2_v, + user_cfg->reg2_ver); + } else { + dev_err(isp_hist->dev, + "hist: Invalid Region parameters\n"); + return -EINVAL; + } + } + + if (user_cfg->num_regions >= 3) { + if (likely((user_cfg->reg3_hor & ISPHIST_REGHORIZ_HEND_MASK) - + ((user_cfg->reg3_hor & + ISPHIST_REGHORIZ_HSTART_MASK) >> + ISPHIST_REGHORIZ_HSTART_SHIFT))) { + WRITE_REG_HORIZ(isp_hist->regs.r3_h, + user_cfg->reg3_hor); + } else { + dev_err(isp_hist->dev, + "hist: Invalid Region parameters\n"); + return -EINVAL; + } + + if (likely((user_cfg->reg3_ver & ISPHIST_REGVERT_VEND_MASK) - + ((user_cfg->reg3_ver & + ISPHIST_REGVERT_VSTART_MASK) >> + ISPHIST_REGVERT_VSTART_SHIFT))) { + WRITE_REG_VERT(isp_hist->regs.r3_v, + user_cfg->reg3_ver); + } else { + dev_err(isp_hist->dev, + "hist: Invalid Region parameters\n"); + return -EINVAL; + } + } + reg_num = user_cfg->num_regions; + if (unlikely(((user_cfg->hist_bins > BINS_256) && + (user_cfg->hist_bins != BINS_32)) || + ((user_cfg->hist_bins == BINS_256) && + reg_num != 0) || ((user_cfg->hist_bins == + BINS_128) && reg_num >= 2))) { + dev_err(isp_hist->dev, "hist: Invalid Bins Number: %d\n", + user_cfg->hist_bins); + return -EINVAL; + } else { + WRITE_NUM_BINS(isp_hist->regs.cnt, user_cfg->hist_bins); + } + + if (user_cfg->input_bit_width > MAX_BIT_WIDTH || + user_cfg->input_bit_width < MIN_BIT_WIDTH) { + dev_err(isp_hist->dev, "hist: Invalid Bit Width: %d\n", + user_cfg->input_bit_width); + return -EINVAL; + } else { + switch (user_cfg->hist_bins) { + case BINS_256: + bit_shift = user_cfg->input_bit_width - 8; + break; + case BINS_128: + bit_shift = user_cfg->input_bit_width - 7; + break; + case BINS_64: + bit_shift = user_cfg->input_bit_width - 6; + break; + case BINS_32: + bit_shift = user_cfg->input_bit_width - 5; + break; + default: + return -EINVAL; + } + WRITE_BIT_SHIFT(isp_hist->regs.cnt, bit_shift); + } + + isp_hist_update_regs(isp_hist); + isp_hist->initialized = 1; + + return 0; +} + +/** + * isp_hist_configure - API to configure HIST registers. + * @histcfg: Pointer to user configuration structure. + * + * Returns 0 on success configuration. + **/ +int isp_hist_configure(struct isp_hist_device *isp_hist, + struct isp_hist_config *histcfg) +{ + + int ret = 0; + + if (NULL == histcfg) { + dev_err(isp_hist->dev, + "hist: Null argument in configuration. \n"); + return -EINVAL; + } + + if (!isp_hist->initialized) { + DPRINTK_ISPHIST("Setting callback for HISTOGRAM\n"); + ret = isp_set_callback(isp_hist->dev, CBK_HIST_DONE, + isp_hist_isr, (void *)NULL, + isp_hist); + if (ret) { + dev_err(isp_hist->dev, "hist: No callback for HIST\n"); + return ret; + } + } + + ret = isp_hist_set_params(isp_hist, histcfg); + if (ret) { + dev_err(isp_hist->dev, "hist: Invalid parameters! \n"); + return ret; + } + + isp_hist->frame_cnt = 0; + isp_hist->completed = 0; + isp_hist_enable(isp_hist, 1); + isp_hist_print_status(isp_hist); + + return 0; +} +EXPORT_SYMBOL(isp_hist_configure); + +/** + * isp_hist_request_statistics - Request statistics in Histogram. + * @histdata: Pointer to data structure. + * + * This API allows the user to request for histogram statistics. + * + * Returns 0 on successful request. + **/ +int isp_hist_request_statistics(struct isp_hist_device *isp_hist, + struct isp_hist_data *histdata) +{ + int i, ret; + u32 curr; + + if (isp_hist_busy(isp_hist)) + return -EBUSY; + + if (!isp_hist->completed && isp_hist->initialized) + return -EINVAL; + + isp_reg_or(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, + ISPHIST_CNT_CLR_EN); + + for (i = 0; i < HIST_MEM_SIZE; i++) { + curr = isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_DATA); + ret = put_user(curr, histdata->hist_statistics_buf + i); + if (ret) { + dev_err(isp_hist->dev, "hist: Failed copy_to_user for " + "HIST stats buff, %d\n", ret); + } + } + + isp_reg_and(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, + ~ISPHIST_CNT_CLR_EN); + isp_hist->completed = 0; + return 0; +} +EXPORT_SYMBOL(isp_hist_request_statistics); + +/** + * isp_hist_init - Module Initialization. + * + * Returns 0 if successful. + **/ +int __init isp_hist_init(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + + isp->isp_hist.dev = dev; + + return 0; +} + +/** + * isp_hist_cleanup - Module cleanup. + **/ +void isp_hist_cleanup(struct device *dev) +{ +} + +/** + * isphist_save_context - Saves the values of the histogram module registers. + **/ +void isphist_save_context(struct device *dev) +{ + DPRINTK_ISPHIST(" Saving context\n"); + isp_save_context(dev, isphist_reg_list); +} +EXPORT_SYMBOL(isphist_save_context); + +/** + * isphist_restore_context - Restores the values of the histogram module regs. + **/ +void isphist_restore_context(struct device *dev) +{ + DPRINTK_ISPHIST(" Restoring context\n"); + isp_restore_context(dev, isphist_reg_list); +} +EXPORT_SYMBOL(isphist_restore_context); + +/** + * isp_hist_print_status - Debug print + **/ +static void isp_hist_print_status(struct isp_hist_device *isp_hist) +{ + DPRINTK_ISPHIST("ISPHIST_PCR = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_PCR)); + DPRINTK_ISPHIST("ISPHIST_CNT = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_CNT)); + DPRINTK_ISPHIST("ISPHIST_WB_GAIN = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_WB_GAIN)); + DPRINTK_ISPHIST("ISPHIST_R0_HORZ = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R0_HORZ)); + DPRINTK_ISPHIST("ISPHIST_R0_VERT = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R0_VERT)); + DPRINTK_ISPHIST("ISPHIST_R1_HORZ = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R1_HORZ)); + DPRINTK_ISPHIST("ISPHIST_R1_VERT = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R1_VERT)); + DPRINTK_ISPHIST("ISPHIST_R2_HORZ = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R2_HORZ)); + DPRINTK_ISPHIST("ISPHIST_R2_VERT = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R2_VERT)); + DPRINTK_ISPHIST("ISPHIST_R3_HORZ = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R3_HORZ)); + DPRINTK_ISPHIST("ISPHIST_R3_VERT = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_R3_VERT)); + DPRINTK_ISPHIST("ISPHIST_ADDR = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_ADDR)); + DPRINTK_ISPHIST("ISPHIST_RADD = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_RADD)); + DPRINTK_ISPHIST("ISPHIST_RADD_OFF = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_RADD_OFF)); + DPRINTK_ISPHIST("ISPHIST_H_V_INFO = 0x%08x\n", + isp_reg_readl(isp_hist->dev, OMAP3_ISP_IOMEM_HIST, + ISPHIST_H_V_INFO)); +} diff --git a/drivers/media/video/isp/isphist.h b/drivers/media/video/isp/isphist.h new file mode 100644 index 00000000000..021b1659097 --- /dev/null +++ b/drivers/media/video/isp/isphist.h @@ -0,0 +1,163 @@ +/* + * isphist.h + * + * Header file for HISTOGRAM module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * + * 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 OMAP_ISP_HIST_H +#define OMAP_ISP_HIST_H + +#include + +#define MAX_REGIONS 0x4 +#define MAX_WB_GAIN 255 +#define MIN_WB_GAIN 0x0 +#define MAX_BIT_WIDTH 14 +#define MIN_BIT_WIDTH 8 + +#define ISPHIST_PCR_EN (1 << 0) +#define HIST_MEM_SIZE 1024 +#define ISPHIST_CNT_CLR_EN (1 << 7) + +#define WRITE_SOURCE(reg, source) \ + (reg = (reg & ~(ISPHIST_CNT_SOURCE_MASK)) \ + | (source << ISPHIST_CNT_SOURCE_SHIFT)) + +#define WRITE_HV_INFO(reg, hv_info) \ + (reg = ((reg & ~(ISPHIST_HV_INFO_MASK)) \ + | (hv_info & ISPHIST_HV_INFO_MASK))) + +#define WRITE_RADD(reg, radd) \ + (reg = (reg & ~(ISPHIST_RADD_MASK)) \ + | (radd << ISPHIST_RADD_SHIFT)) + +#define WRITE_RADD_OFF(reg, radd_off) \ + (reg = (reg & ~(ISPHIST_RADD_OFF_MASK)) \ + | (radd_off << ISPHIST_RADD_OFF_SHIFT)) + +#define WRITE_BIT_SHIFT(reg, bit_shift) \ + (reg = (reg & ~(ISPHIST_CNT_SHIFT_MASK)) \ + | (bit_shift << ISPHIST_CNT_SHIFT_SHIFT)) + +#define WRITE_DATA_SIZE(reg, data_size) \ + (reg = (reg & ~(ISPHIST_CNT_DATASIZE_MASK)) \ + | (data_size << ISPHIST_CNT_DATASIZE_SHIFT)) + +#define WRITE_NUM_BINS(reg, num_bins) \ + (reg = (reg & ~(ISPHIST_CNT_BINS_MASK)) \ + | (num_bins << ISPHIST_CNT_BINS_SHIFT)) + +#define WRITE_WB_R(reg, reg_wb_gain) \ + reg = ((reg & ~(ISPHIST_WB_GAIN_WG00_MASK)) \ + | (reg_wb_gain << ISPHIST_WB_GAIN_WG00_SHIFT)) + +#define WRITE_WB_RG(reg, reg_wb_gain) \ + (reg = (reg & ~(ISPHIST_WB_GAIN_WG01_MASK)) \ + | (reg_wb_gain << ISPHIST_WB_GAIN_WG01_SHIFT)) + +#define WRITE_WB_B(reg, reg_wb_gain) \ + (reg = (reg & ~(ISPHIST_WB_GAIN_WG02_MASK)) \ + | (reg_wb_gain << ISPHIST_WB_GAIN_WG02_SHIFT)) + +#define WRITE_WB_BG(reg, reg_wb_gain) \ + (reg = (reg & ~(ISPHIST_WB_GAIN_WG03_MASK)) \ + | (reg_wb_gain << ISPHIST_WB_GAIN_WG03_SHIFT)) + +#define WRITE_REG_HORIZ(reg, reg_n_hor) \ + (reg = ((reg & ~ISPHIST_REGHORIZ_MASK) \ + | (reg_n_hor & ISPHIST_REGHORIZ_MASK))) + +#define WRITE_REG_VERT(reg, reg_n_vert) \ + (reg = ((reg & ~ISPHIST_REGVERT_MASK) \ + | (reg_n_vert & ISPHIST_REGVERT_MASK))) + +/** + * struct isp_hist_regs - Current value of Histogram configuration registers. + * @pcr: Peripheral control register. + * @cnt: Histogram control register. + * @wb_gain: Histogram white balance gain register. + * @r0_h: Region 0 horizontal register. + * @r0_v: Region 0 vertical register. + * @r1_h: Region 1 horizontal register. + * @r1_v: Region 1 vertical register. + * @r2_h: Region 2 horizontal register. + * @r2_v: Region 2 vertical register. + * @r3_h: Region 3 horizontal register. + * @r3_v: Region 3 vertical register. + * @hist_addr: Histogram address register. + * @hist_data: Histogram data. + * @hist_radd: Address register. When input data comes from mem. + * @hist_radd_off: Address offset register. When input data comes from mem. + * @h_v_info: Image size register. When input data comes from mem. + */ +struct isp_hist_regs { + u32 pcr; + u32 cnt; + u32 wb_gain; + u32 r0_h; + u32 r0_v; + u32 r1_h; + u32 r1_v; + u32 r2_h; + u32 r2_v; + u32 r3_h; + u32 r3_v; + u32 hist_addr; + u32 hist_data; + u32 hist_radd; + u32 hist_radd_off; + u32 h_v_info; +}; + +/** + * struct isp_hist_status - Histogram status. + * @hist_enable: Enables the histogram module. + * @initialized: Flag to indicate that the module is correctly initializated. + * @frame_cnt: Actual frame count. + * @frame_req: Frame requested by user. + * @completed: Flag to indicate if a frame request is completed. + */ +struct isp_hist_device { + u8 hist_enable; + u8 pm_state; + u8 initialized; + u8 frame_cnt; + u8 frame_req; + u8 completed; + struct isp_hist_regs regs; + struct device *dev; +}; + +void isp_hist_enable(struct isp_hist_device *isp_hist, u8 enable); + +int isp_hist_busy(struct isp_hist_device *isp_hist); + +int isp_hist_configure(struct isp_hist_device *isp_hist, + struct isp_hist_config *histcfg); + +int isp_hist_request_statistics(struct isp_hist_device *isp_hist, + struct isp_hist_data *histdata); + +void isphist_save_context(struct device *dev); + +void isp_hist_suspend(struct isp_hist_device *isp_hist); + +void isp_hist_resume(struct isp_hist_device *isp_hist); + +void isphist_restore_context(struct device *dev); + +#endif /* OMAP_ISP_HIST */ diff --git a/drivers/media/video/isp/isppreview.c b/drivers/media/video/isp/isppreview.c new file mode 100644 index 00000000000..44099bee6af --- /dev/null +++ b/drivers/media/video/isp/isppreview.c @@ -0,0 +1,1963 @@ +/* + * isppreview.c + * + * Driver Library for Preview module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Senthilvadivu Guruswamy + * Pallavi Kulkarni + * Sergio Aguirre + * + * 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 +#include +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "isppreview.h" + +/* Structure for saving/restoring preview module registers */ +static struct isp_reg ispprev_reg_list[] = { + {OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RADR_OFFSET, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_DSDR_ADDR, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_DRKF_OFFSET, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_WADD_OFFSET, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_NF, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3, 0x0000}, + {OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, 0x0000}, + {0, ISP_TOK_TERM, 0x0000} +}; + + +/* Default values in Office Flourescent Light for RGBtoRGB Blending */ +static struct ispprev_rgbtorgb flr_rgb2rgb = { + { /* RGB-RGB Matrix */ + {0x01E2, 0x0F30, 0x0FEE}, + {0x0F9B, 0x01AC, 0x0FB9}, + {0x0FE0, 0x0EC0, 0x0260} + }, /* RGB Offset */ + {0x0000, 0x0000, 0x0000} +}; + +/* Default values in Office Flourescent Light for RGB to YUV Conversion*/ +static struct ispprev_csc flr_prev_csc[] = { + { + { /* CSC Coef Matrix */ + {66, 129, 25}, + {-38, -75, 112}, + {112, -94 , -18} + }, /* CSC Offset */ + {0x0, 0x0, 0x0} + }, + { + { /* CSC Coef Matrix BW */ + {66, 129, 25}, + {0, 0, 0}, + {0, 0, 0} + }, /* CSC Offset */ + {0x0, 0x0, 0x0} + }, + { + { /* CSC Coef Matrix Sepia */ + {19, 38, 7}, + {0, 0, 0}, + {0, 0, 0} + }, /* CSC Offset */ + {0x0, 0xE7, 0x14} + } +}; + + +/* Default values in Office Flourescent Light for CFA Gradient*/ +#define FLR_CFA_GRADTHRS_HORZ 0x28 +#define FLR_CFA_GRADTHRS_VERT 0x28 + +/* Default values in Office Flourescent Light for Chroma Suppression*/ +#define FLR_CSUP_GAIN 0x0D +#define FLR_CSUP_THRES 0xEB + +/* Default values in Office Flourescent Light for Noise Filter*/ +#define FLR_NF_STRGTH 0x03 + +/* Default values in Office Flourescent Light for White Balance*/ +#define FLR_WBAL_DGAIN 0x100 +#define FLR_WBAL_COEF0 0x29 +#define FLR_WBAL_COEF1 0x20 +#define FLR_WBAL_COEF2 0x20 +#define FLR_WBAL_COEF3 0x2d + +#define FLR_WBAL_COEF0_ES1 0x23 +#define FLR_WBAL_COEF1_ES1 0x20 +#define FLR_WBAL_COEF2_ES1 0x20 +#define FLR_WBAL_COEF3_ES1 0x39 + +/* Default values in Office Flourescent Light for Black Adjustment*/ +#define FLR_BLKADJ_BLUE 0x0 +#define FLR_BLKADJ_GREEN 0x0 +#define FLR_BLKADJ_RED 0x0 + +/* + * Coeficient Tables for the submodules in Preview. + * Array is initialised with the values from.the tables text file. + */ + +/* + * CFA Filter Coefficient Table + * + */ +static u32 cfa_coef_table[] = { +#include "cfa_coef_table.h" +}; + +/* + * Gamma Correction Table - Red + */ +static u32 redgamma_table[] = { +#include "redgamma_table.h" +}; + +/* + * Gamma Correction Table - Green + */ +static u32 greengamma_table[] = { +#include "greengamma_table.h" +}; + +/* + * Gamma Correction Table - Blue + */ +static u32 bluegamma_table[] = { +#include "bluegamma_table.h" +}; + +/* + * Noise Filter Threshold table + */ +static u32 noise_filter_table[] = { +#include "noise_filter_table.h" +}; + +/* + * Luminance Enhancement Table + */ +static u32 luma_enhance_table[] = { +#include "luma_enhance_table.h" +}; + +static int omap34xx_isp_tables_update(struct isp_prev_device *isp_prev, + struct isptables_update *isptables_struct); + + +/** + * omap34xx_isp_preview_config - Abstraction layer Preview configuration. + * @userspace_add: Pointer from Userspace to structure with flags and data to + * update. + **/ +int omap34xx_isp_preview_config(struct isp_prev_device *isp_prev, + void *userspace_add) +{ + struct isp_device *isp = + container_of(isp_prev, struct isp_device, isp_prev); + struct ispprev_hmed prev_hmed_t; + struct ispprev_csup csup_t; + struct ispprev_wbal prev_wbal_t; + struct ispprev_blkadj prev_blkadj_t; + struct ispprev_yclimit yclimit_t; + struct ispprev_dcor prev_dcor_t; + struct ispprv_update_config *config; + struct isptables_update isp_table_update; + int yen_t[ISPPRV_YENH_TBL_SIZE]; + unsigned long flags; + + if (userspace_add == NULL) + return -EINVAL; + + spin_lock_irqsave(&isp_prev->lock, flags); + isp_prev->shadow_update = 1; + spin_unlock_irqrestore(&isp_prev->lock, flags); + + config = userspace_add; + + if (isp->running != ISP_STOPPED) + goto out_config_shadow; + + if (ISP_ABS_PREV_LUMAENH & config->flag) { + if (ISP_ABS_PREV_LUMAENH & config->update) { + if (copy_from_user(yen_t, config->yen, + sizeof(yen_t))) + goto err_copy_from_user; + isppreview_config_luma_enhancement(isp_prev, yen_t); + } + isp_prev->params.features |= PREV_LUMA_ENHANCE; + } else if (ISP_ABS_PREV_LUMAENH & config->update) + isp_prev->params.features &= ~PREV_LUMA_ENHANCE; + + if (ISP_ABS_PREV_INVALAW & config->flag) { + isppreview_enable_invalaw(isp_prev, 1); + isp_prev->params.features |= PREV_INVERSE_ALAW; + } else { + isppreview_enable_invalaw(isp_prev, 0); + isp_prev->params.features &= ~PREV_INVERSE_ALAW; + } + + if (ISP_ABS_PREV_HRZ_MED & config->flag) { + if (ISP_ABS_PREV_HRZ_MED & config->update) { + if (copy_from_user(&prev_hmed_t, + (struct ispprev_hmed *) + config->prev_hmed, + sizeof(struct ispprev_hmed))) + goto err_copy_from_user; + isppreview_config_hmed(isp_prev, prev_hmed_t); + } + isppreview_enable_hmed(isp_prev, 1); + isp_prev->params.features |= PREV_HORZ_MEDIAN_FILTER; + } else if (ISP_ABS_PREV_HRZ_MED & config->update) { + isppreview_enable_hmed(isp_prev, 0); + isp_prev->params.features &= ~PREV_HORZ_MEDIAN_FILTER; + } + + if (ISP_ABS_PREV_CHROMA_SUPP & config->flag) { + if (ISP_ABS_PREV_CHROMA_SUPP & config->update) { + if (copy_from_user(&csup_t, + (struct ispprev_csup *) + config->csup, + sizeof(struct ispprev_csup))) + goto err_copy_from_user; + isppreview_config_chroma_suppression(isp_prev, csup_t); + } + isppreview_enable_chroma_suppression(isp_prev, 1); + isp_prev->params.features |= PREV_CHROMA_SUPPRESS; + } else if (ISP_ABS_PREV_CHROMA_SUPP & config->update) { + isppreview_enable_chroma_suppression(isp_prev, 0); + isp_prev->params.features &= ~PREV_CHROMA_SUPPRESS; + } + + if (ISP_ABS_PREV_WB & config->update) { + if (copy_from_user(&prev_wbal_t, (struct ispprev_wbal *) + config->prev_wbal, + sizeof(struct ispprev_wbal))) + goto err_copy_from_user; + isppreview_config_whitebalance(isp_prev, prev_wbal_t); + } + + if (ISP_ABS_PREV_BLKADJ & config->update) { + if (copy_from_user(&prev_blkadj_t, (struct ispprev_blkadjl *) + config->prev_blkadj, + sizeof(struct ispprev_blkadj))) + goto err_copy_from_user; + isppreview_config_blkadj(isp_prev, prev_blkadj_t); + } + + if (ISP_ABS_PREV_YC_LIMIT & config->update) { + if (copy_from_user(&yclimit_t, (struct ispprev_yclimit *) + config->yclimit, + sizeof(struct ispprev_yclimit))) + goto err_copy_from_user; + isppreview_config_yc_range(isp_prev, yclimit_t); + } + + if (ISP_ABS_PREV_DEFECT_COR & config->flag) { + if (ISP_ABS_PREV_DEFECT_COR & config->update) { + if (copy_from_user(&prev_dcor_t, + (struct ispprev_dcor *) + config->prev_dcor, + sizeof(struct ispprev_dcor))) + goto err_copy_from_user; + isppreview_config_dcor(isp_prev, prev_dcor_t); + } + isppreview_enable_dcor(isp_prev, 1); + isp_prev->params.features |= PREV_DEFECT_COR; + } else if (ISP_ABS_PREV_DEFECT_COR & config->update) { + isppreview_enable_dcor(isp_prev, 0); + isp_prev->params.features &= ~PREV_DEFECT_COR; + } + + if (ISP_ABS_PREV_GAMMABYPASS & config->flag) { + isppreview_enable_gammabypass(isp_prev, 1); + isp_prev->params.features |= PREV_GAMMA_BYPASS; + } else { + isppreview_enable_gammabypass(isp_prev, 0); + isp_prev->params.features &= ~PREV_GAMMA_BYPASS; + } + +out_config_shadow: + if (ISP_ABS_PREV_RGB2RGB & config->update) { + if (copy_from_user(&isp_prev->params.rgb2rgb, + (struct ispprev_rgbtorgb *) + config->rgb2rgb, + sizeof(struct ispprev_rgbtorgb))) + goto err_copy_from_user; + isppreview_config_rgb_blending(isp_prev, + isp_prev->params.rgb2rgb); + /* The function call above prevents compiler from reordering + * writes so that the flag below is always set after + * isp_prev->params.rgb2rgb is written to. */ + isp_prev->update_rgb_blending = 1; + } + + if (ISP_ABS_PREV_COLOR_CONV & config->update) { + if (copy_from_user(&isp_prev->params.rgb2ycbcr, + (struct ispprev_csc *) + config->prev_csc, + sizeof(struct ispprev_csc))) + goto err_copy_from_user; + isppreview_config_rgb_to_ycbcr(isp_prev, + isp_prev->params.rgb2ycbcr); + /* Same here... this flag has to be set after rgb2ycbcr + * structure is written to. */ + isp_prev->update_rgb_to_ycbcr = 1; + } + + isp_table_update.update = config->update; + isp_table_update.flag = config->flag; + isp_table_update.prev_nf = config->prev_nf; + isp_table_update.red_gamma = config->red_gamma; + isp_table_update.green_gamma = config->green_gamma; + isp_table_update.blue_gamma = config->blue_gamma; + isp_table_update.prev_cfa = config->prev_cfa; + + if (omap34xx_isp_tables_update(isp_prev, &isp_table_update)) + goto err_copy_from_user; + + spin_lock_irqsave(&isp_prev->lock, flags); + isp_prev->shadow_update = 0; + spin_unlock_irqrestore(&isp_prev->lock, flags); + + return 0; + +err_copy_from_user: + spin_lock_irqsave(&isp_prev->lock, flags); + isp_prev->shadow_update = 0; + spin_unlock_irqrestore(&isp_prev->lock, flags); + + dev_err(isp_prev->dev, "preview: Config: Copy From User Error\n"); + return -EFAULT; +} +EXPORT_SYMBOL_GPL(omap34xx_isp_preview_config); + +/** + * omap34xx_isp_tables_update - Abstraction layer Tables update. + * @isptables_struct: Pointer from Userspace to structure with flags and table + * data to update. + **/ +static int omap34xx_isp_tables_update(struct isp_prev_device *isp_prev, + struct isptables_update *isptables_struct) +{ + + if (ISP_ABS_TBL_NF & isptables_struct->flag) { + isp_prev->nf_enable = 1; + isp_prev->params.features |= PREV_NOISE_FILTER; + if (ISP_ABS_TBL_NF & isptables_struct->update) { + if (copy_from_user(&isp_prev->prev_nf_t, + (struct ispprev_nf *) + isptables_struct->prev_nf, + sizeof(struct ispprev_nf))) + goto err_copy_from_user; + + isp_prev->nf_update = 1; + } else + isp_prev->nf_update = 0; + } else { + isp_prev->nf_enable = 0; + isp_prev->params.features &= ~PREV_NOISE_FILTER; + if (ISP_ABS_TBL_NF & isptables_struct->update) + isp_prev->nf_update = 1; + else + isp_prev->nf_update = 0; + } + + if (ISP_ABS_TBL_REDGAMMA & isptables_struct->update) { + if (copy_from_user(redgamma_table, isptables_struct->red_gamma, + sizeof(redgamma_table))) { + goto err_copy_from_user; + } + isp_prev->rg_update = 1; + } else + isp_prev->rg_update = 0; + + if (ISP_ABS_TBL_GREENGAMMA & isptables_struct->update) { + if (copy_from_user(greengamma_table, + isptables_struct->green_gamma, + sizeof(greengamma_table))) + goto err_copy_from_user; + isp_prev->gg_update = 1; + } else + isp_prev->gg_update = 0; + + if (ISP_ABS_TBL_BLUEGAMMA & isptables_struct->update) { + if (copy_from_user(bluegamma_table, + isptables_struct->blue_gamma, + sizeof(bluegamma_table))) { + goto err_copy_from_user; + } + isp_prev->bg_update = 1; + } else + isp_prev->bg_update = 0; + + if (ISP_ABS_PREV_CFA & isptables_struct->update) { + struct ispprev_cfa cfa; + if (isptables_struct->prev_cfa) { + if (copy_from_user(&cfa, + isptables_struct->prev_cfa, + sizeof(struct ispprev_cfa))) + goto err_copy_from_user; + if (cfa.cfa_table != NULL) { + if (copy_from_user(cfa_coef_table, + cfa.cfa_table, + sizeof(cfa_coef_table))) + goto err_copy_from_user; + } + cfa.cfa_table = cfa_coef_table; + isp_prev->params.cfa = cfa; + } + if (ISP_ABS_PREV_CFA & isptables_struct->flag) { + isp_prev->cfa_en = 1; + isp_prev->params.features |= PREV_CFA; + } else { + isp_prev->cfa_en = 0; + isp_prev->params.features &= ~PREV_CFA; + } + isp_prev->cfa_update = 1; + } + + return 0; + +err_copy_from_user: + dev_err(isp_prev->dev, "preview tables: Copy From User Error\n"); + return -EFAULT; +} + +/** + * isppreview_config_shadow_registers - Program shadow registers for preview. + * + * Allows user to program shadow registers associated with preview module. + **/ +void isppreview_config_shadow_registers(struct isp_prev_device *isp_prev) +{ + u8 current_brightness_contrast; + int ctr; + unsigned long flags; + + spin_lock_irqsave(&isp_prev->lock, flags); + if (isp_prev->shadow_update) { + spin_unlock_irqrestore(&isp_prev->lock, flags); + return; + } + + isppreview_query_brightness(isp_prev, ¤t_brightness_contrast); + if (current_brightness_contrast != isp_prev->brightness) { + DPRINTK_ISPPREV(" Changing Brightness level to %d\n", + isp_prev->brightness); + isppreview_config_brightness(isp_prev, isp_prev->brightness); + } + + isppreview_query_contrast(isp_prev, ¤t_brightness_contrast); + if (current_brightness_contrast != isp_prev->contrast) { + DPRINTK_ISPPREV(" Changing Contrast level to %d\n", + isp_prev->contrast); + isppreview_config_contrast(isp_prev, isp_prev->contrast); + } + if (isp_prev->update_color_matrix) { + isppreview_config_rgb_to_ycbcr(isp_prev, + flr_prev_csc[isp_prev->color]); + isp_prev->update_color_matrix = 0; + } + if (isp_prev->update_rgb_blending) { + isp_prev->update_rgb_blending = 0; + isppreview_config_rgb_blending(isp_prev, + isp_prev->params.rgb2rgb); + } + if (isp_prev->update_rgb_to_ycbcr) { + isp_prev->update_rgb_to_ycbcr = 0; + isppreview_config_rgb_to_ycbcr(isp_prev, + isp_prev->params.rgb2ycbcr); + } + + if (isp_prev->gg_update) { + isp_reg_writel(isp_prev->dev, ISPPRV_TBL_ADDR_GREEN_G_START, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + + for (ctr = 0; ctr < ISP_GAMMA_TABLE_SIZE; ctr++) { + isp_reg_writel(isp_prev->dev, greengamma_table[ctr], + OMAP3_ISP_IOMEM_PREV, + ISPPRV_SET_TBL_DATA); + } + isp_prev->gg_update = 0; + } + + if (isp_prev->rg_update) { + isp_reg_writel(isp_prev->dev, ISPPRV_TBL_ADDR_RED_G_START, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + + for (ctr = 0; ctr < ISP_GAMMA_TABLE_SIZE; ctr++) { + isp_reg_writel(isp_prev->dev, redgamma_table[ctr], + OMAP3_ISP_IOMEM_PREV, + ISPPRV_SET_TBL_DATA); + } + isp_prev->rg_update = 0; + } + + if (isp_prev->bg_update) { + isp_reg_writel(isp_prev->dev, ISPPRV_TBL_ADDR_BLUE_G_START, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + + for (ctr = 0; ctr < ISP_GAMMA_TABLE_SIZE; ctr++) { + isp_reg_writel(isp_prev->dev, bluegamma_table[ctr], + OMAP3_ISP_IOMEM_PREV, + ISPPRV_SET_TBL_DATA); + } + isp_prev->bg_update = 0; + } + + if (isp_prev->cfa_update) { + isp_prev->cfa_update = 0; + isppreview_config_cfa(isp_prev, &isp_prev->params.cfa); + isppreview_enable_cfa(isp_prev, isp_prev->cfa_en); + } + + if (isp_prev->nf_update && isp_prev->nf_enable) { + isp_reg_writel(isp_prev->dev, 0xC00, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + isp_reg_writel(isp_prev->dev, isp_prev->prev_nf_t.spread, + OMAP3_ISP_IOMEM_PREV, ISPPRV_NF); + for (ctr = 0; ctr < ISPPRV_NF_TBL_SIZE; ctr++) { + isp_reg_writel(isp_prev->dev, + isp_prev->prev_nf_t.table[ctr], + OMAP3_ISP_IOMEM_PREV, + ISPPRV_SET_TBL_DATA); + } + isppreview_enable_noisefilter(isp_prev, 1); + isp_prev->nf_update = 0; + } + + if (~isp_prev->nf_update && isp_prev->nf_enable) + isppreview_enable_noisefilter(isp_prev, 1); + + if (isp_prev->nf_update && ~isp_prev->nf_enable) + isppreview_enable_noisefilter(isp_prev, 0); + + spin_unlock_irqrestore(&isp_prev->lock, flags); +} +EXPORT_SYMBOL_GPL(isppreview_config_shadow_registers); + +/** + * isppreview_request - Reserves the preview module. + * + * Returns 0 if successful, or -EBUSY if the module was already reserved. + **/ +int isppreview_request(struct isp_prev_device *isp_prev) +{ + isp_reg_or(isp_prev->dev, + OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ISPCTRL_PREV_RAM_EN | + ISPCTRL_PREV_CLK_EN | ISPCTRL_SBL_WR1_RAM_EN); + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_request); + +/** + * isppreview_free - Frees the preview module. + * + * Returns 0 if successful, or -EINVAL if the module was already freed. + **/ +int isppreview_free(struct isp_prev_device *isp_prev) +{ + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ~(ISPCTRL_PREV_CLK_EN | + ISPCTRL_PREV_RAM_EN | + ISPCTRL_SBL_WR1_RAM_EN)); + + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_free); + +/** isppreview_config_datapath - Specifies input and output modules for Preview + * @input: Indicates the module that gives the image to preview. + * @output: Indicates the module to which the preview outputs to. + * + * Configures the default configuration for the CCDC to work with. + * + * The valid values for the input are PRV_RAW_CCDC (0), PRV_RAW_MEM (1), + * PRV_RGBBAYERCFA (2), PRV_COMPCFA (3), PRV_CCDC_DRKF (4), PRV_OTHERS (5). + * + * The valid values for the output are PREVIEW_RSZ (0), PREVIEW_MEM (1). + * + * Returns 0 if successful, or -EINVAL if wrong input or output values are + * specified. + **/ +int isppreview_config_datapath(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe) +{ + u32 pcr = 0; + u8 enable = 0; + struct prev_params *params = &isp_prev->params; + struct ispprev_yclimit yclimit; + + pcr = isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); + + switch (pipe->prv_in) { + case PRV_RAW_CCDC: + pcr &= ~ISPPRV_PCR_SOURCE; + break; + case PRV_RAW_MEM: + pcr |= ISPPRV_PCR_SOURCE; + break; + case PRV_CCDC_DRKF: + pcr |= ISPPRV_PCR_DRKFCAP; + break; + case PRV_COMPCFA: + break; + case PRV_OTHERS: + break; + case PRV_RGBBAYERCFA: + break; + default: + dev_err(isp_prev->dev, "preview: Wrong Input\n"); + return -EINVAL; + }; + + switch (pipe->prv_out) { + case PREVIEW_RSZ: + pcr |= ISPPRV_PCR_RSZPORT; + pcr &= ~ISPPRV_PCR_SDRPORT; + break; + case PREVIEW_MEM: + pcr &= ~ISPPRV_PCR_RSZPORT; + pcr |= ISPPRV_PCR_SDRPORT; + break; + default: + dev_err(isp_prev->dev, "preview: Wrong Output\n"); + return -EINVAL; + } + + isp_reg_writel(isp_prev->dev, pcr, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); + + if (params->csup.hypf_en == 1) + isppreview_config_chroma_suppression(isp_prev, params->csup); + if (params->ytable != NULL) + isppreview_config_luma_enhancement(isp_prev, params->ytable); + + if (params->gtable.redtable != NULL) + isppreview_config_gammacorrn(isp_prev, params->gtable); + + isp_prev->cfa_update = 0; + isppreview_config_cfa(isp_prev, ¶ms->cfa); + enable = (params->features & PREV_CFA) ? 1 : 0; + isppreview_enable_cfa(isp_prev, enable); + + enable = (params->features & PREV_CHROMA_SUPPRESS) ? 1 : 0; + isppreview_enable_chroma_suppression(isp_prev, enable); + + enable = (params->features & PREV_LUMA_ENHANCE) ? 1 : 0; + isppreview_enable_luma_enhancement(isp_prev, enable); + + enable = (params->features & PREV_NOISE_FILTER) ? 1 : 0; + if (enable) + isppreview_config_noisefilter(isp_prev, params->nf); + isppreview_enable_noisefilter(isp_prev, enable); + + enable = (params->features & PREV_DEFECT_COR) ? 1 : 0; + if (enable) + isppreview_config_dcor(isp_prev, params->dcor); + isppreview_enable_dcor(isp_prev, enable); + + enable = (params->features & PREV_GAMMA_BYPASS) ? 1 : 0; + isppreview_enable_gammabypass(isp_prev, enable); + + isppreview_config_whitebalance(isp_prev, params->wbal); + isppreview_config_blkadj(isp_prev, params->blk_adj); + isppreview_config_rgb_blending(isp_prev, params->rgb2rgb); + isppreview_config_rgb_to_ycbcr(isp_prev, params->rgb2ycbcr); + + isppreview_config_contrast(isp_prev, params->contrast); + isppreview_config_brightness(isp_prev, params->brightness); + + yclimit.minC = ISPPRV_YC_MIN; + yclimit.maxC = ISPPRV_YC_MAX; + yclimit.minY = ISPPRV_YC_MIN; + yclimit.maxY = ISPPRV_YC_MAX; + isppreview_config_yc_range(isp_prev, yclimit); + + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_config_datapath); + +/** + * isppreview_set_skip - Set the number of rows/columns that should be skipped. + * h - Start Pixel Horizontal. + * v - Start Line Vertical. + **/ +void isppreview_set_skip(struct isp_prev_device *isp_prev, u32 h, u32 v) +{ + isp_prev->sph = h; + isp_prev->slv = v; +} +EXPORT_SYMBOL_GPL(isppreview_set_skip); + +/** + * isppreview_config_ycpos - Configure byte layout of YUV image. + * @mode: Indicates the required byte layout. + **/ +void isppreview_config_ycpos(struct isp_prev_device *isp_prev, + enum preview_ycpos_mode mode) +{ + u32 pcr = isp_reg_readl(isp_prev->dev, + OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); + pcr &= ~ISPPRV_PCR_YCPOS_CrYCbY; + pcr |= (mode << ISPPRV_PCR_YCPOS_SHIFT); + isp_reg_writel(isp_prev->dev, pcr, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); +} +EXPORT_SYMBOL_GPL(isppreview_config_ycpos); + +/** + * isppreview_config_averager - Enable / disable / configure averager + * @average: Average value to be configured. + **/ +void isppreview_config_averager(struct isp_prev_device *isp_prev, u8 average) +{ + int reg = 0; + + reg = AVE_ODD_PIXEL_DIST | AVE_EVEN_PIXEL_DIST | average; + isp_reg_writel(isp_prev->dev, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); +} +EXPORT_SYMBOL_GPL(isppreview_config_averager); + +/** + * isppreview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview. + * @enable: 1 - Reverse the A-Law done in CCDC. + **/ +void isppreview_enable_invalaw(struct isp_prev_device *isp_prev, u8 enable) +{ + u32 pcr_val = 0; + pcr_val = isp_reg_readl(isp_prev->dev, + OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); + + if (enable) { + isp_reg_writel(isp_prev->dev, + pcr_val | ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW, + OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); + } else { + isp_reg_writel(isp_prev->dev, pcr_val & + ~(ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW), + OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); + } +} +EXPORT_SYMBOL_GPL(isppreview_enable_invalaw); + +/** + * isppreview_enable_drkframe - Enable/Disable of the darkframe subtract. + * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is + * subtracted with the pixels in the current frame. + * + * The proccess is applied for each captured frame. + **/ +void isppreview_enable_drkframe(struct isp_prev_device *isp_prev, u8 enable) +{ + if (enable) + isp_reg_or(isp_prev->dev, + OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_DRKFEN); + else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_DRKFEN); + } +} +EXPORT_SYMBOL_GPL(isppreview_enable_drkframe); + +/** + * isppreview_enable_shadcomp - Enables/Disables the shading compensation. + * @enable: 1 - Enables the shading compensation. + * + * If dark frame subtract won't be used, then enable this shading + * compensation. + **/ +void isppreview_enable_shadcomp(struct isp_prev_device *isp_prev, u8 enable) +{ + + if (enable) { + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SCOMP_EN); + isppreview_enable_drkframe(isp_prev, 1); + } else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_SCOMP_EN); + } +} +EXPORT_SYMBOL_GPL(isppreview_enable_shadcomp); + +/** + * isppreview_config_drkf_shadcomp - Configures shift value in shading comp. + * @scomp_shtval: 3bit value of shift used in shading compensation. + **/ +void isppreview_config_drkf_shadcomp(struct isp_prev_device *isp_prev, + u8 scomp_shtval) +{ + u32 pcr_val = isp_reg_readl(isp_prev->dev, + OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); + + pcr_val &= ISPPRV_PCR_SCOMP_SFT_MASK; + isp_reg_writel(isp_prev->dev, + pcr_val | (scomp_shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); +} +EXPORT_SYMBOL_GPL(isppreview_config_drkf_shadcomp); + +/** + * isppreview_enable_hmed - Enables/Disables of the Horizontal Median Filter. + * @enable: 1 - Enables Horizontal Median Filter. + **/ +void isppreview_enable_hmed(struct isp_prev_device *isp_prev, u8 enable) +{ + if (enable) + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_HMEDEN); + else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_HMEDEN); + } + isp_prev->hmed_en = enable ? 1 : 0; +} +EXPORT_SYMBOL_GPL(isppreview_enable_hmed); + +/** + * isppreview_config_hmed - Configures the Horizontal Median Filter. + * @prev_hmed: Structure containing the odd and even distance between the + * pixels in the image along with the filter threshold. + **/ +void isppreview_config_hmed(struct isp_prev_device *isp_prev, + struct ispprev_hmed prev_hmed) +{ + + u32 odddist = 0; + u32 evendist = 0; + + if (prev_hmed.odddist == 1) + odddist = ~ISPPRV_HMED_ODDDIST; + else + odddist = ISPPRV_HMED_ODDDIST; + + if (prev_hmed.evendist == 1) + evendist = ~ISPPRV_HMED_EVENDIST; + else + evendist = ISPPRV_HMED_EVENDIST; + + isp_reg_writel(isp_prev->dev, odddist | evendist | (prev_hmed.thres << + ISPPRV_HMED_THRESHOLD_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED); + +} +EXPORT_SYMBOL_GPL(isppreview_config_hmed); + +/** + * isppreview_config_noisefilter - Configures the Noise Filter. + * @prev_nf: Structure containing the noisefilter table, strength to be used + * for the noise filter and the defect correction enable flag. + **/ +void isppreview_config_noisefilter(struct isp_prev_device *isp_prev, + struct ispprev_nf prev_nf) +{ + int i = 0; + + isp_reg_writel(isp_prev->dev, prev_nf.spread, OMAP3_ISP_IOMEM_PREV, + ISPPRV_NF); + isp_reg_writel(isp_prev->dev, ISPPRV_NF_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < ISPPRV_NF_TBL_SIZE; i++) { + isp_reg_writel(isp_prev->dev, prev_nf.table[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } +} +EXPORT_SYMBOL_GPL(isppreview_config_noisefilter); + +/** + * isppreview_config_dcor - Configures the defect correction + * @prev_nf: Structure containing the defect correction structure + **/ +void isppreview_config_dcor(struct isp_prev_device *isp_prev, + struct ispprev_dcor prev_dcor) +{ + if (prev_dcor.couplet_mode_en) { + isp_reg_writel(isp_prev->dev, prev_dcor.detect_correct[0], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0); + isp_reg_writel(isp_prev->dev, prev_dcor.detect_correct[1], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1); + isp_reg_writel(isp_prev->dev, prev_dcor.detect_correct[2], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2); + isp_reg_writel(isp_prev->dev, prev_dcor.detect_correct[3], + OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3); + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DCCOUP); + } else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_DCCOUP); + } +} +EXPORT_SYMBOL_GPL(isppreview_config_dcor); + +/** + * isppreview_config_cfa - Configures the CFA Interpolation parameters. + * @prev_cfa: Structure containing the CFA interpolation table, CFA format + * in the image, vertical and horizontal gradient threshold. + **/ +void isppreview_config_cfa(struct isp_prev_device *isp_prev, + struct ispprev_cfa *prev_cfa) +{ + int i = 0; + + isp_prev->cfafmt = prev_cfa->cfafmt; + + isp_reg_and_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_CFAFMT_MASK, + (prev_cfa->cfafmt << ISPPRV_PCR_CFAFMT_SHIFT)); + + isp_reg_writel(isp_prev->dev, + (prev_cfa->cfa_gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) | + (prev_cfa->cfa_gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA); + + isp_reg_writel(isp_prev->dev, ISPPRV_CFA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + + for (i = 0; i < ISPPRV_CFA_TBL_SIZE; i++) { + isp_reg_writel(isp_prev->dev, prev_cfa->cfa_table[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } +} +EXPORT_SYMBOL_GPL(isppreview_config_cfa); + +/** + * isppreview_config_gammacorrn - Configures the Gamma Correction table values + * @gtable: Structure containing the table for red, blue, green gamma table. + **/ +void isppreview_config_gammacorrn(struct isp_prev_device *isp_prev, + struct ispprev_gtable gtable) +{ + int i = 0; + + isp_reg_writel(isp_prev->dev, ISPPRV_REDGAMMA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < ISPPRV_GAMMA_TBL_SIZE; i++) { + isp_reg_writel(isp_prev->dev, gtable.redtable[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } + + isp_reg_writel(isp_prev->dev, ISPPRV_GREENGAMMA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < ISPPRV_GAMMA_TBL_SIZE; i++) { + isp_reg_writel(isp_prev->dev, gtable.greentable[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } + + isp_reg_writel(isp_prev->dev, ISPPRV_BLUEGAMMA_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < ISPPRV_GAMMA_TBL_SIZE; i++) { + isp_reg_writel(isp_prev->dev, gtable.bluetable[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } +} +EXPORT_SYMBOL_GPL(isppreview_config_gammacorrn); + +/** + * isppreview_set_luma_enhancement - Stores the Luminance Enhancement table. + * @ytable: Structure containing the table for Luminance Enhancement table. + **/ +void isppreview_set_luma_enhancement(struct isp_prev_device *isp_prev, + u32 *ytable) +{ + int i; + + for (i = 0; i < ISPPRV_YENH_TBL_SIZE; i++) + isp_prev->params.ytable[i] = ytable[i]; +} +EXPORT_SYMBOL_GPL(isppreview_set_luma_enhancement); + +/** + * isppreview_config_luma_enhancement - Writes the Luminance Enhancement table. + * @ytable: Structure containing the table for Luminance Enhancement table. + **/ +void isppreview_config_luma_enhancement(struct isp_prev_device *isp_prev, + u32 *ytable) +{ + int i = 0; + + isp_reg_writel(isp_prev->dev, ISPPRV_YENH_TABLE_ADDR, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); + for (i = 0; i < ISPPRV_YENH_TBL_SIZE; i++) { + isp_reg_writel(isp_prev->dev, ytable[i], + OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); + } +} +EXPORT_SYMBOL_GPL(isppreview_config_luma_enhancement); + +/** + * isppreview_config_chroma_suppression - Configures the Chroma Suppression. + * @csup: Structure containing the threshold value for suppression + * and the hypass filter enable flag. + **/ +void isppreview_config_chroma_suppression(struct isp_prev_device *isp_prev, + struct ispprev_csup csup) +{ + isp_reg_writel(isp_prev->dev, + csup.gain | (csup.thres << ISPPRV_CSUP_THRES_SHIFT) | + (csup.hypf_en << ISPPRV_CSUP_HPYF_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP); +} +EXPORT_SYMBOL_GPL(isppreview_config_chroma_suppression); + +/** + * isppreview_enable_noisefilter - Enables/Disables the Noise Filter. + * @enable: 1 - Enables the Noise Filter. + **/ +void isppreview_enable_noisefilter(struct isp_prev_device *isp_prev, u8 enable) +{ + if (enable) + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_NFEN); + else + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_NFEN); + isp_prev->nf_en = enable ? 1 : 0; +} +EXPORT_SYMBOL_GPL(isppreview_enable_noisefilter); + +/** + * isppreview_enable_dcor - Enables/Disables the defect correction. + * @enable: 1 - Enables the defect correction. + **/ +void isppreview_enable_dcor(struct isp_prev_device *isp_prev, u8 enable) +{ + if (enable) + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_DCOREN); + else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_DCOREN); + } + isp_prev->dcor_en = enable ? 1 : 0; +} +EXPORT_SYMBOL_GPL(isppreview_enable_dcor); + +/** + * isppreview_enable_cfa - Enable/Disable the CFA Interpolation. + * @enable: 1 - Enables the CFA. + **/ +void isppreview_enable_cfa(struct isp_prev_device *isp_prev, u8 enable) +{ + if (enable) + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); + else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_CFAEN); + } + isp_prev->cfa_en = enable ? 1 : 0; +} +EXPORT_SYMBOL_GPL(isppreview_enable_cfa); + +/** + * isppreview_enable_gammabypass - Enables/Disables the GammaByPass + * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. + * 0 - Goes through Gamma Correction. input and output is 10bit. + **/ +void isppreview_enable_gammabypass(struct isp_prev_device *isp_prev, u8 enable) +{ + if (enable) { + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_GAMMA_BYPASS); + } else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_GAMMA_BYPASS); + } +} +EXPORT_SYMBOL_GPL(isppreview_enable_gammabypass); + +/** + * isppreview_enable_luma_enhancement - Enables/Disables Luminance Enhancement + * @enable: 1 - Enable the Luminance Enhancement. + **/ +void isppreview_enable_luma_enhancement(struct isp_prev_device *isp_prev, + u8 enable) +{ + if (enable) { + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_YNENHEN); + } else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_YNENHEN); + } + isp_prev->yenh_en = enable ? 1 : 0; +} +EXPORT_SYMBOL_GPL(isppreview_enable_luma_enhancement); + +/** + * isppreview_enable_chroma_suppression - Enables/Disables Chrominance Suppr. + * @enable: 1 - Enable the Chrominance Suppression. + **/ +void isppreview_enable_chroma_suppression(struct isp_prev_device *isp_prev, + u8 enable) +{ + if (enable) + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_SUPEN); + else { + isp_reg_and(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ~ISPPRV_PCR_SUPEN); + } + isp_prev->csup_en = enable ? 1 : 0; +} +EXPORT_SYMBOL_GPL(isppreview_enable_chroma_suppression); + +/** + * isppreview_config_whitebalance - Configures the White Balance parameters. + * @prev_wbal: Structure containing the digital gain and white balance + * coefficient. + * + * Coefficient matrix always with default values. + **/ +void isppreview_config_whitebalance(struct isp_prev_device *isp_prev, + struct ispprev_wbal prev_wbal) +{ + u32 val; + + isp_reg_writel(isp_prev->dev, prev_wbal.dgain, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WB_DGAIN); + + val = prev_wbal.coef0 << ISPPRV_WBGAIN_COEF0_SHIFT; + val |= prev_wbal.coef1 << ISPPRV_WBGAIN_COEF1_SHIFT; + val |= prev_wbal.coef2 << ISPPRV_WBGAIN_COEF2_SHIFT; + val |= prev_wbal.coef3 << ISPPRV_WBGAIN_COEF3_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WBGAIN); + + isp_reg_writel(isp_prev->dev, + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT | + ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT | + ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT | + ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT | + ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); +} +EXPORT_SYMBOL_GPL(isppreview_config_whitebalance); + +/** + * isppreview_config_whitebalance2 - Configures the White Balance parameters. + * @prev_wbal: Structure containing the digital gain and white balance + * coefficient. + * + * Coefficient matrix can be changed. + **/ +void isppreview_config_whitebalance2(struct isp_prev_device *isp_prev, + struct prev_white_balance prev_wbal) +{ + isp_reg_writel(isp_prev->dev, prev_wbal.wb_dgain, + OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN); + isp_reg_writel(isp_prev->dev, prev_wbal.wb_gain[0] | + prev_wbal.wb_gain[1] << ISPPRV_WBGAIN_COEF1_SHIFT | + prev_wbal.wb_gain[2] << ISPPRV_WBGAIN_COEF2_SHIFT | + prev_wbal.wb_gain[3] << ISPPRV_WBGAIN_COEF3_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN); + + isp_reg_writel(isp_prev->dev, + prev_wbal.wb_coefmatrix[0][0] << ISPPRV_WBSEL_N0_0_SHIFT | + prev_wbal.wb_coefmatrix[0][1] << ISPPRV_WBSEL_N0_1_SHIFT | + prev_wbal.wb_coefmatrix[0][2] << ISPPRV_WBSEL_N0_2_SHIFT | + prev_wbal.wb_coefmatrix[0][3] << ISPPRV_WBSEL_N0_3_SHIFT | + prev_wbal.wb_coefmatrix[1][0] << ISPPRV_WBSEL_N1_0_SHIFT | + prev_wbal.wb_coefmatrix[1][1] << ISPPRV_WBSEL_N1_1_SHIFT | + prev_wbal.wb_coefmatrix[1][2] << ISPPRV_WBSEL_N1_2_SHIFT | + prev_wbal.wb_coefmatrix[1][3] << ISPPRV_WBSEL_N1_3_SHIFT | + prev_wbal.wb_coefmatrix[2][0] << ISPPRV_WBSEL_N2_0_SHIFT | + prev_wbal.wb_coefmatrix[2][1] << ISPPRV_WBSEL_N2_1_SHIFT | + prev_wbal.wb_coefmatrix[2][2] << ISPPRV_WBSEL_N2_2_SHIFT | + prev_wbal.wb_coefmatrix[2][3] << ISPPRV_WBSEL_N2_3_SHIFT | + prev_wbal.wb_coefmatrix[3][0] << ISPPRV_WBSEL_N3_0_SHIFT | + prev_wbal.wb_coefmatrix[3][1] << ISPPRV_WBSEL_N3_1_SHIFT | + prev_wbal.wb_coefmatrix[3][2] << ISPPRV_WBSEL_N3_2_SHIFT | + prev_wbal.wb_coefmatrix[3][3] << ISPPRV_WBSEL_N3_3_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); +} +EXPORT_SYMBOL_GPL(isppreview_config_whitebalance2); + +/** + * isppreview_config_blkadj - Configures the Black Adjustment parameters. + * @prev_blkadj: Structure containing the black adjustment towards red, green, + * blue. + **/ +void isppreview_config_blkadj(struct isp_prev_device *isp_prev, + struct ispprev_blkadj prev_blkadj) +{ + isp_reg_writel(isp_prev->dev, prev_blkadj.blue | + (prev_blkadj.green << ISPPRV_BLKADJOFF_G_SHIFT) | + (prev_blkadj.red << ISPPRV_BLKADJOFF_R_SHIFT), + OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF); +} +EXPORT_SYMBOL_GPL(isppreview_config_blkadj); + +/** + * isppreview_config_rgb_blending - Configures the RGB-RGB Blending matrix. + * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb + * offset. + **/ +void isppreview_config_rgb_blending(struct isp_prev_device *isp_prev, + struct ispprev_rgbtorgb rgb2rgb) +{ + u32 val = 0; + + val = (rgb2rgb.matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT; + val |= (rgb2rgb.matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT1); + + val = (rgb2rgb.matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT; + val |= (rgb2rgb.matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT2); + + val = (rgb2rgb.matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT; + val |= (rgb2rgb.matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT3); + + val = (rgb2rgb.matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT; + val |= (rgb2rgb.matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT4); + + val = (rgb2rgb.matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT5); + + val = (rgb2rgb.offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT; + val |= (rgb2rgb.offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_OFF1); + + val = (rgb2rgb.offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_OFF2); +} +EXPORT_SYMBOL_GPL(isppreview_config_rgb_blending); + +/** + * Configures the RGB-YCbYCr conversion matrix + * @prev_csc: Structure containing the RGB to YCbYCr matrix and the + * YCbCr offset. + **/ +void isppreview_config_rgb_to_ycbcr(struct isp_prev_device *isp_prev, + struct ispprev_csc prev_csc) +{ + u32 val = 0; + + val = (prev_csc.matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT; + val |= (prev_csc.matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT; + val |= (prev_csc.matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0); + + val = (prev_csc.matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT; + val |= (prev_csc.matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT; + val |= (prev_csc.matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1); + + val = (prev_csc.matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT; + val |= (prev_csc.matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT; + val |= (prev_csc.matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2); + + val = (prev_csc.offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT; + val |= (prev_csc.offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT; + val |= (prev_csc.offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT; + isp_reg_writel(isp_prev->dev, val, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CSC_OFFSET); +} +EXPORT_SYMBOL_GPL(isppreview_config_rgb_to_ycbcr); + +/** + * isppreview_query_contrast - Query the contrast. + * @contrast: Pointer to hold the current programmed contrast value. + **/ +void isppreview_query_contrast(struct isp_prev_device *isp_prev, u8 *contrast) +{ + u32 brt_cnt_val = 0; + + brt_cnt_val = isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CNT_BRT); + *contrast = (brt_cnt_val >> ISPPRV_CNT_BRT_CNT_SHIFT) & 0xff; + DPRINTK_ISPPREV(" Current brt cnt value in hw is %x\n", brt_cnt_val); +} +EXPORT_SYMBOL_GPL(isppreview_query_contrast); + +/** + * isppreview_update_contrast - Updates the contrast. + * @contrast: Pointer to hold the current programmed contrast value. + * + * Value should be programmed before enabling the module. + **/ +void isppreview_update_contrast(struct isp_prev_device *isp_prev, u8 *contrast) +{ + isp_prev->contrast = *contrast; +} +EXPORT_SYMBOL_GPL(isppreview_update_contrast); + +/** + * isppreview_config_contrast - Configures the Contrast. + * @contrast: 8 bit value in U8Q4 format. + * + * Value should be programmed before enabling the module. + **/ +void isppreview_config_contrast(struct isp_prev_device *isp_prev, u8 contrast) +{ + u32 brt_cnt_val = 0; + + brt_cnt_val = isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CNT_BRT); + brt_cnt_val &= ~(0xff << ISPPRV_CNT_BRT_CNT_SHIFT); + contrast &= 0xff; + isp_reg_writel(isp_prev->dev, + brt_cnt_val | contrast << ISPPRV_CNT_BRT_CNT_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); +} +EXPORT_SYMBOL_GPL(isppreview_config_contrast); + +/** + * isppreview_get_contrast_range - Gets the range contrast value. + * @min_contrast: Pointer to hold the minimum Contrast value. + * @max_contrast: Pointer to hold the maximum Contrast value. + **/ +void isppreview_get_contrast_range(u8 *min_contrast, u8 *max_contrast) +{ + *min_contrast = ISPPRV_CONTRAST_MIN; + *max_contrast = ISPPRV_CONTRAST_MAX; +} +EXPORT_SYMBOL_GPL(isppreview_get_contrast_range); + +/** + * isppreview_update_brightness - Updates the brightness in preview module. + * @brightness: Pointer to hold the current programmed brightness value. + * + **/ +void isppreview_update_brightness(struct isp_prev_device *isp_prev, + u8 *brightness) +{ + isp_prev->brightness = *brightness; +} +EXPORT_SYMBOL_GPL(isppreview_update_brightness); + +/** + * isppreview_config_brightness - Configures the brightness. + * @contrast: 8bitvalue in U8Q0 format. + **/ +void isppreview_config_brightness(struct isp_prev_device *isp_prev, + u8 brightness) +{ + u32 brt_cnt_val = 0; + + DPRINTK_ISPPREV("\tConfiguring brightness in ISP: %d\n", brightness); + brt_cnt_val = isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CNT_BRT); + brt_cnt_val &= ~(0xff << ISPPRV_CNT_BRT_BRT_SHIFT); + brightness &= 0xff; + isp_reg_writel(isp_prev->dev, + brt_cnt_val | brightness << ISPPRV_CNT_BRT_BRT_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); +} +EXPORT_SYMBOL_GPL(isppreview_config_brightness); + +/** + * isppreview_query_brightness - Query the brightness. + * @brightness: Pointer to hold the current programmed brightness value. + **/ +void isppreview_query_brightness(struct isp_prev_device *isp_prev, + u8 *brightness) +{ + *brightness = isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CNT_BRT); +} +EXPORT_SYMBOL_GPL(isppreview_query_brightness); + +/** + * isppreview_get_brightness_range - Gets the range brightness value + * @min_brightness: Pointer to hold the minimum brightness value + * @max_brightness: Pointer to hold the maximum brightness value + **/ +void isppreview_get_brightness_range(u8 *min_brightness, u8 *max_brightness) +{ + *min_brightness = ISPPRV_BRIGHT_MIN; + *max_brightness = ISPPRV_BRIGHT_MAX; +} +EXPORT_SYMBOL_GPL(isppreview_get_brightness_range); + +/** + * isppreview_set_color - Sets the color effect. + * @mode: Indicates the required color effect. + **/ +void isppreview_set_color(struct isp_prev_device *isp_prev, u8 *mode) +{ + isp_prev->color = *mode; + isp_prev->update_color_matrix = 1; +} +EXPORT_SYMBOL_GPL(isppreview_set_color); + +/** + * isppreview_get_color - Gets the current color effect. + * @mode: Indicates the current color effect. + **/ +void isppreview_get_color(struct isp_prev_device *isp_prev, u8 *mode) +{ + *mode = isp_prev->color; +} +EXPORT_SYMBOL_GPL(isppreview_get_color); + +/** + * isppreview_config_yc_range - Configures the max and min Y and C values. + * @yclimit: Structure containing the range of Y and C values. + **/ +void isppreview_config_yc_range(struct isp_prev_device *isp_prev, + struct ispprev_yclimit yclimit) +{ + isp_reg_writel(isp_prev->dev, + yclimit.maxC << ISPPRV_SETUP_YC_MAXC_SHIFT | + yclimit.maxY << ISPPRV_SETUP_YC_MAXY_SHIFT | + yclimit.minC << ISPPRV_SETUP_YC_MINC_SHIFT | + yclimit.minY << ISPPRV_SETUP_YC_MINY_SHIFT, + OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); +} +EXPORT_SYMBOL_GPL(isppreview_config_yc_range); + +/** + * isppreview_try_size - Calculates output dimensions with the modules enabled. + * @input_w: input width for the preview in number of pixels per line + * @input_h: input height for the preview in number of lines + * @output_w: output width from the preview in number of pixels per line + * @output_h: output height for the preview in number of lines + * + * Calculates the number of pixels cropped in the submodules that are enabled, + * Fills up the output width height variables in the isp_prev structure. + **/ +int isppreview_try_pipeline(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe) +{ + u32 div = 0; + int max_out; + + if (pipe->ccdc_out_w_img < 32 || pipe->ccdc_out_h < 32) { + dev_err(isp_prev->dev, "preview does not support " + "width < 16 or height < 32 \n"); + return -EINVAL; + } + if (omap_rev() == OMAP3430_REV_ES1_0) + max_out = ISPPRV_MAXOUTPUT_WIDTH; + else + max_out = ISPPRV_MAXOUTPUT_WIDTH_ES2; + + pipe->prv_out_w = pipe->ccdc_out_w; + pipe->prv_out_h = pipe->ccdc_out_h; + pipe->prv_out_w_img = pipe->ccdc_out_w_img; + pipe->prv_out_h_img = pipe->ccdc_out_h; + + isp_prev->fmtavg = 0; + + if (pipe->ccdc_out_w_img > max_out) { + div = (pipe->ccdc_out_w_img/max_out); + if (div >= 2 && div < 4) { + isp_prev->fmtavg = 1; + pipe->prv_out_w_img /= 2; + } else if (div >= 4 && div < 8) { + isp_prev->fmtavg = 2; + pipe->prv_out_w_img /= 4; + } else if (div >= 8) { + isp_prev->fmtavg = 3; + pipe->prv_out_w_img /= 8; + } + } + +/* if (isp_prev->hmed_en) */ + pipe->prv_out_w_img -= 4; +/* if (isp_prev->nf_en) */ + pipe->prv_out_w_img -= 4; + pipe->prv_out_h_img -= 4; +/* if (isp_prev->cfa_en) */ + switch (isp_prev->cfafmt) { + case CFAFMT_BAYER: + case CFAFMT_SONYVGA: + pipe->prv_out_w_img -= 4; + pipe->prv_out_h_img -= 4; + break; + case CFAFMT_RGBFOVEON: + case CFAFMT_RRGGBBFOVEON: + case CFAFMT_DNSPL: + case CFAFMT_HONEYCOMB: + pipe->prv_out_h_img -= 2; + break; + }; +/* if (isp_prev->yenh_en || isp_prev->csup_en) */ + pipe->prv_out_w_img -= 2; + + /* Start at the correct row/column by skipping + * a Sensor specific amount. + */ + pipe->prv_out_w_img -= isp_prev->sph; + pipe->prv_out_h_img -= isp_prev->slv; + + if (pipe->prv_out_w_img % 2) + pipe->prv_out_w_img -= 1; + + /* FIXME: This doesn't apply for prv -> rsz. */ + pipe->prv_out_w = ALIGN(pipe->prv_out_w_img, 0x20); + + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_try_pipeline); + +/** + * isppreview_config_size - Sets the size of ISP preview output. + * @pipe->ccdc_out_w: input width for the preview in number of pixels per line + * @pipe->ccdc_out_h: input height for the preview in number of lines + * @output_w: output width from the preview in number of pixels per line + * @output_h: output height for the preview in number of lines + * + * Configures the appropriate values stored in the isp_prev structure to + * HORZ/VERT_INFO. Configures PRV_AVE if needed for downsampling as calculated + * in trysize. + **/ +int isppreview_s_pipeline(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe) +{ + u32 prevsdroff; + int rval; + + rval = isppreview_config_datapath(isp_prev, pipe); + if (rval) + return rval; + + isp_reg_writel(isp_prev->dev, + (isp_prev->sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | + (pipe->ccdc_out_w - 1), + OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); + isp_reg_writel(isp_prev->dev, + (isp_prev->slv << ISPPRV_VERT_INFO_SLV_SHIFT) | + (pipe->ccdc_out_h - 2), + OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO); + + if (isp_prev->cfafmt == CFAFMT_BAYER) + isp_reg_writel(isp_prev->dev, ISPPRV_AVE_EVENDIST_2 << + ISPPRV_AVE_EVENDIST_SHIFT | + ISPPRV_AVE_ODDDIST_2 << + ISPPRV_AVE_ODDDIST_SHIFT | + isp_prev->fmtavg, + OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); + + if (pipe->prv_out == PREVIEW_MEM) { + prevsdroff = pipe->prv_out_w * ISP_BYTES_PER_PIXEL; + if ((prevsdroff & ISP_32B_BOUNDARY_OFFSET) != prevsdroff) { + DPRINTK_ISPPREV("ISP_WARN: Preview output buffer line" + " size is truncated" + " to 32byte boundary\n"); + prevsdroff &= ISP_32B_BOUNDARY_BUF ; + } + isppreview_config_outlineoffset(isp_prev, prevsdroff); + } + + if (pipe->pix.pixelformat == V4L2_PIX_FMT_UYVY) + isppreview_config_ycpos(isp_prev, YCPOS_YCrYCb); + else + isppreview_config_ycpos(isp_prev, YCPOS_CrYCbY); + + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_s_pipeline); + +/** + * isppreview_config_inlineoffset - Configures the Read address line offset. + * @offset: Line Offset for the input image. + **/ +int isppreview_config_inlineoffset(struct isp_prev_device *isp_prev, u32 offset) +{ + if ((offset & ISP_32B_BOUNDARY_OFFSET) == offset) { + isp_reg_writel(isp_prev->dev, offset & 0xffff, + OMAP3_ISP_IOMEM_PREV, ISPPRV_RADR_OFFSET); + } else { + dev_err(isp_prev->dev, "preview: Offset should be in 32 byte " + "boundary\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_config_inlineoffset); + +/** + * isppreview_set_inaddr - Sets memory address of input frame. + * @addr: 32bit memory address aligned on 32byte boundary. + * + * Configures the memory address from which the input frame is to be read. + **/ +int isppreview_set_inaddr(struct isp_prev_device *isp_prev, u32 addr) +{ + if ((addr & ISP_32B_BOUNDARY_BUF) == addr) + isp_reg_writel(isp_prev->dev, addr, + OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR); + else { + dev_err(isp_prev->dev, "preview: Address should be in 32 byte " + "boundary\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_set_inaddr); + +/** + * isppreview_config_outlineoffset - Configures the Write address line offset. + * @offset: Line Offset for the preview output. + **/ +int isppreview_config_outlineoffset(struct isp_prev_device *isp_prev, + u32 offset) +{ + if ((offset & ISP_32B_BOUNDARY_OFFSET) != offset) { + dev_err(isp_prev->dev, "preview: Offset should be in 32 byte " + "boundary\n"); + return -EINVAL; + } + isp_reg_writel(isp_prev->dev, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WADD_OFFSET); + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_config_outlineoffset); + +/** + * isppreview_set_outaddr - Sets the memory address to store output frame + * @addr: 32bit memory address aligned on 32byte boundary. + * + * Configures the memory address to which the output frame is written. + **/ +int isppreview_set_outaddr(struct isp_prev_device *isp_prev, u32 addr) +{ + if ((addr & ISP_32B_BOUNDARY_BUF) != addr) { + dev_err(isp_prev->dev, "preview: Address should be in 32 byte " + "boundary\n"); + return -EINVAL; + } + isp_reg_writel(isp_prev->dev, addr, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WSDR_ADDR); + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_set_outaddr); + +/** + * isppreview_config_darklineoffset - Sets the Dark frame address line offset. + * @offset: Line Offset for the Darkframe. + **/ +int isppreview_config_darklineoffset(struct isp_prev_device *isp_prev, + u32 offset) +{ + if ((offset & ISP_32B_BOUNDARY_OFFSET) != offset) { + dev_err(isp_prev->dev, "preview: Offset should be in 32 byte " + "boundary\n"); + return -EINVAL; + } + isp_reg_writel(isp_prev->dev, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, + ISPPRV_DRKF_OFFSET); + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_config_darklineoffset); + +/** + * isppreview_set_darkaddr - Sets the memory address to store Dark frame. + * @addr: 32bit memory address aligned on 32 bit boundary. + **/ +int isppreview_set_darkaddr(struct isp_prev_device *isp_prev, u32 addr) +{ + if ((addr & ISP_32B_BOUNDARY_BUF) != addr) { + dev_err(isp_prev->dev, "preview: Address should be in 32 byte " + "boundary\n"); + return -EINVAL; + } + isp_reg_writel(isp_prev->dev, addr, OMAP3_ISP_IOMEM_PREV, + ISPPRV_DSDR_ADDR); + return 0; +} +EXPORT_SYMBOL_GPL(isppreview_set_darkaddr); + +/** + * isppreview_enable - Enables the Preview module. + * @enable: 1 - Enables the preview module. + * + * Client should configure all the sub modules in Preview before this. + **/ +void isppreview_enable(struct isp_prev_device *isp_prev) +{ + isp_reg_or(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT); +} +EXPORT_SYMBOL_GPL(isppreview_enable); + +/** + * isppreview_busy - Gets busy state of preview module. + **/ +int isppreview_busy(struct isp_prev_device *isp_prev) +{ + return isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR) + & ISPPRV_PCR_BUSY; +} +EXPORT_SYMBOL_GPL(isppreview_busy); + +/** + * isppreview_save_context - Saves the values of the preview module registers. + **/ +void isppreview_save_context(struct device *dev) +{ + DPRINTK_ISPPREV("Saving context\n"); + isp_save_context(dev, ispprev_reg_list); +} +EXPORT_SYMBOL_GPL(isppreview_save_context); + +/** + * isppreview_restore_context - Restores the values of preview module registers + **/ +void isppreview_restore_context(struct device *dev) +{ + DPRINTK_ISPPREV("Restoring context\n"); + isp_restore_context(dev, ispprev_reg_list); +} +EXPORT_SYMBOL_GPL(isppreview_restore_context); + +/** + * isppreview_print_status - Prints the values of the Preview Module registers. + * + * Also prints other debug information stored in the preview moduel. + **/ +void isppreview_print_status(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe) +{ + DPRINTK_ISPPREV("Preview Input format =%d, Output Format =%d\n", + pipe->prv_inp, pipe->prv_out); + DPRINTK_ISPPREV("Accepted Preview Input (width = %d,Height = %d)\n", + isp_prev->previn_w, + isp_prev->previn_h); + DPRINTK_ISPPREV("Accepted Preview Output (width = %d,Height = %d)\n", + isp_prev->prevout_w, + isp_prev->prevout_h); + DPRINTK_ISPPREV("###ISP_CTRL in preview =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_CTRL)); + DPRINTK_ISPPREV("###ISP_IRQ0ENABLE in preview =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_IRQ0ENABLE)); + DPRINTK_ISPPREV("###ISP_IRQ0STATUS in preview =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_MAIN, + ISP_IRQ0STATUS)); + DPRINTK_ISPPREV("###PRV PCR =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_PCR)); + DPRINTK_ISPPREV("###PRV HORZ_INFO =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_HORZ_INFO)); + DPRINTK_ISPPREV("###PRV VERT_INFO =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_VERT_INFO)); + DPRINTK_ISPPREV("###PRV WSDR_ADDR =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WSDR_ADDR)); + DPRINTK_ISPPREV("###PRV WADD_OFFSET =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WADD_OFFSET)); + DPRINTK_ISPPREV("###PRV AVE =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_AVE)); + DPRINTK_ISPPREV("###PRV HMED =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_HMED)); + DPRINTK_ISPPREV("###PRV NF =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_NF)); + DPRINTK_ISPPREV("###PRV WB_DGAIN =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WB_DGAIN)); + DPRINTK_ISPPREV("###PRV WBGAIN =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WBGAIN)); + DPRINTK_ISPPREV("###PRV WBSEL =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WBSEL)); + DPRINTK_ISPPREV("###PRV CFA =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CFA)); + DPRINTK_ISPPREV("###PRV BLKADJOFF =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_BLKADJOFF)); + DPRINTK_ISPPREV("###PRV RGB_MAT1 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT1)); + DPRINTK_ISPPREV("###PRV RGB_MAT2 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT2)); + DPRINTK_ISPPREV("###PRV RGB_MAT3 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT3)); + DPRINTK_ISPPREV("###PRV RGB_MAT4 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT4)); + DPRINTK_ISPPREV("###PRV RGB_MAT5 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_MAT5)); + DPRINTK_ISPPREV("###PRV RGB_OFF1 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_OFF1)); + DPRINTK_ISPPREV("###PRV RGB_OFF2 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_RGB_OFF2)); + DPRINTK_ISPPREV("###PRV CSC0 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CSC0)); + DPRINTK_ISPPREV("###PRV CSC1 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CSC1)); + DPRINTK_ISPPREV("###PRV CSC2 =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CSC2)); + DPRINTK_ISPPREV("###PRV CSC_OFFSET =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CSC_OFFSET)); + DPRINTK_ISPPREV("###PRV CNT_BRT =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CNT_BRT)); + DPRINTK_ISPPREV("###PRV CSUP =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_CSUP)); + DPRINTK_ISPPREV("###PRV SETUP_YC =0x%x\n", + isp_reg_readl(isp_prev->dev, OMAP3_ISP_IOMEM_PREV, + ISPPRV_SETUP_YC)); +} +EXPORT_SYMBOL_GPL(isppreview_print_status); + +/** + * isp_preview_init - Module Initialization. + **/ +int __init isp_preview_init(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_prev_device *isp_prev = &isp->isp_prev; + struct prev_params *params = &isp_prev->params; + int i = 0; + + isp_prev->dev = dev; + + /* Init values */ + isp_prev->sph = 2; + isp_prev->slv = 0; + isp_prev->color = V4L2_COLORFX_NONE; + isp_prev->contrast = ISPPRV_CONTRAST_DEF; + params->contrast = ISPPRV_CONTRAST_DEF; + isp_prev->brightness = ISPPRV_BRIGHT_DEF; + params->brightness = ISPPRV_BRIGHT_DEF; + params->average = NO_AVE; + params->lens_shading_shift = 0; + params->cfa.cfafmt = CFAFMT_BAYER; + params->cfa.cfa_table = cfa_coef_table; + params->cfa.cfa_gradthrs_horz = FLR_CFA_GRADTHRS_HORZ; + params->cfa.cfa_gradthrs_vert = FLR_CFA_GRADTHRS_VERT; + params->csup.gain = FLR_CSUP_GAIN; + params->csup.thres = FLR_CSUP_THRES; + params->csup.hypf_en = 0; + params->ytable = kzalloc(sizeof(u32) * ISPPRV_YENH_TBL_SIZE, + GFP_KERNEL); + isppreview_set_luma_enhancement(isp_prev, luma_enhance_table); + params->nf.spread = FLR_NF_STRGTH; + memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table)); + params->dcor.couplet_mode_en = 1; + for (i = 0; i < 4; i++) + params->dcor.detect_correct[i] = 0xE; + params->gtable.bluetable = bluegamma_table; + params->gtable.greentable = greengamma_table; + params->gtable.redtable = redgamma_table; + params->wbal.dgain = FLR_WBAL_DGAIN; + if (omap_rev() > OMAP3430_REV_ES1_0) { + params->wbal.coef0 = FLR_WBAL_COEF0_ES1; + params->wbal.coef1 = FLR_WBAL_COEF1_ES1; + params->wbal.coef2 = FLR_WBAL_COEF2_ES1; + params->wbal.coef3 = FLR_WBAL_COEF3_ES1; + } else { + params->wbal.coef0 = FLR_WBAL_COEF0; + params->wbal.coef1 = FLR_WBAL_COEF1; + params->wbal.coef2 = FLR_WBAL_COEF2; + params->wbal.coef3 = FLR_WBAL_COEF3; + } + params->blk_adj.red = FLR_BLKADJ_RED; + params->blk_adj.green = FLR_BLKADJ_GREEN; + params->blk_adj.blue = FLR_BLKADJ_BLUE; + params->rgb2rgb = flr_rgb2rgb; + params->rgb2ycbcr = flr_prev_csc[isp_prev->color]; + + params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER; + params->features &= ~(PREV_AVERAGER | PREV_INVERSE_ALAW | + PREV_HORZ_MEDIAN_FILTER | + PREV_GAMMA_BYPASS | + PREV_DARK_FRAME_SUBTRACT | + PREV_LENS_SHADING | + PREV_DARK_FRAME_CAPTURE | + PREV_CHROMA_SUPPRESS | + PREV_LUMA_ENHANCE); + + spin_lock_init(&isp_prev->lock); + + return 0; +} + +/** + * isp_preview_cleanup - Module Cleanup. + **/ +void isp_preview_cleanup(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_prev_device *isp_prev = &isp->isp_prev; + struct prev_params *params = &isp_prev->params; + + kfree(params->ytable); +} diff --git a/drivers/media/video/isp/isppreview.h b/drivers/media/video/isp/isppreview.h new file mode 100644 index 00000000000..bd24029f9a2 --- /dev/null +++ b/drivers/media/video/isp/isppreview.h @@ -0,0 +1,426 @@ +/* + * isppreview.h + * + * Driver header file for Preview module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Senthilvadivu Guruswamy + * Pallavi Kulkarni + * Sergio Aguirre + * + * 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 OMAP_ISP_PREVIEW_H +#define OMAP_ISP_PREVIEW_H + +#include +/* Isp query control structure */ + +#define ISPPRV_BRIGHT_STEP 0x1 +#define ISPPRV_BRIGHT_DEF 0x1 +#define ISPPRV_BRIGHT_LOW 0x0 +#define ISPPRV_BRIGHT_HIGH 0xFF +#define ISPPRV_BRIGHT_UNITS 0x1 + +#define ISPPRV_CONTRAST_STEP 0x1 +#define ISPPRV_CONTRAST_DEF 0x10 +#define ISPPRV_CONTRAST_LOW 0x0 +#define ISPPRV_CONTRAST_HIGH 0xFF +#define ISPPRV_CONTRAST_UNITS 0x1 + +#define NO_AVE 0x0 +#define AVE_2_PIX 0x1 +#define AVE_4_PIX 0x2 +#define AVE_8_PIX 0x3 +#define AVE_ODD_PIXEL_DIST (1 << 4) /* For Bayer Sensors */ +#define AVE_EVEN_PIXEL_DIST (1 << 2) + +#define WB_GAIN_MAX 4 + +/* Features list */ +#define PREV_AVERAGER (1 << 0) +#define PREV_INVERSE_ALAW (1 << 1) +#define PREV_HORZ_MEDIAN_FILTER (1 << 2) +#define PREV_NOISE_FILTER (1 << 3) +#define PREV_CFA (1 << 4) +#define PREV_GAMMA_BYPASS (1 << 5) +#define PREV_LUMA_ENHANCE (1 << 6) +#define PREV_CHROMA_SUPPRESS (1 << 7) +#define PREV_DARK_FRAME_SUBTRACT (1 << 8) +#define PREV_LENS_SHADING (1 << 9) +#define PREV_DARK_FRAME_CAPTURE (1 << 10) +#define PREV_DEFECT_COR (1 << 11) + + +#define ISP_NF_TABLE_SIZE (1 << 10) + +#define ISP_GAMMA_TABLE_SIZE (1 << 10) + +/* Table addresses */ +#define ISPPRV_TBL_ADDR_RED_G_START 0x00 +#define ISPPRV_TBL_ADDR_BLUE_G_START 0x800 +#define ISPPRV_TBL_ADDR_GREEN_G_START 0x400 + +/* + *Enumeration Constants for input and output format + */ +enum preview_input { + PRV_RAW_CCDC, + PRV_RAW_MEM, + PRV_RGBBAYERCFA, + PRV_COMPCFA, + PRV_CCDC_DRKF, + PRV_OTHERS +}; +enum preview_output { + PREVIEW_RSZ, + PREVIEW_MEM +}; +/* + * Configure byte layout of YUV image + */ +enum preview_ycpos_mode { + YCPOS_YCrYCb = 0, + YCPOS_YCbYCr = 1, + YCPOS_CbYCrY = 2, + YCPOS_CrYCbY = 3 +}; + +/** + * struct ispprev_gtable - Structure for Gamma Correction. + * @redtable: Pointer to the red gamma table. + * @greentable: Pointer to the green gamma table. + * @bluetable: Pointer to the blue gamma table. + */ +struct ispprev_gtable { + u32 *redtable; + u32 *greentable; + u32 *bluetable; +}; + +/** + * struct prev_white_balance - Structure for White Balance 2. + * @wb_dgain: White balance common gain. + * @wb_gain: Individual color gains. + * @wb_coefmatrix: Coefficient matrix + */ +struct prev_white_balance { + u16 wb_dgain; /* white balance common gain */ + u8 wb_gain[WB_GAIN_MAX]; /* individual color gains */ + u8 wb_coefmatrix[WB_GAIN_MAX][WB_GAIN_MAX]; +}; + +/** + * struct prev_size_params - Structure for size parameters. + * @hstart: Starting pixel. + * @vstart: Starting line. + * @hsize: Width of input image. + * @vsize: Height of input image. + * @pixsize: Pixel size of the image in terms of bits. + * @in_pitch: Line offset of input image. + * @out_pitch: Line offset of output image. + */ +struct prev_size_params { + unsigned int hstart; + unsigned int vstart; + unsigned int hsize; + unsigned int vsize; + unsigned char pixsize; + unsigned short in_pitch; + unsigned short out_pitch; +}; + +/** + * struct prev_rgb2ycbcr_coeffs - Structure RGB2YCbCr parameters. + * @coeff: Color conversion gains in 3x3 matrix. + * @offset: Color conversion offsets. + */ +struct prev_rgb2ycbcr_coeffs { + short coeff[RGB_MAX][RGB_MAX]; + short offset[RGB_MAX]; +}; + +/** + * struct prev_darkfrm_params - Structure for Dark frame suppression. + * @addr: Memory start address. + * @offset: Line offset. + */ +struct prev_darkfrm_params { + u32 addr; + u32 offset; + }; + +/** + * struct prev_params - Structure for all configuration + * @features: Set of features enabled. + * @cfa: CFA coefficients. + * @csup: Chroma suppression coefficients. + * @ytable: Pointer to Luma enhancement coefficients. + * @nf: Noise filter coefficients. + * @dcor: Noise filter coefficients. + * @gtable: Gamma coefficients. + * @wbal: White Balance parameters. + * @blk_adj: Black adjustment parameters. + * @rgb2rgb: RGB blending parameters. + * @rgb2ycbcr: RGB to ycbcr parameters. + * @hmf_params: Horizontal median filter. + * @size_params: Size parameters. + * @drkf_params: Darkframe parameters. + * @lens_shading_shift: + * @average: Downsampling rate for averager. + * @contrast: Contrast. + * @brightness: Brightness. + */ +struct prev_params { + u16 features; + enum preview_ycpos_mode pix_fmt; + struct ispprev_cfa cfa; + struct ispprev_csup csup; + u32 *ytable; + struct ispprev_nf nf; + struct ispprev_dcor dcor; + struct ispprev_gtable gtable; + struct ispprev_wbal wbal; + struct ispprev_blkadj blk_adj; + struct ispprev_rgbtorgb rgb2rgb; + struct ispprev_csc rgb2ycbcr; + struct ispprev_hmed hmf_params; + struct prev_size_params size_params; + struct prev_darkfrm_params drkf_params; + u8 lens_shading_shift; + u8 average; + u8 contrast; + u8 brightness; +}; + +/** + * struct isptables_update - Structure for Table Configuration. + * @update: Specifies which tables should be updated. + * @flag: Specifies which tables should be enabled. + * @prev_nf: Pointer to structure for Noise Filter + * @lsc: Pointer to LSC gain table. (currently not used) + * @red_gamma: Pointer to red gamma correction table. + * @green_gamma: Pointer to green gamma correction table. + * @blue_gamma: Pointer to blue gamma correction table. + */ +struct isptables_update { + u16 update; + u16 flag; + struct ispprev_nf *prev_nf; + u32 *lsc; + u32 *red_gamma; + u32 *green_gamma; + u32 *blue_gamma; + struct ispprev_cfa *prev_cfa; +}; + +/** + * struct isp_prev_device - Structure for storing ISP Preview module information + * @prevout_w: Preview output width. + * @prevout_h: Preview output height. + * @previn_w: Preview input width. + * @previn_h: Preview input height. + * @prev_inpfmt: Preview input format. + * @prev_outfmt: Preview output format. + * @hmed_en: Horizontal median filter enable. + * @nf_en: Noise filter enable. + * @dcor_en: Defect correction enable. + * @cfa_en: Color Filter Array (CFA) interpolation enable. + * @csup_en: Chrominance suppression enable. + * @yenh_en: Luma enhancement enable. + * @fmtavg: Number of horizontal pixels to average in input formatter. The + * input width should be a multiple of this number. + * @brightness: Brightness in preview module. + * @contrast: Contrast in preview module. + * @color: Color effect in preview module. + * @cfafmt: Color Filter Array (CFA) Format. + * + * This structure is used to store the OMAP ISP Preview module Information. + */ +struct isp_prev_device { + u8 update_color_matrix; + u8 update_rgb_blending; + u8 update_rgb_to_ycbcr; + u8 hmed_en; + u8 nf_en; + u8 dcor_en; + u8 cfa_en; + u8 csup_en; + u8 yenh_en; + u8 rg_update; + u8 gg_update; + u8 bg_update; + u8 cfa_update; + u8 nf_enable; + u8 nf_update; + u8 fmtavg; + u8 brightness; + u8 contrast; + enum v4l2_colorfx color; + enum cfa_fmt cfafmt; + struct ispprev_nf prev_nf_t; + struct prev_params params; + int shadow_update; + u32 sph; + u32 slv; + struct device *dev; + spinlock_t lock; +}; + +void isppreview_config_shadow_registers(struct isp_prev_device *isp_prev); + +int isppreview_request(struct isp_prev_device *isp_prev); + +int isppreview_free(struct isp_prev_device *isp_prev); + +int isppreview_config_datapath(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe); + +void isppreview_config_ycpos(struct isp_prev_device *isp_prev, + enum preview_ycpos_mode mode); + +void isppreview_config_averager(struct isp_prev_device *isp_prev, u8 average); + +void isppreview_enable_invalaw(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_enable_drkframe(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_enable_shadcomp(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_config_drkf_shadcomp(struct isp_prev_device *isp_prev, + u8 scomp_shtval); + +void isppreview_enable_gammabypass(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_enable_hmed(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_config_hmed(struct isp_prev_device *isp_prev, + struct ispprev_hmed); + +void isppreview_enable_noisefilter(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_config_noisefilter(struct isp_prev_device *isp_prev, + struct ispprev_nf prev_nf); + +void isppreview_enable_dcor(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_config_dcor(struct isp_prev_device *isp_prev, + struct ispprev_dcor prev_dcor); + + +void isppreview_config_cfa(struct isp_prev_device *isp_prev, + struct ispprev_cfa *cfa); + +void isppreview_config_gammacorrn(struct isp_prev_device *isp_prev, + struct ispprev_gtable); + +void isppreview_config_chroma_suppression(struct isp_prev_device *isp_prev, + struct ispprev_csup csup); + +void isppreview_enable_cfa(struct isp_prev_device *isp_prev, u8 enable); + +void isppreview_set_luma_enhancement(struct isp_prev_device *isp_prev, + u32 *ytable); + +void isppreview_config_luma_enhancement(struct isp_prev_device *isp_prev, + u32 *ytable); + +void isppreview_enable_luma_enhancement(struct isp_prev_device *isp_prev, + u8 enable); + +void isppreview_enable_chroma_suppression(struct isp_prev_device *isp_prev, + u8 enable); + +void isppreview_config_whitebalance(struct isp_prev_device *isp_prev, + struct ispprev_wbal); + +void isppreview_config_blkadj(struct isp_prev_device *isp_prev, + struct ispprev_blkadj); + +void isppreview_config_rgb_blending(struct isp_prev_device *isp_prev, + struct ispprev_rgbtorgb); + +void isppreview_config_rgb_to_ycbcr(struct isp_prev_device *isp_prev, + struct ispprev_csc); + +void isppreview_update_contrast(struct isp_prev_device *isp_prev, u8 *contrast); + +void isppreview_query_contrast(struct isp_prev_device *isp_prev, u8 *contrast); + +void isppreview_config_contrast(struct isp_prev_device *isp_prev, u8 contrast); + +void isppreview_get_contrast_range(u8 *min_contrast, u8 *max_contrast); + +void isppreview_update_brightness(struct isp_prev_device *isp_prev, + u8 *brightness); + +void isppreview_config_brightness(struct isp_prev_device *isp_prev, + u8 brightness); + +void isppreview_get_brightness_range(u8 *min_brightness, u8 *max_brightness); + +void isppreview_set_color(struct isp_prev_device *isp_prev, u8 *mode); + +void isppreview_get_color(struct isp_prev_device *isp_prev, u8 *mode); + +void isppreview_query_brightness(struct isp_prev_device *isp_prev, + u8 *brightness); + +void isppreview_config_yc_range(struct isp_prev_device *isp_prev, + struct ispprev_yclimit yclimit); + +int isppreview_try_pipeline(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe); + +int isppreview_s_pipeline(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe); + +int isppreview_config_inlineoffset(struct isp_prev_device *isp_prev, + u32 offset); + +int isppreview_set_inaddr(struct isp_prev_device *isp_prev, u32 addr); + +int isppreview_config_outlineoffset(struct isp_prev_device *isp_prev, + u32 offset); + +int isppreview_set_outaddr(struct isp_prev_device *isp_prev, u32 addr); + +int isppreview_config_darklineoffset(struct isp_prev_device *isp_prev, + u32 offset); + +int isppreview_set_darkaddr(struct isp_prev_device *isp_prev, u32 addr); + +void isppreview_enable(struct isp_prev_device *isp_prev); + +int isppreview_busy(struct isp_prev_device *isp_prev); + +void isppreview_print_status(struct isp_prev_device *isp_prev, + struct isp_pipeline *pipe); + +#ifndef CONFIG_ARCH_OMAP3410 +void isppreview_save_context(struct device *dev); +#else +static inline void isppreview_save_context(struct device *dev) {} +#endif + +#ifndef CONFIG_ARCH_OMAP3410 +void isppreview_restore_context(struct device *dev); +#else +static inline void isppreview_restore_context(struct device *dev) {} +#endif + +int omap34xx_isp_preview_config(struct isp_prev_device *isp_prev, + void *userspace_add); + +void isppreview_set_skip(struct isp_prev_device *isp_prev, u32 h, u32 v); + +#endif/* OMAP_ISP_PREVIEW_H */ diff --git a/drivers/media/video/isp/ispreg.h b/drivers/media/video/isp/ispreg.h new file mode 100644 index 00000000000..676a33d926b --- /dev/null +++ b/drivers/media/video/isp/ispreg.h @@ -0,0 +1,1702 @@ +/* + * ispreg.h + * + * Header file for all the ISP module in TI's OMAP3 Camera ISP. + * It has the OMAP HW register definitions. + * + * Copyright (C) 2009 Texas Instruments. + * Copyright (C) 2009 Nokia. + * + * Contributors: + * Tuukka Toivonen + * Thara Gopinath + * Sergio Aguirre + * + * 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 __ISPREG_H__ +#define __ISPREG_H__ + +#include + +/* Note: Uncomment below defines as needed for enabling module specific debug + * messages + */ + +/* + #define OMAP_ISPCTRL_DEBUG + #define OMAP_ISPCCDC_DEBUG + #define OMAP_ISPPREV_DEBUG + #define OMAP_ISPRESZ_DEBUG + #define OMAP_ISPMMU_DEBUG + #define OMAP_ISPH3A_DEBUG + #define OMAP_ISP_AF_DEBUG + #define OMAP_ISPHIST_DEBUG +*/ + +#ifdef OMAP_ISPCTRL_DEBUG +#define DPRINTK_ISPCTRL(format, ...) \ + printk(KERN_INFO "ISPCTRL: " format, ## __VA_ARGS__) +#define is_ispctrl_debug_enabled() 1 +#else +#define DPRINTK_ISPCTRL(format, ...) +#define is_ispctrl_debug_enabled() 0 +#endif + +#ifdef OMAP_ISPCCDC_DEBUG +#define DPRINTK_ISPCCDC(format, ...) \ + printk(KERN_INFO "ISPCCDC: " format, ## __VA_ARGS__) +#define is_ispccdc_debug_enabled() 1 +#else +#define DPRINTK_ISPCCDC(format, ...) +#define is_ispccdc_debug_enabled() 0 +#endif + +#ifdef OMAP_ISPPREV_DEBUG +#define DPRINTK_ISPPREV(format, ...) \ + printk(KERN_INFO "ISPPREV: " format, ## __VA_ARGS__) +#define is_ispprev_debug_enabled() 1 +#else +#define DPRINTK_ISPPREV(format, ...) +#define is_ispprev_debug_enabled() 0 +#endif + +#ifdef OMAP_ISPRESZ_DEBUG +#define DPRINTK_ISPRESZ(format, ...) \ + printk(KERN_INFO "ISPRESZ: " format, ## __VA_ARGS__) +#define is_ispresz_debug_enabled() 1 +#else +#define DPRINTK_ISPRESZ(format, ...) +#define is_ispresz_debug_enabled() 0 +#endif + +#ifdef OMAP_ISPMMU_DEBUG +#define DPRINTK_ISPMMU(format, ...) \ + printk(KERN_INFO "ISPMMU: " format, ## __VA_ARGS__) +#define is_ispmmu_debug_enabled() 1 +#else +#define DPRINTK_ISPMMU(format, ...) +#define is_ispmmu_debug_enabled() 0 +#endif + +#ifdef OMAP_ISPH3A_DEBUG +#define DPRINTK_ISPH3A(format, ...) \ + printk(KERN_INFO "ISPH3A: " format, ## __VA_ARGS__) +#define is_isph3a_debug_enabled() 1 +#else +#define DPRINTK_ISPH3A(format, ...) +#define is_isph3a_debug_enabled() 0 +#endif + +#ifdef OMAP_ISP_AF_DEBUG +#define DPRINTK_ISP_AF(format, ...) \ + printk(KERN_INFO "ISP_AF: " format, ## __VA_ARGS__) +#define is_isp_af_debug_enabled() 1 +#else +#define DPRINTK_ISP_AF(format, ...) +#define is_isp_af_debug_enabled() 0 +#endif + +#ifdef OMAP_ISPHIST_DEBUG +#define DPRINTK_ISPHIST(format, ...) \ + printk(KERN_INFO "ISPHIST: " format, ## __VA_ARGS__) +#define is_isphist_debug_enabled() 1 +#else +#define DPRINTK_ISPHIST(format, ...) +#define is_isphist_debug_enabled() 0 +#endif + +#define ISP_32B_BOUNDARY_BUF 0xFFFFFFE0 +#define ISP_32B_BOUNDARY_OFFSET 0x0000FFE0 + +#define CM_CAM_MCLK_HZ 216000000 + +/* ISP Submodules offset */ + +#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE +#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset)) + +#define OMAP3ISP_CBUFF_REG_OFFSET 0x0100 +#define OMAP3ISP_CBUFF_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CBUFF_REG_OFFSET) +#define OMAP3ISP_CBUFF_REG(offset) (OMAP3ISP_CBUFF_REG_BASE + (offset)) + +#define OMAP3ISP_CCP2_REG_OFFSET 0x0400 +#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CCP2_REG_OFFSET) +#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset)) + +#define OMAP3ISP_CCDC_REG_OFFSET 0x0600 +#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CCDC_REG_OFFSET) +#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset)) + +#define OMAP3ISP_HIST_REG_OFFSET 0x0A00 +#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_HIST_REG_OFFSET) +#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset)) + +#define OMAP3ISP_H3A_REG_OFFSET 0x0C00 +#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_H3A_REG_OFFSET) +#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset)) + +#define OMAP3ISP_PREV_REG_OFFSET 0x0E00 +#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_PREV_REG_OFFSET) +#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset)) + +#define OMAP3ISP_RESZ_REG_OFFSET 0x1000 +#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_RESZ_REG_OFFSET) +#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset)) + +#define OMAP3ISP_SBL_REG_OFFSET 0x1200 +#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_SBL_REG_OFFSET) +#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset)) + +#define OMAP3ISP_MMU_REG_OFFSET 0x1400 +#define OMAP3ISP_MMU_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_MMU_REG_OFFSET) +#define OMAP3ISP_MMU_REG(offset) (OMAP3ISP_MMU_REG_BASE + (offset)) + +#define OMAP3ISP_CSI2A_REG_OFFSET 0x1800 +#define OMAP3ISP_CSI2A_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSI2A_REG_OFFSET) +#define OMAP3ISP_CSI2A_REG(offset) (OMAP3ISP_CSI2A_REG_BASE + (offset)) + +#define OMAP3ISP_CSI2PHY_REG_OFFSET 0x1970 +#define OMAP3ISP_CSI2PHY_REG_BASE (OMAP3ISP_REG_BASE + \ + OMAP3ISP_CSI2PHY_REG_OFFSET) +#define OMAP3ISP_CSI2PHY_REG(offset) (OMAP3ISP_CSI2PHY_REG_BASE + (offset)) + +/* ISP module register offset */ + +#define ISP_REVISION (0x000) +#define ISP_SYSCONFIG (0x004) +#define ISP_SYSSTATUS (0x008) +#define ISP_IRQ0ENABLE (0x00C) +#define ISP_IRQ0STATUS (0x010) +#define ISP_IRQ1ENABLE (0x014) +#define ISP_IRQ1STATUS (0x018) +#define ISP_TCTRL_GRESET_LENGTH (0x030) +#define ISP_TCTRL_PSTRB_REPLAY (0x034) +#define ISP_CTRL (0x040) +#define ISP_SECURE (0x044) +#define ISP_TCTRL_CTRL (0x050) +#define ISP_TCTRL_FRAME (0x054) +#define ISP_TCTRL_PSTRB_DELAY (0x058) +#define ISP_TCTRL_STRB_DELAY (0x05C) +#define ISP_TCTRL_SHUT_DELAY (0x060) +#define ISP_TCTRL_PSTRB_LENGTH (0x064) +#define ISP_TCTRL_STRB_LENGTH (0x068) +#define ISP_TCTRL_SHUT_LENGTH (0x06C) +#define ISP_PING_PONG_ADDR (0x070) +#define ISP_PING_PONG_MEM_RANGE (0x074) +#define ISP_PING_PONG_BUF_SIZE (0x078) + +/* CSI1 receiver registers (ES2.0) */ +#define ISPCSI1_REVISION (0x000) +#define ISPCSI1_SYSCONFIG (0x004) +#define ISPCSI1_SYSSTATUS (0x008) +#define ISPCSI1_LC01_IRQENABLE (0x00C) +#define ISPCSI1_LC01_IRQSTATUS (0x010) +#define ISPCSI1_LC01_IRQSTATUS_LC0_FS_IRQ (1 << 11) +#define ISPCSI1_LC01_IRQSTATUS_LC0_LE_IRQ (1 << 10) +#define ISPCSI1_LC01_IRQSTATUS_LC0_LS_IRQ (1 << 9) +#define ISPCSI1_LC01_IRQSTATUS_LC0_FE_IRQ (1 << 8) +#define ISPCSI1_LC01_IRQSTATUS_LC0_COUNT_IRQ (1 << 7) +#define ISPCSI1_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ (1 << 5) +#define ISPCSI1_LC01_IRQSTATUS_LC0_CRC_IRQ (1 << 4) +#define ISPCSI1_LC01_IRQSTATUS_LC0_FSP_IRQ (1 << 3) +#define ISPCSI1_LC01_IRQSTATUS_LC0_FW_IRQ (1 << 2) +#define ISPCSI1_LC01_IRQSTATUS_LC0_FSC_IRQ (1 << 1) +#define ISPCSI1_LC01_IRQSTATUS_LC0_SSC_IRQ (1 << 0) + +#define ISPCSI1_LC23_IRQENABLE (0x014) +#define ISPCSI1_LC23_IRQSTATUS (0x018) +#define ISPCSI1_LCM_IRQENABLE (0x02C) +#define ISPCSI1_LCM_IRQSTATUS (0x030) +#define ISPCSI1_CTRL (0x040) +#define ISPCSI1_DBG (0x044) +#define ISPCSI1_GNQ (0x048) +#define ISPCSI1_LCx_CTRL(x) ((0x050)+0x30*(x)) +#define ISPCSI1_LCx_CODE(x) ((0x054)+0x30*(x)) +#define ISPCSI1_LCx_STAT_START(x) ((0x058)+0x30*(x)) +#define ISPCSI1_LCx_STAT_SIZE(x) ((0x05C)+0x30*(x)) +#define ISPCSI1_LCx_SOF_ADDR(x) ((0x060)+0x30*(x)) +#define ISPCSI1_LCx_EOF_ADDR(x) ((0x064)+0x30*(x)) +#define ISPCSI1_LCx_DAT_START(x) ((0x068)+0x30*(x)) +#define ISPCSI1_LCx_DAT_SIZE(x) ((0x06C)+0x30*(x)) +#define ISPCSI1_LCx_DAT_PING_ADDR(x) ((0x070)+0x30*(x)) +#define ISPCSI1_LCx_DAT_PONG_ADDR(x) ((0x074)+0x30*(x)) +#define ISPCSI1_LCx_DAT_OFST(x) ((0x078)+0x30*(x)) +#define ISPCSI1_LCM_CTRL (0x1D0) +#define ISPCSI1_LCM_VSIZE (0x1D4) +#define ISPCSI1_LCM_HSIZE (0x1D8) +#define ISPCSI1_LCM_PREFETCH (0x1DC) +#define ISPCSI1_LCM_SRC_ADDR (0x1E0) +#define ISPCSI1_LCM_SRC_OFST (0x1E4) +#define ISPCSI1_LCM_DST_ADDR (0x1E8) +#define ISPCSI1_LCM_DST_OFST (0x1EC) +#define ISP_CSIB_SYSCONFIG ISPCSI1_SYSCONFIG +#define ISP_CSIA_SYSCONFIG ISPCSI2_SYSCONFIG + +/* ISP_CBUFF Registers */ + +#define ISP_CBUFF_SYSCONFIG (0x010) +#define ISP_CBUFF_IRQENABLE (0x01C) + +#define ISP_CBUFF0_CTRL (0x020) +#define ISP_CBUFF1_CTRL (0x024) + +#define ISP_CBUFF0_START (0x040) +#define ISP_CBUFF1_START (0x044) + +#define ISP_CBUFF0_END (0x050) +#define ISP_CBUFF1_END (0x054) + +#define ISP_CBUFF0_WINDOWSIZE (0x060) +#define ISP_CBUFF1_WINDOWSIZE (0x064) + +#define ISP_CBUFF0_THRESHOLD (0x070) +#define ISP_CBUFF1_THRESHOLD (0x074) + +/* CCDC module register offset */ + +#define ISPCCDC_PID (0x000) +#define ISPCCDC_PCR (0x004) +#define ISPCCDC_SYN_MODE (0x008) +#define ISPCCDC_HD_VD_WID (0x00C) +#define ISPCCDC_PIX_LINES (0x010) +#define ISPCCDC_HORZ_INFO (0x014) +#define ISPCCDC_VERT_START (0x018) +#define ISPCCDC_VERT_LINES (0x01C) +#define ISPCCDC_CULLING (0x020) +#define ISPCCDC_HSIZE_OFF (0x024) +#define ISPCCDC_SDOFST (0x028) +#define ISPCCDC_SDR_ADDR (0x02C) +#define ISPCCDC_CLAMP (0x030) +#define ISPCCDC_DCSUB (0x034) +#define ISPCCDC_COLPTN (0x038) +#define ISPCCDC_BLKCMP (0x03C) +#define ISPCCDC_FPC (0x040) +#define ISPCCDC_FPC_ADDR (0x044) +#define ISPCCDC_VDINT (0x048) +#define ISPCCDC_ALAW (0x04C) +#define ISPCCDC_REC656IF (0x050) +#define ISPCCDC_CFG (0x054) +#define ISPCCDC_FMTCFG (0x058) +#define ISPCCDC_FMT_HORZ (0x05C) +#define ISPCCDC_FMT_VERT (0x060) +#define ISPCCDC_FMT_ADDR0 (0x064) +#define ISPCCDC_FMT_ADDR1 (0x068) +#define ISPCCDC_FMT_ADDR2 (0x06C) +#define ISPCCDC_FMT_ADDR3 (0x070) +#define ISPCCDC_FMT_ADDR4 (0x074) +#define ISPCCDC_FMT_ADDR5 (0x078) +#define ISPCCDC_FMT_ADDR6 (0x07C) +#define ISPCCDC_FMT_ADDR7 (0x080) +#define ISPCCDC_PRGEVEN0 (0x084) +#define ISPCCDC_PRGEVEN1 (0x088) +#define ISPCCDC_PRGODD0 (0x08C) +#define ISPCCDC_PRGODD1 (0x090) +#define ISPCCDC_VP_OUT (0x094) + +#define ISPCCDC_LSC_CONFIG (0x098) +#define ISPCCDC_LSC_INITIAL (0x09C) +#define ISPCCDC_LSC_TABLE_BASE (0x0A0) +#define ISPCCDC_LSC_TABLE_OFFSET (0x0A4) + +/* SBL */ +#define ISPSBL_PCR 0x4 +#define ISPSBL_PCR_H3A_AEAWB_WBL_OVF (1 << 16) +#define ISPSBL_PCR_H3A_AF_WBL_OVF (1 << 17) +#define ISPSBL_PCR_RSZ4_WBL_OVF (1 << 18) +#define ISPSBL_PCR_RSZ3_WBL_OVF (1 << 19) +#define ISPSBL_PCR_RSZ2_WBL_OVF (1 << 20) +#define ISPSBL_PCR_RSZ1_WBL_OVF (1 << 21) +#define ISPSBL_PCR_PRV_WBL_OVF (1 << 22) +#define ISPSBL_PCR_CCDC_WBL_OVF (1 << 23) +#define ISPSBL_PCR_CCDCPRV_2_RSZ_OVF (1 << 24) +#define ISPSBL_PCR_CSIA_WBL_OVF (1 << 25) +#define ISPSBL_PCR_CSIB_WBL_OVF (1 << 26) +#define ISPSBL_CCDC_WR_0 (0x028) +#define ISPSBL_CCDC_WR_0_DATA_READY (1 << 21) +#define ISPSBL_CCDC_WR_1 (0x02C) +#define ISPSBL_CCDC_WR_2 (0x030) +#define ISPSBL_CCDC_WR_3 (0x034) + +/* Histogram registers */ +#define ISPHIST_PID (0x000) +#define ISPHIST_PCR (0x004) +#define ISPHIST_CNT (0x008) +#define ISPHIST_WB_GAIN (0x00C) +#define ISPHIST_R0_HORZ (0x010) +#define ISPHIST_R0_VERT (0x014) +#define ISPHIST_R1_HORZ (0x018) +#define ISPHIST_R1_VERT (0x01C) +#define ISPHIST_R2_HORZ (0x020) +#define ISPHIST_R2_VERT (0x024) +#define ISPHIST_R3_HORZ (0x028) +#define ISPHIST_R3_VERT (0x02C) +#define ISPHIST_ADDR (0x030) +#define ISPHIST_DATA (0x034) +#define ISPHIST_RADD (0x038) +#define ISPHIST_RADD_OFF (0x03C) +#define ISPHIST_H_V_INFO (0x040) + +/* H3A module registers */ +#define ISPH3A_PID (0x000) +#define ISPH3A_PCR (0x004) +#define ISPH3A_AEWWIN1 (0x04C) +#define ISPH3A_AEWINSTART (0x050) +#define ISPH3A_AEWINBLK (0x054) +#define ISPH3A_AEWSUBWIN (0x058) +#define ISPH3A_AEWBUFST (0x05C) +#define ISPH3A_AFPAX1 (0x008) +#define ISPH3A_AFPAX2 (0x00C) +#define ISPH3A_AFPAXSTART (0x010) +#define ISPH3A_AFIIRSH (0x014) +#define ISPH3A_AFBUFST (0x018) +#define ISPH3A_AFCOEF010 (0x01C) +#define ISPH3A_AFCOEF032 (0x020) +#define ISPH3A_AFCOEF054 (0x024) +#define ISPH3A_AFCOEF076 (0x028) +#define ISPH3A_AFCOEF098 (0x02C) +#define ISPH3A_AFCOEF0010 (0x030) +#define ISPH3A_AFCOEF110 (0x034) +#define ISPH3A_AFCOEF132 (0x038) +#define ISPH3A_AFCOEF154 (0x03C) +#define ISPH3A_AFCOEF176 (0x040) +#define ISPH3A_AFCOEF198 (0x044) +#define ISPH3A_AFCOEF1010 (0x048) + +#define ISPPRV_PCR (0x004) +#define ISPPRV_HORZ_INFO (0x008) +#define ISPPRV_VERT_INFO (0x00C) +#define ISPPRV_RSDR_ADDR (0x010) +#define ISPPRV_RADR_OFFSET (0x014) +#define ISPPRV_DSDR_ADDR (0x018) +#define ISPPRV_DRKF_OFFSET (0x01C) +#define ISPPRV_WSDR_ADDR (0x020) +#define ISPPRV_WADD_OFFSET (0x024) +#define ISPPRV_AVE (0x028) +#define ISPPRV_HMED (0x02C) +#define ISPPRV_NF (0x030) +#define ISPPRV_WB_DGAIN (0x034) +#define ISPPRV_WBGAIN (0x038) +#define ISPPRV_WBSEL (0x03C) +#define ISPPRV_CFA (0x040) +#define ISPPRV_BLKADJOFF (0x044) +#define ISPPRV_RGB_MAT1 (0x048) +#define ISPPRV_RGB_MAT2 (0x04C) +#define ISPPRV_RGB_MAT3 (0x050) +#define ISPPRV_RGB_MAT4 (0x054) +#define ISPPRV_RGB_MAT5 (0x058) +#define ISPPRV_RGB_OFF1 (0x05C) +#define ISPPRV_RGB_OFF2 (0x060) +#define ISPPRV_CSC0 (0x064) +#define ISPPRV_CSC1 (0x068) +#define ISPPRV_CSC2 (0x06C) +#define ISPPRV_CSC_OFFSET (0x070) +#define ISPPRV_CNT_BRT (0x074) +#define ISPPRV_CSUP (0x078) +#define ISPPRV_SETUP_YC (0x07C) +#define ISPPRV_SET_TBL_ADDR (0x080) +#define ISPPRV_SET_TBL_DATA (0x084) +#define ISPPRV_CDC_THR0 (0x090) +#define ISPPRV_CDC_THR1 (ISPPRV_CDC_THR0 + (0x4)) +#define ISPPRV_CDC_THR2 (ISPPRV_CDC_THR0 + (0x4) * 2) +#define ISPPRV_CDC_THR3 (ISPPRV_CDC_THR0 + (0x4) * 3) + +#define ISPPRV_REDGAMMA_TABLE_ADDR 0x0000 +#define ISPPRV_GREENGAMMA_TABLE_ADDR 0x0400 +#define ISPPRV_BLUEGAMMA_TABLE_ADDR 0x0800 +#define ISPPRV_NF_TABLE_ADDR 0x0C00 +#define ISPPRV_YENH_TABLE_ADDR 0x1000 +#define ISPPRV_CFA_TABLE_ADDR 0x1400 + +#define ISPPRV_MAXOUTPUT_WIDTH 1280 +#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300 +#define ISPRSZ_MIN_OUTPUT 64 +#define ISPRSZ_MAX_OUTPUT 3312 + +/* Resizer module register offset */ +#define ISPRSZ_PID (0x000) +#define ISPRSZ_PCR (0x004) +#define ISPRSZ_CNT (0x008) +#define ISPRSZ_OUT_SIZE (0x00C) +#define ISPRSZ_IN_START (0x010) +#define ISPRSZ_IN_SIZE (0x014) +#define ISPRSZ_SDR_INADD (0x018) +#define ISPRSZ_SDR_INOFF (0x01C) +#define ISPRSZ_SDR_OUTADD (0x020) +#define ISPRSZ_SDR_OUTOFF (0x024) +#define ISPRSZ_HFILT10 (0x028) +#define ISPRSZ_HFILT32 (0x02C) +#define ISPRSZ_HFILT54 (0x030) +#define ISPRSZ_HFILT76 (0x034) +#define ISPRSZ_HFILT98 (0x038) +#define ISPRSZ_HFILT1110 (0x03C) +#define ISPRSZ_HFILT1312 (0x040) +#define ISPRSZ_HFILT1514 (0x044) +#define ISPRSZ_HFILT1716 (0x048) +#define ISPRSZ_HFILT1918 (0x04C) +#define ISPRSZ_HFILT2120 (0x050) +#define ISPRSZ_HFILT2322 (0x054) +#define ISPRSZ_HFILT2524 (0x058) +#define ISPRSZ_HFILT2726 (0x05C) +#define ISPRSZ_HFILT2928 (0x060) +#define ISPRSZ_HFILT3130 (0x064) +#define ISPRSZ_VFILT10 (0x068) +#define ISPRSZ_VFILT32 (0x06C) +#define ISPRSZ_VFILT54 (0x070) +#define ISPRSZ_VFILT76 (0x074) +#define ISPRSZ_VFILT98 (0x078) +#define ISPRSZ_VFILT1110 (0x07C) +#define ISPRSZ_VFILT1312 (0x080) +#define ISPRSZ_VFILT1514 (0x084) +#define ISPRSZ_VFILT1716 (0x088) +#define ISPRSZ_VFILT1918 (0x08C) +#define ISPRSZ_VFILT2120 (0x090) +#define ISPRSZ_VFILT2322 (0x094) +#define ISPRSZ_VFILT2524 (0x098) +#define ISPRSZ_VFILT2726 (0x09C) +#define ISPRSZ_VFILT2928 (0x0A0) +#define ISPRSZ_VFILT3130 (0x0A4) +#define ISPRSZ_YENH (0x0A8) + +/* MMU module registers */ +#define ISPMMU_REVISION (0x000) +#define ISPMMU_SYSCONFIG (0x010) +#define ISPMMU_SYSSTATUS (0x014) +#define ISPMMU_IRQSTATUS (0x018) +#define ISPMMU_IRQENABLE (0x01C) +#define ISPMMU_WALKING_ST (0x040) +#define ISPMMU_CNTL (0x044) +#define ISPMMU_FAULT_AD (0x048) +#define ISPMMU_TTB (0x04C) +#define ISPMMU_LOCK (0x050) +#define ISPMMU_LD_TLB (0x054) +#define ISPMMU_CAM (0x058) +#define ISPMMU_RAM (0x05C) +#define ISPMMU_GFLUSH (0x060) +#define ISPMMU_FLUSH_ENTRY (0x064) +#define ISPMMU_READ_CAM (0x068) +#define ISPMMU_READ_RAM (0x06c) +#define ISPMMU_EMU_FAULT_AD (0x070) + +#define ISP_INT_CLR 0xFF113F11 +#define ISPPRV_PCR_EN 1 +#define ISPPRV_PCR_BUSY (1 << 1) +#define ISPPRV_PCR_SOURCE (1 << 2) +#define ISPPRV_PCR_ONESHOT (1 << 3) +#define ISPPRV_PCR_WIDTH (1 << 4) +#define ISPPRV_PCR_INVALAW (1 << 5) +#define ISPPRV_PCR_DRKFEN (1 << 6) +#define ISPPRV_PCR_DRKFCAP (1 << 7) +#define ISPPRV_PCR_HMEDEN (1 << 8) +#define ISPPRV_PCR_NFEN (1 << 9) +#define ISPPRV_PCR_CFAEN (1 << 10) +#define ISPPRV_PCR_CFAFMT_SHIFT 11 +#define ISPPRV_PCR_CFAFMT_MASK 0x7800 +#define ISPPRV_PCR_CFAFMT_BAYER (0 << 11) +#define ISPPRV_PCR_CFAFMT_SONYVGA (1 << 11) +#define ISPPRV_PCR_CFAFMT_RGBFOVEON (2 << 11) +#define ISPPRV_PCR_CFAFMT_DNSPL (3 << 11) +#define ISPPRV_PCR_CFAFMT_HONEYCOMB (4 << 11) +#define ISPPRV_PCR_CFAFMT_RRGGBBFOVEON (5 << 11) +#define ISPPRV_PCR_YNENHEN (1 << 15) +#define ISPPRV_PCR_SUPEN (1 << 16) +#define ISPPRV_PCR_YCPOS_SHIFT 17 +#define ISPPRV_PCR_YCPOS_YCrYCb (0 << 17) +#define ISPPRV_PCR_YCPOS_YCbYCr (1 << 17) +#define ISPPRV_PCR_YCPOS_CbYCrY (2 << 17) +#define ISPPRV_PCR_YCPOS_CrYCbY (3 << 17) +#define ISPPRV_PCR_RSZPORT (1 << 19) +#define ISPPRV_PCR_SDRPORT (1 << 20) +#define ISPPRV_PCR_SCOMP_EN (1 << 21) +#define ISPPRV_PCR_SCOMP_SFT_SHIFT (22) +#define ISPPRV_PCR_SCOMP_SFT_MASK (~(7 << 22)) +#define ISPPRV_PCR_GAMMA_BYPASS (1 << 26) +#define ISPPRV_PCR_DCOREN (1 << 27) +#define ISPPRV_PCR_DCCOUP (1 << 28) +#define ISPPRV_PCR_DRK_FAIL (1 << 31) + +#define ISPPRV_HORZ_INFO_EPH_SHIFT 0 +#define ISPPRV_HORZ_INFO_EPH_MASK 0x3fff +#define ISPPRV_HORZ_INFO_SPH_SHIFT 16 +#define ISPPRV_HORZ_INFO_SPH_MASK 0x3fff0 + +#define ISPPRV_VERT_INFO_ELV_SHIFT 0 +#define ISPPRV_VERT_INFO_ELV_MASK 0x3fff +#define ISPPRV_VERT_INFO_SLV_SHIFT 16 +#define ISPPRV_VERT_INFO_SLV_MASK 0x3fff0 + +#define ISPPRV_AVE_EVENDIST_SHIFT 2 +#define ISPPRV_AVE_EVENDIST_1 0x0 +#define ISPPRV_AVE_EVENDIST_2 0x1 +#define ISPPRV_AVE_EVENDIST_3 0x2 +#define ISPPRV_AVE_EVENDIST_4 0x3 +#define ISPPRV_AVE_ODDDIST_SHIFT 4 +#define ISPPRV_AVE_ODDDIST_1 0x0 +#define ISPPRV_AVE_ODDDIST_2 0x1 +#define ISPPRV_AVE_ODDDIST_3 0x2 +#define ISPPRV_AVE_ODDDIST_4 0x3 + +#define ISPPRV_HMED_THRESHOLD_SHIFT 0 +#define ISPPRV_HMED_EVENDIST (1 << 8) +#define ISPPRV_HMED_ODDDIST (1 << 9) + +#define ISPPRV_WBGAIN_COEF0_SHIFT 0 +#define ISPPRV_WBGAIN_COEF1_SHIFT 8 +#define ISPPRV_WBGAIN_COEF2_SHIFT 16 +#define ISPPRV_WBGAIN_COEF3_SHIFT 24 + +#define ISPPRV_WBSEL_COEF0 0x0 +#define ISPPRV_WBSEL_COEF1 0x1 +#define ISPPRV_WBSEL_COEF2 0x2 +#define ISPPRV_WBSEL_COEF3 0x3 + +#define ISPPRV_WBSEL_N0_0_SHIFT 0 +#define ISPPRV_WBSEL_N0_1_SHIFT 2 +#define ISPPRV_WBSEL_N0_2_SHIFT 4 +#define ISPPRV_WBSEL_N0_3_SHIFT 6 +#define ISPPRV_WBSEL_N1_0_SHIFT 8 +#define ISPPRV_WBSEL_N1_1_SHIFT 10 +#define ISPPRV_WBSEL_N1_2_SHIFT 12 +#define ISPPRV_WBSEL_N1_3_SHIFT 14 +#define ISPPRV_WBSEL_N2_0_SHIFT 16 +#define ISPPRV_WBSEL_N2_1_SHIFT 18 +#define ISPPRV_WBSEL_N2_2_SHIFT 20 +#define ISPPRV_WBSEL_N2_3_SHIFT 22 +#define ISPPRV_WBSEL_N3_0_SHIFT 24 +#define ISPPRV_WBSEL_N3_1_SHIFT 26 +#define ISPPRV_WBSEL_N3_2_SHIFT 28 +#define ISPPRV_WBSEL_N3_3_SHIFT 30 + +#define ISPPRV_CFA_GRADTH_HOR_SHIFT 0 +#define ISPPRV_CFA_GRADTH_VER_SHIFT 8 + +#define ISPPRV_BLKADJOFF_B_SHIFT 0 +#define ISPPRV_BLKADJOFF_G_SHIFT 8 +#define ISPPRV_BLKADJOFF_R_SHIFT 16 + +#define ISPPRV_RGB_MAT1_MTX_RR_SHIFT 0 +#define ISPPRV_RGB_MAT1_MTX_GR_SHIFT 16 + +#define ISPPRV_RGB_MAT2_MTX_BR_SHIFT 0 +#define ISPPRV_RGB_MAT2_MTX_RG_SHIFT 16 + +#define ISPPRV_RGB_MAT3_MTX_GG_SHIFT 0 +#define ISPPRV_RGB_MAT3_MTX_BG_SHIFT 16 + +#define ISPPRV_RGB_MAT4_MTX_RB_SHIFT 0 +#define ISPPRV_RGB_MAT4_MTX_GB_SHIFT 16 + +#define ISPPRV_RGB_MAT5_MTX_BB_SHIFT 0 + +#define ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT 0 +#define ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT 16 + +#define ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT 0 + +#define ISPPRV_CSC0_RY_SHIFT 0 +#define ISPPRV_CSC0_GY_SHIFT 10 +#define ISPPRV_CSC0_BY_SHIFT 20 + +#define ISPPRV_CSC1_RCB_SHIFT 0 +#define ISPPRV_CSC1_GCB_SHIFT 10 +#define ISPPRV_CSC1_BCB_SHIFT 20 + +#define ISPPRV_CSC2_RCR_SHIFT 0 +#define ISPPRV_CSC2_GCR_SHIFT 10 +#define ISPPRV_CSC2_BCR_SHIFT 20 + +#define ISPPRV_CSC_OFFSET_CR_SHIFT 0 +#define ISPPRV_CSC_OFFSET_CB_SHIFT 8 +#define ISPPRV_CSC_OFFSET_Y_SHIFT 16 + +#define ISPPRV_CNT_BRT_BRT_SHIFT 0 +#define ISPPRV_CNT_BRT_CNT_SHIFT 8 + +#define ISPPRV_CONTRAST_MAX 0x10 +#define ISPPRV_CONTRAST_MIN 0xFF +#define ISPPRV_BRIGHT_MIN 0x00 +#define ISPPRV_BRIGHT_MAX 0xFF + +#define ISPPRV_CSUP_CSUPG_SHIFT 0 +#define ISPPRV_CSUP_THRES_SHIFT 8 +#define ISPPRV_CSUP_HPYF_SHIFT 16 + +#define ISPPRV_SETUP_YC_MINC_SHIFT 0 +#define ISPPRV_SETUP_YC_MAXC_SHIFT 8 +#define ISPPRV_SETUP_YC_MINY_SHIFT 16 +#define ISPPRV_SETUP_YC_MAXY_SHIFT 24 +#define ISPPRV_YC_MAX 0xFF +#define ISPPRV_YC_MIN 0x0 + +/* Define bit fields within selected registers */ +#define ISP_REVISION_SHIFT 0 + +#define ISP_SYSCONFIG_AUTOIDLE 0 +#define ISP_SYSCONFIG_SOFTRESET (1 << 1) +#define ISP_SYSCONFIG_MIDLEMODE_SHIFT 12 +#define ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY 0x0 +#define ISP_SYSCONFIG_MIDLEMODE_NOSTANBY 0x1 +#define ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY 0x2 + +#define ISP_SYSSTATUS_RESETDONE 0 + +#define IRQ0ENABLE_CSIA_IRQ 1 +#define IRQ0ENABLE_CSIA_LC1_IRQ (1 << 1) +#define IRQ0ENABLE_CSIA_LC2_IRQ (1 << 2) +#define IRQ0ENABLE_CSIA_LC3_IRQ (1 << 3) +#define IRQ0ENABLE_CSIB_IRQ (1 << 4) +#define IRQ0ENABLE_CSIB_LC1_IRQ (1 << 5) +#define IRQ0ENABLE_CSIB_LC2_IRQ (1 << 6) +#define IRQ0ENABLE_CSIB_LC3_IRQ (1 << 7) +#define IRQ0ENABLE_CCDC_VD0_IRQ (1 << 8) +#define IRQ0ENABLE_CCDC_VD1_IRQ (1 << 9) +#define IRQ0ENABLE_CCDC_VD2_IRQ (1 << 10) +#define IRQ0ENABLE_CCDC_ERR_IRQ (1 << 11) +#define IRQ0ENABLE_H3A_AF_DONE_IRQ (1 << 12) +#define IRQ0ENABLE_H3A_AWB_DONE_IRQ (1 << 13) +#define IRQ0ENABLE_HIST_DONE_IRQ (1 << 16) +#define IRQ0ENABLE_CCDC_LSC_DONE_IRQ (1 << 17) +#define IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ (1 << 18) +#define IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ (1 << 19) +#define IRQ0ENABLE_PRV_DONE_IRQ (1 << 20) +#define IRQ0ENABLE_RSZ_DONE_IRQ (1 << 24) +#define IRQ0ENABLE_OVF_IRQ (1 << 25) +#define IRQ0ENABLE_PING_IRQ (1 << 26) +#define IRQ0ENABLE_PONG_IRQ (1 << 27) +#define IRQ0ENABLE_MMU_ERR_IRQ (1 << 28) +#define IRQ0ENABLE_OCP_ERR_IRQ (1 << 29) +#define IRQ0ENABLE_SEC_ERR_IRQ (1 << 30) +#define IRQ0ENABLE_HS_VS_IRQ (1 << 31) + +#define IRQ0STATUS_CSIA_IRQ 1 +#define IRQ0STATUS_CSIA_LC1_IRQ (1 << 1) +#define IRQ0STATUS_CSIA_LC2_IRQ (1 << 2) +#define IRQ0STATUS_CSIA_LC3_IRQ (1 << 3) +#define IRQ0STATUS_CSIB_IRQ (1 << 4) +#define IRQ0STATUS_CSIB_LC1_IRQ (1 << 5) +#define IRQ0STATUS_CSIB_LC2_IRQ (1 << 6) +#define IRQ0STATUS_CSIB_LC3_IRQ (1 << 7) +#define IRQ0STATUS_CCDC_VD0_IRQ (1 << 8) +#define IRQ0STATUS_CCDC_VD1_IRQ (1 << 9) +#define IRQ0STATUS_CCDC_VD2_IRQ (1 << 10) +#define IRQ0STATUS_CCDC_ERR_IRQ (1 << 11) +#define IRQ0STATUS_H3A_AF_DONE_IRQ (1 << 12) +#define IRQ0STATUS_H3A_AWB_DONE_IRQ (1 << 13) +#define IRQ0STATUS_HIST_DONE_IRQ (1 << 16) +#define IRQ0STATUS_PRV_DONE_IRQ (1 << 20) +#define IRQ0STATUS_RSZ_DONE_IRQ (1 << 24) +#define IRQ0STATUS_OVF_IRQ (1 << 25) +#define IRQ0STATUS_PING_IRQ (1 << 26) +#define IRQ0STATUS_PONG_IRQ (1 << 27) +#define IRQ0STATUS_MMU_ERR_IRQ (1 << 28) +#define IRQ0STATUS_OCP_ERR_IRQ (1 << 29) +#define IRQ0STATUS_SEC_ERR_IRQ (1 << 30) +#define IRQ0STATUS_HS_VS_IRQ (1 << 31) + +#define TCTRL_GRESET_LEN 0 + +#define TCTRL_PSTRB_REPLAY_DELAY 0 +#define TCTRL_PSTRB_REPLAY_COUNTER_SHIFT 25 + +#define ISPCTRL_PAR_SER_CLK_SEL_PARALLEL 0x0 +#define ISPCTRL_PAR_SER_CLK_SEL_CSIA 0x1 +#define ISPCTRL_PAR_SER_CLK_SEL_CSIB 0x2 +#define ISPCTRL_PAR_SER_CLK_SEL_MASK 0xFFFFFFFC + +#define ISPCTRL_PAR_BRIDGE_SHIFT 2 +#define ISPCTRL_PAR_BRIDGE_DISABLE (0x0 << 2) +#define ISPCTRL_PAR_BRIDGE_LENDIAN (0x2 << 2) +#define ISPCTRL_PAR_BRIDGE_BENDIAN (0x3 << 2) + +#define ISPCTRL_PAR_CLK_POL_SHIFT 4 +#define ISPCTRL_PAR_CLK_POL_INV (1 << 4) +#define ISPCTRL_PING_PONG_EN (1 << 5) +#define ISPCTRL_SHIFT_SHIFT 6 +#define ISPCTRL_SHIFT_0 (0x0 << 6) +#define ISPCTRL_SHIFT_2 (0x1 << 6) +#define ISPCTRL_SHIFT_4 (0x2 << 6) +#define ISPCTRL_SHIFT_MASK (~(0x3 << 6)) + +#define ISPCTRL_CCDC_CLK_EN (1 << 8) +#define ISPCTRL_SCMP_CLK_EN (1 << 9) +#define ISPCTRL_H3A_CLK_EN (1 << 10) +#define ISPCTRL_HIST_CLK_EN (1 << 11) +#define ISPCTRL_PREV_CLK_EN (1 << 12) +#define ISPCTRL_RSZ_CLK_EN (1 << 13) +#define ISPCTRL_SYNC_DETECT_SHIFT 14 +#define ISPCTRL_SYNC_DETECT_HSFALL (0x0 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_HSRISE (0x1 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_VSFALL (0x2 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_VSRISE (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) +#define ISPCTRL_SYNC_DETECT_MASK (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) + +#define ISPCTRL_CCDC_RAM_EN (1 << 16) +#define ISPCTRL_PREV_RAM_EN (1 << 17) +#define ISPCTRL_SBL_RD_RAM_EN (1 << 18) +#define ISPCTRL_SBL_WR1_RAM_EN (1 << 19) +#define ISPCTRL_SBL_WR0_RAM_EN (1 << 20) +#define ISPCTRL_SBL_AUTOIDLE (1 << 21) +#define ISPCTRL_SBL_SHARED_RPORTB (1 << 28) +#define ISPCTRL_JPEG_FLUSH (1 << 30) +#define ISPCTRL_CCDC_FLUSH (1 << 31) + +#define ISPSECURE_SECUREMODE 0 + +#define ISPTCTRL_CTRL_DIV_LOW 0x0 +#define ISPTCTRL_CTRL_DIV_HIGH 0x1 +#define ISPTCTRL_CTRL_DIV_BYPASS 0x1F + +#define ISPTCTRL_CTRL_DIVA_SHIFT 0 +#define ISPTCTRL_CTRL_DIVA_MASK (0x1F << ISPTCTRL_CTRL_DIVA_SHIFT) + +#define ISPTCTRL_CTRL_DIVB_SHIFT 5 +#define ISPTCTRL_CTRL_DIVB_MASK (0x1F << ISPTCTRL_CTRL_DIVB_SHIFT) + +#define ISPTCTRL_CTRL_DIVC_SHIFT 10 +#define ISPTCTRL_CTRL_DIVC_NOCLOCK (0x0 << 10) + +#define ISPTCTRL_CTRL_SHUTEN (1 << 21) +#define ISPTCTRL_CTRL_PSTRBEN (1 << 22) +#define ISPTCTRL_CTRL_STRBEN (1 << 23) +#define ISPTCTRL_CTRL_SHUTPOL (1 << 24) +#define ISPTCTRL_CTRL_STRBPSTRBPOL (1 << 26) + +#define ISPTCTRL_CTRL_INSEL_SHIFT 27 +#define ISPTCTRL_CTRL_INSEL_PARALLEL (0x0 << 27) +#define ISPTCTRL_CTRL_INSEL_CSIA (0x1 << 27) +#define ISPTCTRL_CTRL_INSEL_CSIB (0x2 << 27) + +#define ISPTCTRL_CTRL_GRESETEn (1 << 29) +#define ISPTCTRL_CTRL_GRESETPOL (1 << 30) +#define ISPTCTRL_CTRL_GRESETDIR (1 << 31) + +#define ISPTCTRL_FRAME_SHUT_SHIFT 0 +#define ISPTCTRL_FRAME_PSTRB_SHIFT 6 +#define ISPTCTRL_FRAME_STRB_SHIFT 12 + +#define ISPCCDC_PID_PREV_SHIFT 0 +#define ISPCCDC_PID_CID_SHIFT 8 +#define ISPCCDC_PID_TID_SHIFT 16 + +#define ISPCCDC_PCR_EN 1 +#define ISPCCDC_PCR_BUSY (1 << 1) + +#define ISPCCDC_SYN_MODE_VDHDOUT 0x1 +#define ISPCCDC_SYN_MODE_FLDOUT (1 << 1) +#define ISPCCDC_SYN_MODE_VDPOL (1 << 2) +#define ISPCCDC_SYN_MODE_HDPOL (1 << 3) +#define ISPCCDC_SYN_MODE_FLDPOL (1 << 4) +#define ISPCCDC_SYN_MODE_EXWEN (1 << 5) +#define ISPCCDC_SYN_MODE_DATAPOL (1 << 6) +#define ISPCCDC_SYN_MODE_FLDMODE (1 << 7) +#define ISPCCDC_SYN_MODE_DATSIZ_MASK 0xFFFFF8FF +#define ISPCCDC_SYN_MODE_DATSIZ_8_16 (0x0 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_12 (0x4 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_11 (0x5 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_10 (0x6 << 8) +#define ISPCCDC_SYN_MODE_DATSIZ_8 (0x7 << 8) +#define ISPCCDC_SYN_MODE_PACK8 (1 << 11) +#define ISPCCDC_SYN_MODE_INPMOD_MASK 0xFFFFCFFF +#define ISPCCDC_SYN_MODE_INPMOD_RAW (0 << 12) +#define ISPCCDC_SYN_MODE_INPMOD_YCBCR16 (1 << 12) +#define ISPCCDC_SYN_MODE_INPMOD_YCBCR8 (2 << 12) +#define ISPCCDC_SYN_MODE_LPF (1 << 14) +#define ISPCCDC_SYN_MODE_FLDSTAT (1 << 15) +#define ISPCCDC_SYN_MODE_VDHDEN (1 << 16) +#define ISPCCDC_SYN_MODE_WEN (1 << 17) +#define ISPCCDC_SYN_MODE_VP2SDR (1 << 18) +#define ISPCCDC_SYN_MODE_SDR2RSZ (1 << 19) + +#define ISPCCDC_HD_VD_WID_VDW_SHIFT 0 +#define ISPCCDC_HD_VD_WID_HDW_SHIFT 16 + +#define ISPCCDC_PIX_LINES_HLPRF_SHIFT 0 +#define ISPCCDC_PIX_LINES_PPLN_SHIFT 16 + +#define ISPCCDC_HORZ_INFO_NPH_SHIFT 0 +#define ISPCCDC_HORZ_INFO_NPH_MASK 0xFFFF8000 +#define ISPCCDC_HORZ_INFO_SPH_MASK 0x1000FFFF +#define ISPCCDC_HORZ_INFO_SPH_SHIFT 16 + +#define ISPCCDC_VERT_START_SLV0_SHIFT 16 +#define ISPCCDC_VERT_START_SLV0_MASK 0x1000FFFF +#define ISPCCDC_VERT_START_SLV1_SHIFT 0 + +#define ISPCCDC_VERT_LINES_NLV_MASK 0xFFFF8000 +#define ISPCCDC_VERT_LINES_NLV_SHIFT 0 + +#define ISPCCDC_CULLING_CULV_SHIFT 0 +#define ISPCCDC_CULLING_CULHODD_SHIFT 16 +#define ISPCCDC_CULLING_CULHEVN_SHIFT 24 + +#define ISPCCDC_HSIZE_OFF_SHIFT 0 + +#define ISPCCDC_SDOFST_FINV (1 << 14) +#define ISPCCDC_SDOFST_FOFST_1L 0 +#define ISPCCDC_SDOFST_FOFST_4L (3 << 12) +#define ISPCCDC_SDOFST_LOFST3_SHIFT 0 +#define ISPCCDC_SDOFST_LOFST2_SHIFT 3 +#define ISPCCDC_SDOFST_LOFST1_SHIFT 6 +#define ISPCCDC_SDOFST_LOFST0_SHIFT 9 +#define EVENEVEN 1 +#define ODDEVEN 2 +#define EVENODD 3 +#define ODDODD 4 + +#define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 +#define ISPCCDC_CLAMP_OBST_SHIFT 10 +#define ISPCCDC_CLAMP_OBSLN_SHIFT 25 +#define ISPCCDC_CLAMP_OBSLEN_SHIFT 28 +#define ISPCCDC_CLAMP_CLAMPEN (1 << 31) + +#define ISPCCDC_COLPTN_R_Ye 0x0 +#define ISPCCDC_COLPTN_Gr_Cy 0x1 +#define ISPCCDC_COLPTN_Gb_G 0x2 +#define ISPCCDC_COLPTN_B_Mg 0x3 +#define ISPCCDC_COLPTN_CP0PLC0_SHIFT 0 +#define ISPCCDC_COLPTN_CP0PLC1_SHIFT 2 +#define ISPCCDC_COLPTN_CP0PLC2_SHIFT 4 +#define ISPCCDC_COLPTN_CP0PLC3_SHIFT 6 +#define ISPCCDC_COLPTN_CP1PLC0_SHIFT 8 +#define ISPCCDC_COLPTN_CP1PLC1_SHIFT 10 +#define ISPCCDC_COLPTN_CP1PLC2_SHIFT 12 +#define ISPCCDC_COLPTN_CP1PLC3_SHIFT 14 +#define ISPCCDC_COLPTN_CP2PLC0_SHIFT 16 +#define ISPCCDC_COLPTN_CP2PLC1_SHIFT 18 +#define ISPCCDC_COLPTN_CP2PLC2_SHIFT 20 +#define ISPCCDC_COLPTN_CP2PLC3_SHIFT 22 +#define ISPCCDC_COLPTN_CP3PLC0_SHIFT 24 +#define ISPCCDC_COLPTN_CP3PLC1_SHIFT 26 +#define ISPCCDC_COLPTN_CP3PLC2_SHIFT 28 +#define ISPCCDC_COLPTN_CP3PLC3_SHIFT 30 + +#define ISPCCDC_BLKCMP_B_MG_SHIFT 0 +#define ISPCCDC_BLKCMP_GB_G_SHIFT 8 +#define ISPCCDC_BLKCMP_GR_CY_SHIFT 16 +#define ISPCCDC_BLKCMP_R_YE_SHIFT 24 + +#define ISPCCDC_FPC_FPNUM_SHIFT 0 +#define ISPCCDC_FPC_FPCEN (1 << 15) +#define ISPCCDC_FPC_FPERR (1 << 16) + +#define ISPCCDC_VDINT_1_SHIFT 0 +#define ISPCCDC_VDINT_0_SHIFT 16 +#define ISPCCDC_VDINT_0_MASK 0x7FFF +#define ISPCCDC_VDINT_1_MASK 0x7FFF + +#define ISPCCDC_ALAW_GWDI_SHIFT 0 +#define ISPCCDC_ALAW_CCDTBL (1 << 3) + +#define ISPCCDC_REC656IF_R656ON 1 +#define ISPCCDC_REC656IF_ECCFVH (1 << 1) + +#define ISPCCDC_CFG_BW656 (1 << 5) +#define ISPCCDC_CFG_FIDMD_SHIFT 6 +#define ISPCCDC_CFG_WENLOG (1 << 8) +#define ISPCCDC_CFG_WENLOG_AND (0 << 8) +#define ISPCCDC_CFG_WENLOG_OR (1 << 8) +#define ISPCCDC_CFG_Y8POS (1 << 11) +#define ISPCCDC_CFG_BSWD (1 << 12) +#define ISPCCDC_CFG_MSBINVI (1 << 13) +#define ISPCCDC_CFG_VDLC (1 << 15) + +#define ISPCCDC_FMTCFG_FMTEN 0x1 +#define ISPCCDC_FMTCFG_LNALT (1 << 1) +#define ISPCCDC_FMTCFG_LNUM_SHIFT 2 +#define ISPCCDC_FMTCFG_PLEN_ODD_SHIFT 4 +#define ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT 8 +#define ISPCCDC_FMTCFG_VPIN_MASK 0xFFFF8000 +#define ISPCCDC_FMTCFG_VPIN_12_3 (0x3 << 12) +#define ISPCCDC_FMTCFG_VPIN_11_2 (0x4 << 12) +#define ISPCCDC_FMTCFG_VPIN_10_1 (0x5 << 12) +#define ISPCCDC_FMTCFG_VPIN_9_0 (0x6 << 12) +#define ISPCCDC_FMTCFG_VPEN (1 << 15) + +#define ISPCCDC_FMTCFG_VPIF_FRQ_MASK 0xFFF8FFFF +#define ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT 16 +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY2 (0x0 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY3 (0x1 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY4 (0x2 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY5 (0x3 << 16) +#define ISPCCDC_FMTCFG_VPIF_FRQ_BY6 (0x4 << 16) + +#define ISPCCDC_FMT_HORZ_FMTLNH_SHIFT 0 +#define ISPCCDC_FMT_HORZ_FMTSPH_SHIFT 16 + +#define ISPCCDC_FMT_VERT_FMTLNV_SHIFT 0 +#define ISPCCDC_FMT_VERT_FMTSLV_SHIFT 16 + +#define ISPCCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF0000 +#define ISPCCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF + +#define ISPCCDC_FMT_VERT_FMTSLV_MASK 0x1FFF0000 +#define ISPCCDC_FMT_VERT_FMTLNV_MASK 0x1FFF + +#define ISPCCDC_VP_OUT_HORZ_ST_SHIFT 0 +#define ISPCCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define ISPCCDC_VP_OUT_VERT_NUM_SHIFT 17 + +#define ISPRSZ_PID_PREV_SHIFT 0 +#define ISPRSZ_PID_CID_SHIFT 8 +#define ISPRSZ_PID_TID_SHIFT 16 + +#define ISPRSZ_PCR_ENABLE 0x5 +#define ISPRSZ_PCR_BUSY (1 << 1) + +#define ISPRSZ_CNT_HRSZ_SHIFT 0 +#define ISPRSZ_CNT_HRSZ_MASK 0x3FF +#define ISPRSZ_CNT_VRSZ_SHIFT 10 +#define ISPRSZ_CNT_VRSZ_MASK 0xFFC00 +#define ISPRSZ_CNT_HSTPH_SHIFT 20 +#define ISPRSZ_CNT_HSTPH_MASK 0x700000 +#define ISPRSZ_CNT_VSTPH_SHIFT 23 +#define ISPRSZ_CNT_VSTPH_MASK 0x3800000 +#define ISPRSZ_CNT_CBILIN_MASK 0x20000000 +#define ISPRSZ_CNT_INPTYP_MASK 0x08000000 +#define ISPRSZ_CNT_PIXFMT_MASK 0x04000000 +#define ISPRSZ_CNT_YCPOS (1 << 26) +#define ISPRSZ_CNT_INPTYP (1 << 27) +#define ISPRSZ_CNT_INPSRC (1 << 28) +#define ISPRSZ_CNT_CBILIN (1 << 29) + +#define ISPRSZ_OUT_SIZE_HORZ_SHIFT 0 +#define ISPRSZ_OUT_SIZE_HORZ_MASK 0x7FF +#define ISPRSZ_OUT_SIZE_VERT_SHIFT 16 +#define ISPRSZ_OUT_SIZE_VERT_MASK 0x7FF0000 + + +#define ISPRSZ_IN_START_HORZ_ST_SHIFT 0 +#define ISPRSZ_IN_START_HORZ_ST_MASK 0x1FFF +#define ISPRSZ_IN_START_VERT_ST_SHIFT 16 +#define ISPRSZ_IN_START_VERT_ST_MASK 0x1FFF0000 + + +#define ISPRSZ_IN_SIZE_HORZ_SHIFT 0 +#define ISPRSZ_IN_SIZE_HORZ_MASK 0x1FFF +#define ISPRSZ_IN_SIZE_VERT_SHIFT 16 +#define ISPRSZ_IN_SIZE_VERT_MASK 0x1FFF0000 + +#define ISPRSZ_SDR_INADD_ADDR_SHIFT 0 +#define ISPRSZ_SDR_INADD_ADDR_MASK 0xFFFFFFFF + +#define ISPRSZ_SDR_INOFF_OFFSET_SHIFT 0 +#define ISPRSZ_SDR_INOFF_OFFSET_MASK 0xFFFF + +#define ISPRSZ_SDR_OUTADD_ADDR_SHIFT 0 +#define ISPRSZ_SDR_OUTADD_ADDR_MASK 0xFFFFFFFF + + +#define ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT 0 +#define ISPRSZ_SDR_OUTOFF_OFFSET_MASK 0xFFFF + +#define ISPRSZ_HFILT10_COEF0_SHIFT 0 +#define ISPRSZ_HFILT10_COEF0_MASK 0x3FF +#define ISPRSZ_HFILT10_COEF1_SHIFT 16 +#define ISPRSZ_HFILT10_COEF1_MASK 0x3FF0000 + +#define ISPRSZ_HFILT32_COEF2_SHIFT 0 +#define ISPRSZ_HFILT32_COEF2_MASK 0x3FF +#define ISPRSZ_HFILT32_COEF3_SHIFT 16 +#define ISPRSZ_HFILT32_COEF3_MASK 0x3FF0000 + +#define ISPRSZ_HFILT54_COEF4_SHIFT 0 +#define ISPRSZ_HFILT54_COEF4_MASK 0x3FF +#define ISPRSZ_HFILT54_COEF5_SHIFT 16 +#define ISPRSZ_HFILT54_COEF5_MASK 0x3FF0000 + +#define ISPRSZ_HFILT76_COEFF6_SHIFT 0 +#define ISPRSZ_HFILT76_COEFF6_MASK 0x3FF +#define ISPRSZ_HFILT76_COEFF7_SHIFT 16 +#define ISPRSZ_HFILT76_COEFF7_MASK 0x3FF0000 + +#define ISPRSZ_HFILT98_COEFF8_SHIFT 0 +#define ISPRSZ_HFILT98_COEFF8_MASK 0x3FF +#define ISPRSZ_HFILT98_COEFF9_SHIFT 16 +#define ISPRSZ_HFILT98_COEFF9_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1110_COEF10_SHIFT 0 +#define ISPRSZ_HFILT1110_COEF10_MASK 0x3FF +#define ISPRSZ_HFILT1110_COEF11_SHIFT 16 +#define ISPRSZ_HFILT1110_COEF11_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1312_COEFF12_SHIFT 0 +#define ISPRSZ_HFILT1312_COEFF12_MASK 0x3FF +#define ISPRSZ_HFILT1312_COEFF13_SHIFT 16 +#define ISPRSZ_HFILT1312_COEFF13_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1514_COEFF14_SHIFT 0 +#define ISPRSZ_HFILT1514_COEFF14_MASK 0x3FF +#define ISPRSZ_HFILT1514_COEFF15_SHIFT 16 +#define ISPRSZ_HFILT1514_COEFF15_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1716_COEF16_SHIFT 0 +#define ISPRSZ_HFILT1716_COEF16_MASK 0x3FF +#define ISPRSZ_HFILT1716_COEF17_SHIFT 16 +#define ISPRSZ_HFILT1716_COEF17_MASK 0x3FF0000 + +#define ISPRSZ_HFILT1918_COEF18_SHIFT 0 +#define ISPRSZ_HFILT1918_COEF18_MASK 0x3FF +#define ISPRSZ_HFILT1918_COEF19_SHIFT 16 +#define ISPRSZ_HFILT1918_COEF19_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2120_COEF20_SHIFT 0 +#define ISPRSZ_HFILT2120_COEF20_MASK 0x3FF +#define ISPRSZ_HFILT2120_COEF21_SHIFT 16 +#define ISPRSZ_HFILT2120_COEF21_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2322_COEF22_SHIFT 0 +#define ISPRSZ_HFILT2322_COEF22_MASK 0x3FF +#define ISPRSZ_HFILT2322_COEF23_SHIFT 16 +#define ISPRSZ_HFILT2322_COEF23_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2524_COEF24_SHIFT 0 +#define ISPRSZ_HFILT2524_COEF24_MASK 0x3FF +#define ISPRSZ_HFILT2524_COEF25_SHIFT 16 +#define ISPRSZ_HFILT2524_COEF25_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2726_COEF26_SHIFT 0 +#define ISPRSZ_HFILT2726_COEF26_MASK 0x3FF +#define ISPRSZ_HFILT2726_COEF27_SHIFT 16 +#define ISPRSZ_HFILT2726_COEF27_MASK 0x3FF0000 + +#define ISPRSZ_HFILT2928_COEF28_SHIFT 0 +#define ISPRSZ_HFILT2928_COEF28_MASK 0x3FF +#define ISPRSZ_HFILT2928_COEF29_SHIFT 16 +#define ISPRSZ_HFILT2928_COEF29_MASK 0x3FF0000 + +#define ISPRSZ_HFILT3130_COEF30_SHIFT 0 +#define ISPRSZ_HFILT3130_COEF30_MASK 0x3FF +#define ISPRSZ_HFILT3130_COEF31_SHIFT 16 +#define ISPRSZ_HFILT3130_COEF31_MASK 0x3FF0000 + +#define ISPRSZ_VFILT10_COEF0_SHIFT 0 +#define ISPRSZ_VFILT10_COEF0_MASK 0x3FF +#define ISPRSZ_VFILT10_COEF1_SHIFT 16 +#define ISPRSZ_VFILT10_COEF1_MASK 0x3FF0000 + +#define ISPRSZ_VFILT32_COEF2_SHIFT 0 +#define ISPRSZ_VFILT32_COEF2_MASK 0x3FF +#define ISPRSZ_VFILT32_COEF3_SHIFT 16 +#define ISPRSZ_VFILT32_COEF3_MASK 0x3FF0000 + +#define ISPRSZ_VFILT54_COEF4_SHIFT 0 +#define ISPRSZ_VFILT54_COEF4_MASK 0x3FF +#define ISPRSZ_VFILT54_COEF5_SHIFT 16 +#define ISPRSZ_VFILT54_COEF5_MASK 0x3FF0000 + +#define ISPRSZ_VFILT76_COEFF6_SHIFT 0 +#define ISPRSZ_VFILT76_COEFF6_MASK 0x3FF +#define ISPRSZ_VFILT76_COEFF7_SHIFT 16 +#define ISPRSZ_VFILT76_COEFF7_MASK 0x3FF0000 + +#define ISPRSZ_VFILT98_COEFF8_SHIFT 0 +#define ISPRSZ_VFILT98_COEFF8_MASK 0x3FF +#define ISPRSZ_VFILT98_COEFF9_SHIFT 16 +#define ISPRSZ_VFILT98_COEFF9_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1110_COEF10_SHIFT 0 +#define ISPRSZ_VFILT1110_COEF10_MASK 0x3FF +#define ISPRSZ_VFILT1110_COEF11_SHIFT 16 +#define ISPRSZ_VFILT1110_COEF11_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1312_COEFF12_SHIFT 0 +#define ISPRSZ_VFILT1312_COEFF12_MASK 0x3FF +#define ISPRSZ_VFILT1312_COEFF13_SHIFT 16 +#define ISPRSZ_VFILT1312_COEFF13_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1514_COEFF14_SHIFT 0 +#define ISPRSZ_VFILT1514_COEFF14_MASK 0x3FF +#define ISPRSZ_VFILT1514_COEFF15_SHIFT 16 +#define ISPRSZ_VFILT1514_COEFF15_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1716_COEF16_SHIFT 0 +#define ISPRSZ_VFILT1716_COEF16_MASK 0x3FF +#define ISPRSZ_VFILT1716_COEF17_SHIFT 16 +#define ISPRSZ_VFILT1716_COEF17_MASK 0x3FF0000 + +#define ISPRSZ_VFILT1918_COEF18_SHIFT 0 +#define ISPRSZ_VFILT1918_COEF18_MASK 0x3FF +#define ISPRSZ_VFILT1918_COEF19_SHIFT 16 +#define ISPRSZ_VFILT1918_COEF19_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2120_COEF20_SHIFT 0 +#define ISPRSZ_VFILT2120_COEF20_MASK 0x3FF +#define ISPRSZ_VFILT2120_COEF21_SHIFT 16 +#define ISPRSZ_VFILT2120_COEF21_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2322_COEF22_SHIFT 0 +#define ISPRSZ_VFILT2322_COEF22_MASK 0x3FF +#define ISPRSZ_VFILT2322_COEF23_SHIFT 16 +#define ISPRSZ_VFILT2322_COEF23_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2524_COEF24_SHIFT 0 +#define ISPRSZ_VFILT2524_COEF24_MASK 0x3FF +#define ISPRSZ_VFILT2524_COEF25_SHIFT 16 +#define ISPRSZ_VFILT2524_COEF25_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2726_COEF26_SHIFT 0 +#define ISPRSZ_VFILT2726_COEF26_MASK 0x3FF +#define ISPRSZ_VFILT2726_COEF27_SHIFT 16 +#define ISPRSZ_VFILT2726_COEF27_MASK 0x3FF0000 + +#define ISPRSZ_VFILT2928_COEF28_SHIFT 0 +#define ISPRSZ_VFILT2928_COEF28_MASK 0x3FF +#define ISPRSZ_VFILT2928_COEF29_SHIFT 16 +#define ISPRSZ_VFILT2928_COEF29_MASK 0x3FF0000 + +#define ISPRSZ_VFILT3130_COEF30_SHIFT 0 +#define ISPRSZ_VFILT3130_COEF30_MASK 0x3FF +#define ISPRSZ_VFILT3130_COEF31_SHIFT 16 +#define ISPRSZ_VFILT3130_COEF31_MASK 0x3FF0000 + +#define ISPRSZ_YENH_CORE_SHIFT 0 +#define ISPRSZ_YENH_CORE_MASK 0xFF +#define ISPRSZ_YENH_SLOP_SHIFT 8 +#define ISPRSZ_YENH_SLOP_MASK 0xF00 +#define ISPRSZ_YENH_GAIN_SHIFT 12 +#define ISPRSZ_YENH_GAIN_MASK 0xF000 +#define ISPRSZ_YENH_ALGO_SHIFT 16 +#define ISPRSZ_YENH_ALGO_MASK 0x30000 + +#define ISPH3A_PCR_AEW_ALAW_EN_SHIFT 1 +#define ISPH3A_PCR_AF_MED_TH_SHIFT 3 +#define ISPH3A_PCR_AF_RGBPOS_SHIFT 11 +#define ISPH3A_PCR_AEW_AVE2LMT_SHIFT 22 +#define ISPH3A_PCR_AEW_AVE2LMT_MASK 0xFFC00000 +#define ISPH3A_PCR_BUSYAF (1 << 15) +#define ISPH3A_PCR_BUSYAEAWB (1 << 18) + +#define ISPH3A_AEWWIN1_WINHC_SHIFT 0 +#define ISPH3A_AEWWIN1_WINHC_MASK 0x3F +#define ISPH3A_AEWWIN1_WINVC_SHIFT 6 +#define ISPH3A_AEWWIN1_WINVC_MASK 0x1FC0 +#define ISPH3A_AEWWIN1_WINW_SHIFT 13 +#define ISPH3A_AEWWIN1_WINW_MASK 0xFE000 +#define ISPH3A_AEWWIN1_WINH_SHIFT 24 +#define ISPH3A_AEWWIN1_WINH_MASK 0x7F000000 + +#define ISPH3A_AEWINSTART_WINSH_SHIFT 0 +#define ISPH3A_AEWINSTART_WINSH_MASK 0x0FFF +#define ISPH3A_AEWINSTART_WINSV_SHIFT 16 +#define ISPH3A_AEWINSTART_WINSV_MASK 0x0FFF0000 + +#define ISPH3A_AEWINBLK_WINH_SHIFT 0 +#define ISPH3A_AEWINBLK_WINH_MASK 0x7F +#define ISPH3A_AEWINBLK_WINSV_SHIFT 16 +#define ISPH3A_AEWINBLK_WINSV_MASK 0x0FFF0000 + +#define ISPH3A_AEWSUBWIN_AEWINCH_SHIFT 0 +#define ISPH3A_AEWSUBWIN_AEWINCH_MASK 0x0F +#define ISPH3A_AEWSUBWIN_AEWINCV_SHIFT 8 +#define ISPH3A_AEWSUBWIN_AEWINCV_MASK 0x0F00 + +#define ISPHIST_PCR_ENABLE_SHIFT 0 +#define ISPHIST_PCR_ENABLE_MASK 0x01 +#define ISPHIST_PCR_BUSY 0x02 + +#define ISPHIST_CNT_DATASIZE_SHIFT 8 +#define ISPHIST_CNT_DATASIZE_MASK 0x0100 +#define ISPHIST_CNT_CLEAR_SHIFT 7 +#define ISPHIST_CNT_CLEAR_MASK 0x080 +#define ISPHIST_CNT_CFA_SHIFT 6 +#define ISPHIST_CNT_CFA_MASK 0x040 +#define ISPHIST_CNT_BINS_SHIFT 4 +#define ISPHIST_CNT_BINS_MASK 0x030 +#define ISPHIST_CNT_SOURCE_SHIFT 3 +#define ISPHIST_CNT_SOURCE_MASK 0x08 +#define ISPHIST_CNT_SHIFT_SHIFT 0 +#define ISPHIST_CNT_SHIFT_MASK 0x07 + +#define ISPHIST_WB_GAIN_WG00_SHIFT 24 +#define ISPHIST_WB_GAIN_WG00_MASK 0xFF000000 +#define ISPHIST_WB_GAIN_WG01_SHIFT 16 +#define ISPHIST_WB_GAIN_WG01_MASK 0xFF0000 +#define ISPHIST_WB_GAIN_WG02_SHIFT 8 +#define ISPHIST_WB_GAIN_WG02_MASK 0xFF00 +#define ISPHIST_WB_GAIN_WG03_SHIFT 0 +#define ISPHIST_WB_GAIN_WG03_MASK 0xFF + +#define ISPHIST_REGHORIZ_HSTART_SHIFT 16 /* + * REGION 0 to 3 HORZ + * and VERT + */ +#define ISPHIST_REGHORIZ_HSTART_MASK 0x3FFF0000 +#define ISPHIST_REGHORIZ_HEND_SHIFT 0 +#define ISPHIST_REGHORIZ_HEND_MASK 0x3FFF +#define ISPHIST_REGVERT_VSTART_SHIFT 16 +#define ISPHIST_REGVERT_VSTART_MASK 0x3FFF0000 +#define ISPHIST_REGVERT_VEND_SHIFT 0 +#define ISPHIST_REGVERT_VEND_MASK 0x3FFF + +#define ISPHIST_REGHORIZ_MASK 0x3FFF3FFF +#define ISPHIST_REGVERT_MASK 0x3FFF3FFF + +#define ISPHIST_ADDR_SHIFT 0 +#define ISPHIST_ADDR_MASK 0x3FF + +#define ISPHIST_DATA_SHIFT 0 +#define ISPHIST_DATA_MASK 0xFFFFF + +#define ISPHIST_RADD_SHIFT 0 +#define ISPHIST_RADD_MASK 0xFFFFFFFF + +#define ISPHIST_RADD_OFF_SHIFT 0 +#define ISPHIST_RADD_OFF_MASK 0xFFFF + +#define ISPHIST_HV_INFO_HSIZE_SHIFT 16 +#define ISPHIST_HV_INFO_HSIZE_MASK 0x3FFF0000 +#define ISPHIST_HV_INFO_VSIZE_SHIFT 0 +#define ISPHIST_HV_INFO_VSIZE_MASK 0x3FFF + +#define ISPHIST_HV_INFO_MASK 0x3FFF3FFF + +#define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700 +#define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8 +#define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800 +#define ISPCCDC_LSC_GAIN_MODE_M_SHIFT 12 +#define ISPCCDC_LSC_GAIN_FORMAT_MASK 0xE +#define ISPCCDC_LSC_GAIN_FORMAT_SHIFT 1 +#define ISPCCDC_LSC_AFTER_REFORMATTER_MASK (1<<6) + +#define ISPCCDC_LSC_INITIAL_X_MASK 0x3F +#define ISPCCDC_LSC_INITIAL_X_SHIFT 0 +#define ISPCCDC_LSC_INITIAL_Y_MASK 0x3F0000 +#define ISPCCDC_LSC_INITIAL_Y_SHIFT 16 + +#define ISPMMU_REVISION_REV_MINOR_MASK 0xF +#define ISPMMU_REVISION_REV_MAJOR_SHIFT 0x4 + +#define IRQENABLE_MULTIHITFAULT (1<<4) +#define IRQENABLE_TWFAULT (1<<3) +#define IRQENABLE_EMUMISS (1<<2) +#define IRQENABLE_TRANSLNFAULT (1<<1) +#define IRQENABLE_TLBMISS (1) + +#define ISPMMU_MMUCNTL_MMU_EN (1<<1) +#define ISPMMU_MMUCNTL_TWL_EN (1<<2) +#define ISPMMU_MMUCNTL_EMUTLBUPDATE (1<<3) +#define ISPMMU_AUTOIDLE 0x1 +#define ISPMMU_SIDLEMODE_FORCEIDLE 0 +#define ISPMMU_SIDLEMODE_NOIDLE 1 +#define ISPMMU_SIDLEMODE_SMARTIDLE 2 +#define ISPMMU_SIDLEMODE_SHIFT 3 + +#define ISPCSI1_AUTOIDLE 0x1 +#define ISPCSI1_MIDLEMODE_SHIFT 12 +#define ISPCSI1_MIDLEMODE_FORCESTANDBY 0x0 +#define ISPCSI1_MIDLEMODE_NOSTANDBY 0x1 +#define ISPCSI1_MIDLEMODE_SMARTSTANDBY 0x2 + +/* CSI2 receiver registers (ES2.0) */ +#define ISPCSI2_REVISION (0x000) +#define ISPCSI2_SYSCONFIG (0x010) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK \ + (0x3 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_FORCE \ + (0x0 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO \ + (0x1 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART \ + (0x2 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) +#define ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT 1 +#define ISPCSI2_SYSCONFIG_SOFT_RESET_MASK \ + (0x1 << ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT) +#define ISPCSI2_SYSCONFIG_SOFT_RESET_NORMAL \ + (0x0 << ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT) +#define ISPCSI2_SYSCONFIG_SOFT_RESET_RESET \ + (0x1 << ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT) +#define ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT 0 +#define ISPCSI2_SYSCONFIG_AUTO_IDLE_MASK \ + (0x1 << ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT) +#define ISPCSI2_SYSCONFIG_AUTO_IDLE_FREE \ + (0x0 << ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT) +#define ISPCSI2_SYSCONFIG_AUTO_IDLE_AUTO \ + (0x1 << ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT) +#define ISPCSI2_SYSSTATUS (0x014) +#define ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT 0 +#define ISPCSI2_SYSSTATUS_RESET_DONE_MASK \ + (0x1 << ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT) +#define ISPCSI2_SYSSTATUS_RESET_DONE_ONGOING \ + (0x0 << ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT) +#define ISPCSI2_SYSSTATUS_RESET_DONE_DONE \ + (0x1 << ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT) +#define ISPCSI2_IRQSTATUS (0x018) +#define ISPCSI2_IRQSTATUS_OCP_ERR_IRQ (1 << 14) +#define ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ (1 << 13) +#define ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 12) +#define ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ (1 << 11) +#define ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ (1 << 10) +#define ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ (1 << 9) +#define ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ (1 << 8) +#define ISPCSI2_IRQSTATUS_CONTEXT(n) (1 << (n)) + +#define ISPCSI2_IRQENABLE (0x01C) +#define ISPCSI2_CTRL (0x040) +#define ISPCSI2_CTRL_VP_CLK_EN_SHIFT 15 +#define ISPCSI2_CTRL_VP_CLK_EN_MASK (0x1 << ISPCSI2_CTRL_VP_CLK_EN_SHIFT) +#define ISPCSI2_CTRL_VP_CLK_EN_DISABLE (0x0 << ISPCSI2_CTRL_VP_CLK_EN_SHIFT) +#define ISPCSI2_CTRL_VP_CLK_EN_ENABLE (0x1 << ISPCSI2_CTRL_VP_CLK_EN_SHIFT) + +#define ISPCSI2_CTRL_VP_ONLY_EN_SHIFT 11 +#define ISPCSI2_CTRL_VP_ONLY_EN_MASK (0x1 << ISPCSI2_CTRL_VP_ONLY_EN_SHIFT) +#define ISPCSI2_CTRL_VP_ONLY_EN_DISABLE (0x0 << ISPCSI2_CTRL_VP_ONLY_EN_SHIFT) +#define ISPCSI2_CTRL_VP_ONLY_EN_ENABLE (0x1 << ISPCSI2_CTRL_VP_ONLY_EN_SHIFT) + +#define ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT 8 +#define ISPCSI2_CTRL_VP_OUT_CTRL_MASK (0x3 << \ + ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) +#define ISPCSI2_CTRL_VP_OUT_CTRL_DISABLE (0x0 << \ + ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) +#define ISPCSI2_CTRL_VP_OUT_CTRL_DIV2 (0x1 << \ + ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) +#define ISPCSI2_CTRL_VP_OUT_CTRL_DIV3 (0x2 << \ + ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) +#define ISPCSI2_CTRL_VP_OUT_CTRL_DIV4 (0x3 << \ + ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) + +#define ISPCSI2_CTRL_DBG_EN_SHIFT 7 +#define ISPCSI2_CTRL_DBG_EN_MASK (0x1 << ISPCSI2_CTRL_DBG_EN_SHIFT) +#define ISPCSI2_CTRL_DBG_EN_DISABLE (0x0 << ISPCSI2_CTRL_DBG_EN_SHIFT) +#define ISPCSI2_CTRL_DBG_EN_ENABLE (0x1 << ISPCSI2_CTRL_DBG_EN_SHIFT) + +#define ISPCSI2_CTRL_BURST_SIZE_SHIFT 5 +#define ISPCSI2_CTRL_BURST_SIZE_MASK (0x3 << \ + ISPCSI2_CTRL_BURST_SIZE_SHIFT) +#define ISPCSI2_CTRL_BURST_SIZE_MYSTERY_VAL (0x2 << \ + ISPCSI2_CTRL_BURST_SIZE_SHIFT) + +#define ISPCSI2_CTRL_FRAME_SHIFT 3 +#define ISPCSI2_CTRL_FRAME_MASK (0x1 << ISPCSI2_CTRL_FRAME_SHIFT) +#define ISPCSI2_CTRL_FRAME_DISABLE_IMM (0x0 << ISPCSI2_CTRL_FRAME_SHIFT) +#define ISPCSI2_CTRL_FRAME_DISABLE_FEC (0x1 << ISPCSI2_CTRL_FRAME_SHIFT) + +#define ISPCSI2_CTRL_ECC_EN_SHIFT 2 +#define ISPCSI2_CTRL_ECC_EN_MASK (0x1 << ISPCSI2_CTRL_ECC_EN_SHIFT) +#define ISPCSI2_CTRL_ECC_EN_DISABLE (0x0 << ISPCSI2_CTRL_ECC_EN_SHIFT) +#define ISPCSI2_CTRL_ECC_EN_ENABLE (0x1 << ISPCSI2_CTRL_ECC_EN_SHIFT) + +#define ISPCSI2_CTRL_SECURE_SHIFT 1 +#define ISPCSI2_CTRL_SECURE_MASK (0x1 << ISPCSI2_CTRL_SECURE_SHIFT) +#define ISPCSI2_CTRL_SECURE_DISABLE (0x0 << ISPCSI2_CTRL_SECURE_SHIFT) +#define ISPCSI2_CTRL_SECURE_ENABLE (0x1 << ISPCSI2_CTRL_SECURE_SHIFT) + +#define ISPCSI2_CTRL_IF_EN_SHIFT 0 +#define ISPCSI2_CTRL_IF_EN_MASK (0x1 << ISPCSI2_CTRL_IF_EN_SHIFT) +#define ISPCSI2_CTRL_IF_EN_DISABLE (0x0 << ISPCSI2_CTRL_IF_EN_SHIFT) +#define ISPCSI2_CTRL_IF_EN_ENABLE (0x1 << ISPCSI2_CTRL_IF_EN_SHIFT) + +#define ISPCSI2_DBG_H (0x044) +#define ISPCSI2_GNQ (0x048) +#define ISPCSI2_COMPLEXIO_CFG1 (0x050) +#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT 29 +#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_MASK \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_ONGOING \ + (0x0 << ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_DONE \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT 27 +#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_MASK \ + (0x3 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_OFF \ + (0x0 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ON \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ULPW \ + (0x2 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT 25 +#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_MASK \ + (0x3 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_OFF \ + (0x0 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ON \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ULPW \ + (0x2 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT 24 +#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_MASK \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_DISABLE \ + (0x0 << ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_ENABLE \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT) + +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n) (3 + ((n) * 4)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_MASK(n) \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_PN(n) \ + (0x0 << ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_NP(n) \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n)) + +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n) ((n) * 4) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_MASK(n) \ + (0x7 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_NC(n) \ + (0x0 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_1(n) \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_2(n) \ + (0x2 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_3(n) \ + (0x3 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_4(n) \ + (0x4 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) +#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_5(n) \ + (0x5 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) + +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT 3 +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_MASK \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_PN \ + (0x0 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_NP \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT) + +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT 0 +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_MASK \ + (0x7 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_1 \ + (0x1 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_2 \ + (0x2 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_3 \ + (0x3 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_4 \ + (0x4 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) +#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_5 \ + (0x5 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) + +#define ISPCSI2_COMPLEXIO1_IRQSTATUS (0x054) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEALLULPMEXIT (1 << 26) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEALLULPMENTER (1 << 25) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM5 (1 << 24) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM4 (1 << 23) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM3 (1 << 22) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM2 (1 << 21) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM1 (1 << 20) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL5 (1 << 19) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL4 (1 << 18) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL3 (1 << 17) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL2 (1 << 16) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL1 (1 << 15) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC5 (1 << 14) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC4 (1 << 13) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC3 (1 << 12) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC2 (1 << 11) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC1 (1 << 10) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS5 (1 << 9) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS4 (1 << 8) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS3 (1 << 7) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS2 (1 << 6) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS1 (1 << 5) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS5 (1 << 4) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS4 (1 << 3) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS3 (1 << 2) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS2 (1 << 1) +#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS1 1 + +#define ISPCSI2_SHORT_PACKET (0x05C) +#define ISPCSI2_COMPLEXIO1_IRQENABLE (0x060) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMEXIT (1 << 26) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMENTER (1 << 25) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM5 (1 << 24) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM4 (1 << 23) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM3 (1 << 22) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM2 (1 << 21) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM1 (1 << 20) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL5 (1 << 19) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL4 (1 << 18) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL3 (1 << 17) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL2 (1 << 16) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL1 (1 << 15) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC5 (1 << 14) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC4 (1 << 13) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC3 (1 << 12) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC2 (1 << 11) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC1 (1 << 10) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS5 (1 << 9) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS4 (1 << 8) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS3 (1 << 7) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS2 (1 << 6) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS1 (1 << 5) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS5 (1 << 4) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS4 (1 << 3) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS3 (1 << 2) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS2 (1 << 1) +#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS1 1 +#define ISPCSI2_DBG_P (0x068) +#define ISPCSI2_TIMING (0x06C) + + +#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n) \ + ((16 * ((n) - 1)) + 15) +#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_MASK(n) \ + (0x1 << ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n)) +#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_DISABLE(n) \ + (0x0 << ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n)) +#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_ENABLE(n) \ + (0x1 << ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n)) +#define ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n) ((16 * ((n) - 1)) + 14) +#define ISPCSI2_TIMING_STOP_STATE_X16_IO_MASK(n) \ + (0x1 << ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n)) +#define ISPCSI2_TIMING_STOP_STATE_X16_IO_DISABLE(n) \ + (0x0 << ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n)) +#define ISPCSI2_TIMING_STOP_STATE_X16_IO_ENABLE(n) \ + (0x1 << ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n)) +#define ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n) ((16 * ((n) - 1)) + 13) +#define ISPCSI2_TIMING_STOP_STATE_X4_IO_MASK(n) \ + (0x1 << ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n)) +#define ISPCSI2_TIMING_STOP_STATE_X4_IO_DISABLE(n) \ + (0x0 << ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n)) +#define ISPCSI2_TIMING_STOP_STATE_X4_IO_ENABLE(n) \ + (0x1 << ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n)) +#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n) (16 * ((n) - 1)) +#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(n) \ + (0x1fff << ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n)) + +#define ISPCSI2_CTX_CTRL1(n) ((0x070) + 0x20 * (n)) +#define ISPCSI2_CTX_CTRL1_COUNT_SHIFT 8 +#define ISPCSI2_CTX_CTRL1_COUNT_MASK (0xFF << \ + ISPCSI2_CTX_CTRL1_COUNT_SHIFT) +#define ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT 7 +#define ISPCSI2_CTX_CTRL1_EOF_EN_MASK \ + (0x1 << ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_EOF_EN_DISABLE \ + (0x0 << ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_EOF_EN_ENABLE \ + (0x1 << ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT 6 +#define ISPCSI2_CTX_CTRL1_EOL_EN_MASK \ + (0x1 << ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_EOL_EN_DISABLE \ + (0x0 << ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_EOL_EN_ENABLE \ + (0x1 << ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_CS_EN_SHIFT 5 +#define ISPCSI2_CTX_CTRL1_CS_EN_MASK \ + (0x1 << ISPCSI2_CTX_CTRL1_CS_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_CS_EN_DISABLE \ + (0x0 << ISPCSI2_CTX_CTRL1_CS_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_CS_EN_ENABLE \ + (0x1 << ISPCSI2_CTX_CTRL1_CS_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT 4 +#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_MASK \ + (0x1 << ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_DISABLE \ + (0x0 << ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_ENABLE \ + (0x1 << ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_PING_PONG_SHIFT 3 +#define ISPCSI2_CTX_CTRL1_PING_PONG_MASK \ + (0x1 << ISPCSI2_CTX_CTRL1_PING_PONG_SHIFT) +#define ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT 0 +#define ISPCSI2_CTX_CTRL1_CTX_EN_MASK \ + (0x1 << ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_CTX_EN_DISABLE \ + (0x0 << ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT) +#define ISPCSI2_CTX_CTRL1_CTX_EN_ENABLE \ + (0x1 << ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT) + +#define ISPCSI2_CTX_CTRL2(n) ((0x074) + 0x20 * (n)) +#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 +#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK \ + (0x3 << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT) +#define ISPCSI2_CTX_CTRL2_FORMAT_SHIFT 0 +#define ISPCSI2_CTX_CTRL2_FORMAT_MASK (0x3FF << \ + ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) + +#define ISPCSI2_CTX_DAT_OFST(n) ((0x078) + 0x20 * (n)) +#define ISPCSI2_CTX_DAT_OFST_OFST_SHIFT 5 +#define ISPCSI2_CTX_DAT_OFST_OFST_MASK (0x7FF << \ + ISPCSI2_CTX_DAT_OFST_OFST_SHIFT) + +#define ISPCSI2_CTX_DAT_PING_ADDR(n) ((0x07C) + 0x20 * (n)) +#define ISPCSI2_CTX_DAT_PONG_ADDR(n) ((0x080) + 0x20 * (n)) +#define ISPCSI2_CTX_IRQENABLE(n) ((0x084) + 0x20 * (n)) +#define ISPCSI2_CTX_IRQENABLE_ECC_CORRECTION_IRQ (1 << 8) +#define ISPCSI2_CTX_IRQENABLE_LINE_NUMBER_IRQ (1 << 7) +#define ISPCSI2_CTX_IRQENABLE_FRAME_NUMBER_IRQ (1 << 6) +#define ISPCSI2_CTX_IRQENABLE_CS_IRQ (1 << 5) +#define ISPCSI2_CTX_IRQENABLE_LE_IRQ (1 << 3) +#define ISPCSI2_CTX_IRQENABLE_LS_IRQ (1 << 2) +#define ISPCSI2_CTX_IRQENABLE_FE_IRQ (1 << 1) +#define ISPCSI2_CTX_IRQENABLE_FS_IRQ 1 +#define ISPCSI2_CTX_IRQSTATUS(n) ((0x088) + 0x20 * (n)) +#define ISPCSI2_CTX_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 8) +#define ISPCSI2_CTX_IRQSTATUS_LINE_NUMBER_IRQ (1 << 7) +#define ISPCSI2_CTX_IRQSTATUS_FRAME_NUMBER_IRQ (1 << 6) +#define ISPCSI2_CTX_IRQSTATUS_CS_IRQ (1 << 5) +#define ISPCSI2_CTX_IRQSTATUS_LE_IRQ (1 << 3) +#define ISPCSI2_CTX_IRQSTATUS_LS_IRQ (1 << 2) +#define ISPCSI2_CTX_IRQSTATUS_FE_IRQ (1 << 1) +#define ISPCSI2_CTX_IRQSTATUS_FS_IRQ 1 + +#define ISPCSI2_CTX_CTRL3(n) ((0x08C) + 0x20 * (n)) +#define ISPCSI2_CTX_CTRL3_ALPHA_SHIFT 5 +#define ISPCSI2_CTX_CTRL3_ALPHA_MASK (0x3FFF << \ + ISPCSI2_CTX_CTRL3_ALPHA_SHIFT) + +#define ISPCSI2PHY_CFG0 (0x000) +#define ISPCSI2PHY_CFG0_THS_TERM_SHIFT 8 +#define ISPCSI2PHY_CFG0_THS_TERM_MASK \ + (0xFF << ISPCSI2PHY_CFG0_THS_TERM_SHIFT) +#define ISPCSI2PHY_CFG0_THS_TERM_RESETVAL \ + (0x04 << ISPCSI2PHY_CFG0_THS_TERM_SHIFT) +#define ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT 0 +#define ISPCSI2PHY_CFG0_THS_SETTLE_MASK \ + (0xFF << ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT) +#define ISPCSI2PHY_CFG0_THS_SETTLE_RESETVAL \ + (0x27 << ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT) +#define ISPCSI2PHY_CFG1 (0x004) +#define ISPCSI2PHY_CFG1_RESETDONECTRLCLK_SHIFT 29 +#define ISPCSI2PHY_CFG1_RESETDONECTRLCLK_MASK \ + (0x1 << ISPCSI2PHY_CFG1_RESETDONECTRLCLK_SHIFT) +#define ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT 18 +#define ISPCSI2PHY_CFG1_TCLK_TERM_MASK \ + (0x7F << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) +#define ISPCSI2PHY_CFG1_TCLK_TERM__RESETVAL \ + (0x00 << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) +#define ISPCSI2PHY_CFG1_RESERVED1_SHIFT 10 +#define ISPCSI2PHY_CFG1_RESERVED1_MASK \ + (0xFF << ISPCSI2PHY_CFG1_RESERVED1_SHIFT) +#define ISPCSI2PHY_CFG1_RESERVED1__RESETVAL \ + (0xB8 << ISPCSI2PHY_CFG1_RESERVED1_SHIFT) +#define ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT 8 +#define ISPCSI2PHY_CFG1_TCLK_MISS_MASK \ + (0x3 << ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT) +#define ISPCSI2PHY_CFG1_TCLK_MISS__RESETVAL \ + (0x1 << ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT) +#define ISPCSI2PHY_CFG1_TCLK_SETTLE_SHIFT 0 +#define ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK \ + (0xFF << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) +#define ISPCSI2PHY_CFG1_TCLK_SETTLE__RESETVAL \ + (0x0E << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) +#define ISPCSI2PHY_CFG1__RESETVAL (ISPCSI2PHY_CFG1_TCLK_TERM__RESETVAL | \ + ISPCSI2PHY_CFG1_RESERVED1__RESETVAL | \ + ISPCSI2PHY_CFG1_TCLK_MISS__RESETVAL | \ + ISPCSI2PHY_CFG1_TCLK_SETTLE__RESETVAL) +#define ISPCSI2PHY_CFG1__EDITABLE_MASK (ISPCSI2PHY_CFG1_TCLK_TERM_MASK | \ + ISPCSI2PHY_CFG1_RESERVED1_MASK | \ + ISPCSI2PHY_CFG1_TCLK_MISS_MASK | \ + ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK) + +#endif /* __ISPREG_H__ */ diff --git a/drivers/media/video/isp/ispresizer.c b/drivers/media/video/isp/ispresizer.c new file mode 100644 index 00000000000..3542b165683 --- /dev/null +++ b/drivers/media/video/isp/ispresizer.c @@ -0,0 +1,924 @@ +/* + * ispresizer.c + * + * Driver Library for Resizer module in TI's OMAP3 Camera ISP + * + * Copyright (C)2009 Texas Instruments, Inc. + * + * Contributors: + * Sameer Venkatraman + * Mohit Jalori + * Sergio Aguirre + * + * 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 +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispresizer.h" + +/* Default configuration of resizer,filter coefficients,yenh for camera isp */ +static struct isprsz_coef ispreszdefcoef = { + { + 0x0000, 0x0100, 0x0000, 0x0000, + 0x03FA, 0x00F6, 0x0010, 0x0000, + 0x03F9, 0x00DB, 0x002C, 0x0000, + 0x03FB, 0x00B3, 0x0053, 0x03FF, + 0x03FD, 0x0082, 0x0084, 0x03FD, + 0x03FF, 0x0053, 0x00B3, 0x03FB, + 0x0000, 0x002C, 0x00DB, 0x03F9, + 0x0000, 0x0010, 0x00F6, 0x03FA + }, + { + 0x0000, 0x0100, 0x0000, 0x0000, + 0x03FA, 0x00F6, 0x0010, 0x0000, + 0x03F9, 0x00DB, 0x002C, 0x0000, + 0x03FB, 0x00B3, 0x0053, 0x03FF, + 0x03FD, 0x0082, 0x0084, 0x03FD, + 0x03FF, 0x0053, 0x00B3, 0x03FB, + 0x0000, 0x002C, 0x00DB, 0x03F9, + 0x0000, 0x0010, 0x00F6, 0x03FA + }, + { + 0x0004, 0x0023, 0x005A, 0x0058, + 0x0023, 0x0004, 0x0000, 0x0002, + 0x0018, 0x004d, 0x0060, 0x0031, + 0x0008, 0x0000, 0x0001, 0x000f, + 0x003f, 0x0062, 0x003f, 0x000f, + 0x0001, 0x0000, 0x0008, 0x0031, + 0x0060, 0x004d, 0x0018, 0x0002 + }, + { + 0x0004, 0x0023, 0x005A, 0x0058, + 0x0023, 0x0004, 0x0000, 0x0002, + 0x0018, 0x004d, 0x0060, 0x0031, + 0x0008, 0x0000, 0x0001, 0x000f, + 0x003f, 0x0062, 0x003f, 0x000f, + 0x0001, 0x0000, 0x0008, 0x0031, + 0x0060, 0x004d, 0x0018, 0x0002 + } +}; + +/* Structure for saving/restoring resizer module registers */ +static struct isp_reg isprsz_reg_list[] = { + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT10, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT32, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT54, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT76, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT98, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1110, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1312, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1514, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1716, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1918, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2120, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2322, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2524, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2726, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2928, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT3130, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT10, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT32, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT54, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT76, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT98, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1110, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1312, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1514, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1716, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1918, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2120, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2322, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2524, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2726, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2928, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT3130, 0x0000}, + {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH, 0x0000}, + {0, ISP_TOK_TERM, 0x0000} +}; + +/** + * ispresizer_applycrop - Apply crop to input image. + **/ +void ispresizer_applycrop(struct isp_res_device *isp_res) +{ + struct isp_device *isp = + container_of(isp_res, struct isp_device, isp_res); + if (!isp_res->applycrop) + return; + + ispresizer_s_pipeline(isp_res, &isp->pipeline); + + isp_res->applycrop = 0; + + return; +} + +/** + * ispresizer_config_shadow_registers - Configure shadow registers. + **/ +void ispresizer_config_shadow_registers(struct isp_res_device *isp_res) +{ + ispresizer_applycrop(isp_res); + + return; +} +EXPORT_SYMBOL(ispresizer_config_shadow_registers); + +int ispresizer_config_crop(struct isp_res_device *isp_res, + struct v4l2_crop *a) +{ + struct isp_device *isp = + container_of(isp_res, struct isp_device, isp_res); + struct v4l2_crop *crop = a; + int rval; + + if (crop->c.left < 0) + crop->c.left = 0; + if (crop->c.width < 0) + crop->c.width = 0; + if (crop->c.top < 0) + crop->c.top = 0; + if (crop->c.height < 0) + crop->c.height = 0; + + if (crop->c.left >= isp->pipeline.prv_out_w_img) + crop->c.left = isp->pipeline.prv_out_w_img - 1; + if (crop->c.top >= isp->pipeline.rsz_out_h) + crop->c.top = isp->pipeline.rsz_out_h - 1; + + /* Make sure the crop rectangle is never smaller than width + * and height divided by 4, since the resizer cannot upscale it + * by more than 4x. */ + + if (crop->c.width < (isp->pipeline.rsz_out_w + 3) / 4) + crop->c.width = (isp->pipeline.rsz_out_w + 3) / 4; + if (crop->c.height < (isp->pipeline.rsz_out_h + 3) / 4) + crop->c.height = (isp->pipeline.rsz_out_h + 3) / 4; + + if (crop->c.left + crop->c.width > isp->pipeline.prv_out_w_img) + crop->c.width = isp->pipeline.prv_out_w_img - crop->c.left; + if (crop->c.top + crop->c.height > isp->pipeline.rsz_out_h) + crop->c.height = + isp->pipeline.prv_out_h - crop->c.top; + + isp->pipeline.rsz_crop = crop->c; + + rval = ispresizer_try_pipeline(isp_res, &isp->pipeline); + if (rval) + return rval; + + isp_res->applycrop = 1; + + if (isp->running == ISP_STOPPED) + ispresizer_applycrop(isp_res); + + return 0; +} +EXPORT_SYMBOL(ispresizer_config_crop); + +/** + * ispresizer_request - Reserves the Resizer module. + * + * Allows only one user at a time. + * + * Returns 0 if successful, or -EBUSY if resizer module was already requested. + **/ +int ispresizer_request(struct isp_res_device *isp_res) +{ + mutex_lock(&isp_res->ispres_mutex); + if (!isp_res->res_inuse) { + isp_res->res_inuse = 1; + mutex_unlock(&isp_res->ispres_mutex); + isp_reg_writel(isp_res->dev, + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_MAIN, ISP_CTRL) | + ISPCTRL_SBL_WR0_RAM_EN | + ISPCTRL_RSZ_CLK_EN, + OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + return 0; + } else { + mutex_unlock(&isp_res->ispres_mutex); + dev_err(isp_res->dev, "resizer: Module Busy\n"); + return -EBUSY; + } +} +EXPORT_SYMBOL(ispresizer_request); + +/** + * ispresizer_free - Makes Resizer module free. + * + * Returns 0 if successful, or -EINVAL if resizer module was already freed. + **/ +int ispresizer_free(struct isp_res_device *isp_res) +{ + mutex_lock(&isp_res->ispres_mutex); + if (isp_res->res_inuse) { + isp_res->res_inuse = 0; + mutex_unlock(&isp_res->ispres_mutex); + isp_reg_and(isp_res->dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, + ~(ISPCTRL_RSZ_CLK_EN | ISPCTRL_SBL_WR0_RAM_EN)); + return 0; + } else { + mutex_unlock(&isp_res->ispres_mutex); + DPRINTK_ISPRESZ("ISP_ERR : Resizer Module already freed\n"); + return -EINVAL; + } +} +EXPORT_SYMBOL(ispresizer_free); + +/** + * ispresizer_config_datapath - Specifies which input to use in resizer module + * @input: Indicates the module that gives the image to resizer. + * + * Sets up the default resizer configuration according to the arguments. + * + * Returns 0 if successful, or -EINVAL if an unsupported input was requested. + **/ +int ispresizer_config_datapath(struct isp_res_device *isp_res, + struct isp_pipeline *pipe) +{ + u32 cnt = 0; + + DPRINTK_ISPRESZ("ispresizer_config_datapath()+\n"); + + switch (pipe->rsz_in) { + case RSZ_OTFLY_YUV: + cnt &= ~ISPRSZ_CNT_INPTYP; + cnt &= ~ISPRSZ_CNT_INPSRC; + ispresizer_set_inaddr(isp_res, 0); + ispresizer_config_inlineoffset(isp_res, 0); + break; + case RSZ_MEM_YUV: + cnt |= ISPRSZ_CNT_INPSRC; + cnt &= ~ISPRSZ_CNT_INPTYP; + break; + case RSZ_MEM_COL8: + cnt |= ISPRSZ_CNT_INPSRC; + cnt |= ISPRSZ_CNT_INPTYP; + break; + default: + dev_err(isp_res->dev, "resizer: Wrong Input\n"); + return -EINVAL; + } + isp_reg_or(isp_res->dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, cnt); + ispresizer_config_ycpos(isp_res, 0); + ispresizer_config_filter_coef(isp_res, &ispreszdefcoef); + ispresizer_enable_cbilin(isp_res, 0); + ispresizer_config_luma_enhance(isp_res, &isp_res->defaultyenh); + DPRINTK_ISPRESZ("ispresizer_config_datapath()-\n"); + return 0; +} +EXPORT_SYMBOL(ispresizer_config_datapath); + +/** + * ispresizer_try_size - Validates input and output images size. + * @input_w: input width for the resizer in number of pixels per line + * @input_h: input height for the resizer in number of lines + * @output_w: output width from the resizer in number of pixels per line + * resizer when writing to memory needs this to be multiple of 16. + * @pipe->rsz_out_h: output height for the resizer in number of lines, must be + * even. + * + * Calculates the horizontal and vertical resize ratio, number of pixels to + * be cropped in the resizer module and checks the validity of various + * parameters. Formula used for calculation is:- + * + * 8-phase 4-tap mode :- + * inputwidth = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 + * inputheight = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 + * endpahse for width = ((32 * sph + (ow - 1) * hrsz + 16) >> 5) % 8 + * endphase for height = ((32 * sph + (oh - 1) * hrsz + 16) >> 5) % 8 + * + * 4-phase 7-tap mode :- + * inputwidth = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 + * inputheight = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 + * endpahse for width = ((64 * sph + (ow - 1) * hrsz + 32) >> 6) % 4 + * endphase for height = ((64 * sph + (oh - 1) * hrsz + 32) >> 6) % 4 + * + * Where: + * sph = Start phase horizontal + * spv = Start phase vertical + * ow = Output width + * oh = Output height + * hrsz = Horizontal resize value + * vrsz = Vertical resize value + * + * Fills up the output/input widht/height, horizontal/vertical resize ratio, + * horizontal/vertical crop variables in the isp_res structure. + **/ +int ispresizer_try_pipeline(struct isp_res_device *isp_res, + struct isp_pipeline *pipe) +{ + u32 rsz, rsz_7, rsz_4; + u32 sph; + int max_in_otf, max_out_7tap; + + if (pipe->rsz_crop.width < 32 || pipe->rsz_crop.height < 32) { + DPRINTK_ISPCCDC("ISP_ERR: RESIZER cannot handle input width" + " less than 32 pixels or height less than" + " 32\n"); + return -EINVAL; + } + + if (pipe->rsz_crop.height > MAX_IN_HEIGHT) + return -EINVAL; + + if (pipe->rsz_out_w < 16) + pipe->rsz_out_w = 16; + + if (pipe->rsz_out_h < 2) + pipe->rsz_out_h = 2; + + if (omap_rev() == OMAP3430_REV_ES1_0) { + max_in_otf = MAX_IN_WIDTH_ONTHEFLY_MODE; + max_out_7tap = MAX_7TAP_VRSZ_OUTWIDTH; + } else { + max_in_otf = MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; + max_out_7tap = MAX_7TAP_VRSZ_OUTWIDTH_ES2; + } + + if (pipe->rsz_in == RSZ_OTFLY_YUV) { + if (pipe->rsz_crop.width > max_in_otf) + return -EINVAL; + } else { + if (pipe->rsz_crop.width > MAX_IN_WIDTH_MEMORY_MODE) + return -EINVAL; + } + + pipe->rsz_out_h &= 0xfffffffe; + sph = DEFAULTSTPHASE; + + rsz_7 = ((pipe->rsz_crop.height - 7) * 256) / (pipe->rsz_out_h - 1); + rsz_4 = ((pipe->rsz_crop.height - 4) * 256) / (pipe->rsz_out_h - 1); + + rsz = (pipe->rsz_crop.height * 256) / pipe->rsz_out_h; + + if (rsz <= MID_RESIZE_VALUE) { + rsz = rsz_4; + if (rsz < MINIMUM_RESIZE_VALUE) { + rsz = MINIMUM_RESIZE_VALUE; + pipe->rsz_out_h = + (((pipe->rsz_crop.height - 4) * 256) / rsz) + 1; + dev_dbg(isp_res->dev, + "resizer: %s: using height %d instead\n", + __func__, pipe->rsz_out_h); + } + } else { + rsz = rsz_7; + if (pipe->rsz_out_w > max_out_7tap) + pipe->rsz_out_w = max_out_7tap; + if (rsz > MAXIMUM_RESIZE_VALUE) { + rsz = MAXIMUM_RESIZE_VALUE; + pipe->rsz_out_h = + (((pipe->rsz_crop.height - 7) * 256) / rsz) + 1; + dev_dbg(isp_res->dev, + "resizer: %s: using height %d instead\n", + __func__, pipe->rsz_out_h); + } + } + + if (rsz > MID_RESIZE_VALUE) { + pipe->rsz_crop.height = + (((64 * sph) + ((pipe->rsz_out_h - 1) * rsz) + 32) + / 256) + 7; + } else { + pipe->rsz_crop.height = + (((32 * sph) + ((pipe->rsz_out_h - 1) * rsz) + 16) + / 256) + 4; + } + + isp_res->v_resz = rsz; + /* FIXME: pipe->rsz_crop.height here is the real input height! */ + isp_res->v_startphase = sph; + + pipe->rsz_out_w &= 0xfffffff0; + sph = DEFAULTSTPHASE; + + rsz_7 = ((pipe->rsz_crop.width - 7) * 256) / (pipe->rsz_out_w - 1); + rsz_4 = ((pipe->rsz_crop.width - 4) * 256) / (pipe->rsz_out_w - 1); + + rsz = (pipe->rsz_crop.width * 256) / pipe->rsz_out_w; + if (rsz > MID_RESIZE_VALUE) { + rsz = rsz_7; + if (rsz > MAXIMUM_RESIZE_VALUE) { + rsz = MAXIMUM_RESIZE_VALUE; + pipe->rsz_out_w = + (((pipe->rsz_crop.width - 7) * 256) / rsz) + 1; + pipe->rsz_out_w = (pipe->rsz_out_w + 0xf) & 0xfffffff0; + dev_dbg(isp_res->dev, + "resizer: %s: using width %d instead\n", + __func__, pipe->rsz_out_w); + } + } else { + rsz = rsz_4; + if (rsz < MINIMUM_RESIZE_VALUE) { + rsz = MINIMUM_RESIZE_VALUE; + pipe->rsz_out_w = + (((pipe->rsz_crop.width - 4) * 256) / rsz) + 1; + pipe->rsz_out_w = (pipe->rsz_out_w + 0xf) & 0xfffffff0; + dev_dbg(isp_res->dev, + "resizer: %s: using width %d instead\n", + __func__, pipe->rsz_out_w); + } + } + + /* Recalculate input based on TRM equations */ + if (rsz > MID_RESIZE_VALUE) { + pipe->rsz_crop.width = + (((64 * sph) + ((pipe->rsz_out_w - 1) * rsz) + 32) + / 256) + 7; + } else { + pipe->rsz_crop.width = + (((32 * sph) + ((pipe->rsz_out_w - 1) * rsz) + 16) + / 256) + 7; + } + + isp_res->h_resz = rsz; + /* FIXME: pipe->rsz_crop.width here is the real input width! */ + isp_res->h_startphase = sph; + + pipe->rsz_out_w_img = pipe->rsz_out_w; + + return 0; +} +EXPORT_SYMBOL(ispresizer_try_pipeline); + +/** + * ispresizer_config_size - Configures input and output image size. + * @pipe->rsz_crop.width: input width for the resizer in number of pixels per + * line. + * @pipe->rsz_crop.height: input height for the resizer in number of lines. + * @pipe->rsz_out_w: output width from the resizer in number of pixels per line. + * @pipe->rsz_out_h: output height for the resizer in number of lines. + * + * Configures the appropriate values stored in the isp_res structure in the + * resizer registers. + * + * Returns 0 if successful, or -EINVAL if passed values haven't been verified + * with ispresizer_try_size() previously. + **/ +int ispresizer_s_pipeline(struct isp_res_device *isp_res, + struct isp_pipeline *pipe) +{ + int i, j; + u32 res; + int rval; + + rval = ispresizer_config_datapath(isp_res, pipe); + if (rval) + return rval; + + /* Set Resizer input address and offset adderss */ + ispresizer_config_inlineoffset(isp_res, + pipe->prv_out_w * ISP_BYTES_PER_PIXEL); + + res = isp_reg_readl(isp_res->dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & + ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); + isp_reg_writel(isp_res->dev, res | + (isp_res->h_startphase << ISPRSZ_CNT_HSTPH_SHIFT) | + (isp_res->v_startphase << ISPRSZ_CNT_VSTPH_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_CNT); + /* Set start address for cropping */ + ispresizer_set_inaddr(isp_res, isp_res->tmp_buf); + + isp_reg_writel(isp_res->dev, + (pipe->rsz_crop.width << ISPRSZ_IN_SIZE_HORZ_SHIFT) | + (pipe->rsz_crop.height << + ISPRSZ_IN_SIZE_VERT_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_IN_SIZE); + if (!isp_res->algo) { + isp_reg_writel(isp_res->dev, + (pipe->rsz_out_w << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | + (pipe->rsz_out_h << ISPRSZ_OUT_SIZE_VERT_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_OUT_SIZE); + } else { + isp_reg_writel(isp_res->dev, + ((pipe->rsz_out_w - 4) + << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | + (pipe->rsz_out_h << ISPRSZ_OUT_SIZE_VERT_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_OUT_SIZE); + } + + res = isp_reg_readl(isp_res->dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & + ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); + isp_reg_writel(isp_res->dev, res | + ((isp_res->h_resz - 1) << ISPRSZ_CNT_HRSZ_SHIFT) | + ((isp_res->v_resz - 1) << ISPRSZ_CNT_VRSZ_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_CNT); + if (isp_res->h_resz <= MID_RESIZE_VALUE) { + j = 0; + for (i = 0; i < 16; i++) { + isp_reg_writel(isp_res->dev, + (isp_res->coeflist.h_filter_coef_4tap[j] + << ISPRSZ_HFILT10_COEF0_SHIFT) | + (isp_res->coeflist.h_filter_coef_4tap[j + 1] + << ISPRSZ_HFILT10_COEF1_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_HFILT10 + (i * 0x04)); + j += 2; + } + } else { + j = 0; + for (i = 0; i < 16; i++) { + if ((i + 1) % 4 == 0) { + isp_reg_writel(isp_res->dev, + (isp_res->coeflist. + h_filter_coef_7tap[j] << + ISPRSZ_HFILT10_COEF0_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_HFILT10 + (i * 0x04)); + j += 1; + } else { + isp_reg_writel(isp_res->dev, + (isp_res->coeflist. + h_filter_coef_7tap[j] << + ISPRSZ_HFILT10_COEF0_SHIFT) | + (isp_res->coeflist. + h_filter_coef_7tap[j+1] << + ISPRSZ_HFILT10_COEF1_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_HFILT10 + (i * 0x04)); + j += 2; + } + } + } + if (isp_res->v_resz <= MID_RESIZE_VALUE) { + j = 0; + for (i = 0; i < 16; i++) { + isp_reg_writel(isp_res->dev, (isp_res->coeflist. + v_filter_coef_4tap[j] << + ISPRSZ_VFILT10_COEF0_SHIFT) | + (isp_res->coeflist. + v_filter_coef_4tap[j + 1] << + ISPRSZ_VFILT10_COEF1_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_VFILT10 + (i * 0x04)); + j += 2; + } + } else { + j = 0; + for (i = 0; i < 16; i++) { + if ((i + 1) % 4 == 0) { + isp_reg_writel(isp_res->dev, + (isp_res->coeflist. + v_filter_coef_7tap[j] << + ISPRSZ_VFILT10_COEF0_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_VFILT10 + (i * 0x04)); + j += 1; + } else { + isp_reg_writel(isp_res->dev, + (isp_res->coeflist. + v_filter_coef_7tap[j] << + ISPRSZ_VFILT10_COEF0_SHIFT) | + (isp_res->coeflist. + v_filter_coef_7tap[j+1] << + ISPRSZ_VFILT10_COEF1_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_VFILT10 + (i * 0x04)); + j += 2; + } + } + } + + ispresizer_config_outlineoffset(isp_res, pipe->rsz_out_w*2); + + if (pipe->pix.pixelformat == V4L2_PIX_FMT_UYVY) + ispresizer_config_ycpos(isp_res, 0); + else + ispresizer_config_ycpos(isp_res, 1); + + DPRINTK_ISPRESZ("ispresizer_config_size()-\n"); + return 0; +} +EXPORT_SYMBOL(ispresizer_s_pipeline); + +/** + * ispresizer_enable - Enables the resizer module. + * @enable: 1 - Enable, 0 - Disable + * + * Client should configure all the sub modules in resizer before this. + **/ +void ispresizer_enable(struct isp_res_device *isp_res, int enable) +{ + int val; + DPRINTK_ISPRESZ("+ispresizer_enable()+\n"); + if (enable) { + val = (isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & 0x2) | + ISPRSZ_PCR_ENABLE; + } else { + val = isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & + ~ISPRSZ_PCR_ENABLE; + } + isp_reg_writel(isp_res->dev, val, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR); + DPRINTK_ISPRESZ("+ispresizer_enable()-\n"); +} +EXPORT_SYMBOL(ispresizer_enable); + +/** + * ispresizer_busy - Checks if ISP resizer is busy. + * + * Returns busy field from ISPRSZ_PCR register. + **/ +int ispresizer_busy(struct isp_res_device *isp_res) +{ + return isp_reg_readl(isp_res->dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & + ISPPRV_PCR_BUSY; +} +EXPORT_SYMBOL(ispresizer_busy); + +/** + * ispresizer_config_startphase - Sets the horizontal and vertical start phase. + * @hstartphase: horizontal start phase (0 - 7). + * @vstartphase: vertical startphase (0 - 7). + * + * This API just updates the isp_res struct. Actual register write happens in + * ispresizer_config_size. + **/ +void ispresizer_config_startphase(struct isp_res_device *isp_res, + u8 hstartphase, u8 vstartphase) +{ + DPRINTK_ISPRESZ("ispresizer_config_startphase()+\n"); + isp_res->h_startphase = hstartphase; + isp_res->v_startphase = vstartphase; + DPRINTK_ISPRESZ("ispresizer_config_startphase()-\n"); +} +EXPORT_SYMBOL(ispresizer_config_startphase); + +/** + * ispresizer_config_ycpos - Specifies if output should be in YC or CY format. + * @yc: 0 - YC format, 1 - CY format + **/ +void ispresizer_config_ycpos(struct isp_res_device *isp_res, u8 yc) +{ + DPRINTK_ISPRESZ("ispresizer_config_ycpos()+\n"); + isp_reg_and_or(isp_res->dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ~ISPRSZ_CNT_YCPOS, (yc ? ISPRSZ_CNT_YCPOS : 0)); + DPRINTK_ISPRESZ("ispresizer_config_ycpos()-\n"); +} +EXPORT_SYMBOL(ispresizer_config_ycpos); + +/** + * Sets the chrominance algorithm + * @cbilin: 0 - chrominance uses same processing as luminance, + * 1 - bilinear interpolation processing + **/ +void ispresizer_enable_cbilin(struct isp_res_device *isp_res, u8 enable) +{ + DPRINTK_ISPRESZ("ispresizer_enable_cbilin()+\n"); + isp_reg_and_or(isp_res->dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, + ~ISPRSZ_CNT_CBILIN, (enable ? ISPRSZ_CNT_CBILIN : 0)); + DPRINTK_ISPRESZ("ispresizer_enable_cbilin()-\n"); +} +EXPORT_SYMBOL(ispresizer_enable_cbilin); + +/** + * ispresizer_config_luma_enhance - Configures luminance enhancer parameters. + * @yenh: Pointer to structure containing desired values for core, slope, gain + * and algo parameters. + **/ +void ispresizer_config_luma_enhance(struct isp_res_device *isp_res, + struct isprsz_yenh *yenh) +{ + DPRINTK_ISPRESZ("ispresizer_config_luma_enhance()+\n"); + isp_res->algo = yenh->algo; + isp_reg_writel(isp_res->dev, (yenh->algo << ISPRSZ_YENH_ALGO_SHIFT) | + (yenh->gain << ISPRSZ_YENH_GAIN_SHIFT) | + (yenh->slope << ISPRSZ_YENH_SLOP_SHIFT) | + (yenh->coreoffset << ISPRSZ_YENH_CORE_SHIFT), + OMAP3_ISP_IOMEM_RESZ, + ISPRSZ_YENH); + DPRINTK_ISPRESZ("ispresizer_config_luma_enhance()-\n"); +} +EXPORT_SYMBOL(ispresizer_config_luma_enhance); + +/** + * ispresizer_config_filter_coef - Sets filter coefficients for 4 & 7-tap mode. + * This API just updates the isp_res struct.Actual register write happens in + * ispresizer_config_size. + * @coef: Structure containing horizontal and vertical filter coefficients for + * both 4-tap and 7-tap mode. + **/ +void ispresizer_config_filter_coef(struct isp_res_device *isp_res, + struct isprsz_coef *coef) +{ + int i; + DPRINTK_ISPRESZ("ispresizer_config_filter_coef()+\n"); + for (i = 0; i < 32; i++) { + isp_res->coeflist.h_filter_coef_4tap[i] = + coef->h_filter_coef_4tap[i]; + isp_res->coeflist.v_filter_coef_4tap[i] = + coef->v_filter_coef_4tap[i]; + } + for (i = 0; i < 28; i++) { + isp_res->coeflist.h_filter_coef_7tap[i] = + coef->h_filter_coef_7tap[i]; + isp_res->coeflist.v_filter_coef_7tap[i] = + coef->v_filter_coef_7tap[i]; + } + DPRINTK_ISPRESZ("ispresizer_config_filter_coef()-\n"); +} +EXPORT_SYMBOL(ispresizer_config_filter_coef); + +/** + * ispresizer_config_inlineoffset - Configures the read address line offset. + * @offset: Line Offset for the input image. + * + * Returns 0 if successful, or -EINVAL if offset is not 32 bits aligned. + **/ +int ispresizer_config_inlineoffset(struct isp_res_device *isp_res, u32 offset) +{ + DPRINTK_ISPRESZ("ispresizer_config_inlineoffset()+\n"); + if (offset % 32) + return -EINVAL; + isp_reg_writel(isp_res->dev, offset << ISPRSZ_SDR_INOFF_OFFSET_SHIFT, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF); + DPRINTK_ISPRESZ("ispresizer_config_inlineoffset()-\n"); + return 0; +} +EXPORT_SYMBOL(ispresizer_config_inlineoffset); + +/** + * ispresizer_set_inaddr - Sets the memory address of the input frame. + * @addr: 32bit memory address aligned on 32byte boundary. + * + * Returns 0 if successful, or -EINVAL if address is not 32 bits aligned. + **/ +int ispresizer_set_inaddr(struct isp_res_device *isp_res, u32 addr) +{ + struct isp_device *isp = + container_of(isp_res, struct isp_device, isp_res); + + DPRINTK_ISPRESZ("ispresizer_set_inaddr()+\n"); + + if (addr % 32) + return -EINVAL; + isp_res->tmp_buf = addr; + /* FIXME: is this the right place to put crop-related junk? */ + isp_reg_writel(isp_res->dev, + isp_res->tmp_buf + ISP_BYTES_PER_PIXEL + * ((isp->pipeline.rsz_crop.left & ~0xf) + + isp->pipeline.prv_out_w + * isp->pipeline.rsz_crop.top), + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); + /* Set the fractional part of the starting address. Needed for crop */ + isp_reg_writel(isp_res->dev, ((isp->pipeline.rsz_crop.left & 0xf) << + ISPRSZ_IN_START_HORZ_ST_SHIFT) | + (0x00 << ISPRSZ_IN_START_VERT_ST_SHIFT), + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START); + + DPRINTK_ISPRESZ("ispresizer_set_inaddr()-\n"); + return 0; +} +EXPORT_SYMBOL(ispresizer_set_inaddr); + +/** + * ispresizer_config_outlineoffset - Configures the write address line offset. + * @offset: Line offset for the preview output. + * + * Returns 0 if successful, or -EINVAL if address is not 32 bits aligned. + **/ +int ispresizer_config_outlineoffset(struct isp_res_device *isp_res, u32 offset) +{ + DPRINTK_ISPRESZ("ispresizer_config_outlineoffset()+\n"); + if (offset % 32) + return -EINVAL; + isp_reg_writel(isp_res->dev, offset << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF); + DPRINTK_ISPRESZ("ispresizer_config_outlineoffset()-\n"); + return 0; +} +EXPORT_SYMBOL(ispresizer_config_outlineoffset); + +/** + * Configures the memory address to which the output frame is written. + * @addr: 32bit memory address aligned on 32byte boundary. + **/ +int ispresizer_set_outaddr(struct isp_res_device *isp_res, u32 addr) +{ + DPRINTK_ISPRESZ("ispresizer_set_outaddr()+\n"); + if (addr % 32) + return -EINVAL; + isp_reg_writel(isp_res->dev, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD); + DPRINTK_ISPRESZ("ispresizer_set_outaddr()-\n"); + return 0; +} +EXPORT_SYMBOL(ispresizer_set_outaddr); + +/** + * ispresizer_save_context - Saves the values of the resizer module registers. + **/ +void ispresizer_save_context(struct device *dev) +{ + DPRINTK_ISPRESZ("Saving context\n"); + isp_save_context(dev, isprsz_reg_list); +} +EXPORT_SYMBOL(ispresizer_save_context); + +/** + * ispresizer_restore_context - Restores resizer module register values. + **/ +void ispresizer_restore_context(struct device *dev) +{ + DPRINTK_ISPRESZ("Restoring context\n"); + isp_restore_context(dev, isprsz_reg_list); +} +EXPORT_SYMBOL(ispresizer_restore_context); + +/** + * ispresizer_print_status - Prints the values of the resizer module registers. + **/ +void ispresizer_print_status(struct isp_res_device *isp_res) +{ + if (!is_ispresz_debug_enabled()) + return; + DPRINTK_ISPRESZ("###ISP_CTRL inresizer =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); + DPRINTK_ISPRESZ("###ISP_IRQ0ENABLE in resizer =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE)); + DPRINTK_ISPRESZ("###ISP_IRQ0STATUS in resizer =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS)); + DPRINTK_ISPRESZ("###RSZ PCR =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR)); + DPRINTK_ISPRESZ("###RSZ CNT =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT)); + DPRINTK_ISPRESZ("###RSZ OUT SIZE =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE)); + DPRINTK_ISPRESZ("###RSZ IN START =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START)); + DPRINTK_ISPRESZ("###RSZ IN SIZE =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE)); + DPRINTK_ISPRESZ("###RSZ SDR INADD =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD)); + DPRINTK_ISPRESZ("###RSZ SDR INOFF =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF)); + DPRINTK_ISPRESZ("###RSZ SDR OUTADD =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD)); + DPRINTK_ISPRESZ("###RSZ SDR OTOFF =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF)); + DPRINTK_ISPRESZ("###RSZ YENH =0x%x\n", + isp_reg_readl(isp_res->dev, + OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH)); +} +EXPORT_SYMBOL(ispresizer_print_status); + +/** + * isp_resizer_init - Module Initialisation. + * + * Always returns 0. + **/ +int __init isp_resizer_init(struct device *dev) +{ + struct isp_device *isp = dev_get_drvdata(dev); + struct isp_res_device *isp_res = &isp->isp_res; + + mutex_init(&isp_res->ispres_mutex); + isp_res->dev = dev; + + return 0; +} + +/** + * isp_resizer_cleanup - Module Cleanup. + **/ +void isp_resizer_cleanup(struct device *dev) +{ +} diff --git a/drivers/media/video/isp/ispresizer.h b/drivers/media/video/isp/ispresizer.h new file mode 100644 index 00000000000..27212f7a6dd --- /dev/null +++ b/drivers/media/video/isp/ispresizer.h @@ -0,0 +1,190 @@ +/* + * ispresizer.h + * + * Driver header file for Resizer module in TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sameer Venkatraman + * Mohit Jalori + * Sergio Aguirre + * + * 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 OMAP_ISP_RESIZER_H +#define OMAP_ISP_RESIZER_H + +/* + * Resizer Constants + */ +#define MAX_IN_WIDTH_MEMORY_MODE 4095 + +#define MAX_IN_WIDTH_ONTHEFLY_MODE 1280 +#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095 +#define MAX_IN_HEIGHT 4095 +#define MINIMUM_RESIZE_VALUE 64 +#define MAXIMUM_RESIZE_VALUE 1024 +#define MID_RESIZE_VALUE 512 + +#define MAX_7TAP_HRSZ_OUTWIDTH 1280 +#define MAX_7TAP_VRSZ_OUTWIDTH 640 + +#define MAX_7TAP_HRSZ_OUTWIDTH_ES2 3300 +#define MAX_7TAP_VRSZ_OUTWIDTH_ES2 1650 + +#define DEFAULTSTPIXEL 0 +#define DEFAULTSTPHASE 1 +#define DEFAULTHSTPIXEL4TAPMODE 3 +#define FOURPHASE 4 +#define EIGHTPHASE 8 +#define RESIZECONSTANT 256 +#define SHIFTER4TAPMODE 0 +#define SHIFTER7TAPMODE 1 +#define DEFAULTOFFSET 7 +#define OFFSETVERT4TAPMODE 4 +#define OPWDALIGNCONSTANT 0xfffffff0 + +/* + * The client is supposed to call resizer API in the following sequence: + * - request() + * - config_datatpath() + * - optionally config/enable sub modules + * - try/config size + * - setup callback + * - setup in/out memory offsets and ptrs + * - enable() + * ... + * - disable() + * - free() + */ + +enum resizer_input { + RSZ_OTFLY_YUV, + RSZ_MEM_YUV, + RSZ_MEM_COL8 +}; + +/** + * struct isprsz_coef - Structure for resizer filter coeffcients. + * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap + * mode (.5x-4x) + * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap + * mode (.5x-4x) + * @h_filter_coef_7tap: Horizontal filter coefficients for 4-phase/7-tap + * mode (.25x-.5x) + * @v_filter_coef_7tap: Vertical filter coefficients for 4-phase/7-tap + * mode (.25x-.5x) + */ +struct isprsz_coef { + u16 h_filter_coef_4tap[32]; + u16 v_filter_coef_4tap[32]; + u16 h_filter_coef_7tap[28]; + u16 v_filter_coef_7tap[28]; +}; + +/** + * struct isprsz_yenh - Structure for resizer luminance enhancer parameters. + * @algo: Algorithm select. + * @gain: Maximum gain. + * @slope: Slope. + * @coreoffset: Coring offset. + */ +struct isprsz_yenh { + u8 algo; + u8 gain; + u8 slope; + u8 coreoffset; +}; + +/** + * struct isp_res_device - Structure for the resizer module to store its + * information. + * @res_inuse: Indicates if resizer module has been reserved. 1 - Reserved, + * 0 - Freed. + * @h_startphase: Horizontal starting phase. + * @v_startphase: Vertical starting phase. + * @h_resz: Horizontal resizing value. + * @v_resz: Vertical resizing value. + * @outputwidth: Output Image Width in pixels. + * @outputheight: Output Image Height in pixels. + * @inputwidth: Input Image Width in pixels. + * @inputheight: Input Image Height in pixels. + * @algo: Algorithm select. 0 - Disable, 1 - [-1 2 -1]/2 high-pass filter, + * 2 - [-1 -2 6 -2 -1]/4 high-pass filter. + * @ipht_crop: Vertical start line for cropping. + * @ipwd_crop: Horizontal start pixel for cropping. + * @cropwidth: Crop Width. + * @cropheight: Crop Height. + * @resinput: Resizer input. + * @coeflist: Register configuration for Resizer. + * @ispres_mutex: Mutex for isp resizer. + */ +struct isp_res_device { + u8 res_inuse; + u8 h_startphase; + u8 v_startphase; + u16 h_resz; + u16 v_resz; + u8 algo; + dma_addr_t tmp_buf; + struct isprsz_coef coeflist; + struct mutex ispres_mutex; /* For checking/modifying res_inuse */ + struct isprsz_yenh defaultyenh; + struct device *dev; + int applycrop; +}; + +int ispresizer_config_crop(struct isp_res_device *isp_res, + struct v4l2_crop *a); +void ispresizer_config_shadow_registers(struct isp_res_device *isp_res); + +int ispresizer_request(struct isp_res_device *isp_res); + +int ispresizer_free(struct isp_res_device *isp_res); + +void ispresizer_enable_cbilin(struct isp_res_device *isp_res, u8 enable); + +void ispresizer_config_ycpos(struct isp_res_device *isp_res, u8 yc); + +void ispresizer_config_startphase(struct isp_res_device *isp_res, + u8 hstartphase, u8 vstartphase); + +void ispresizer_config_filter_coef(struct isp_res_device *isp_res, + struct isprsz_coef *coef); + +void ispresizer_config_luma_enhance(struct isp_res_device *isp_res, + struct isprsz_yenh *yenh); + +int ispresizer_try_pipeline(struct isp_res_device *isp_res, + struct isp_pipeline *pipe); + +int ispresizer_s_pipeline(struct isp_res_device *isp_res, + struct isp_pipeline *pipe); + +int ispresizer_config_inlineoffset(struct isp_res_device *isp_res, u32 offset); + +int ispresizer_set_inaddr(struct isp_res_device *isp_res, u32 addr); + +int ispresizer_config_outlineoffset(struct isp_res_device *isp_res, u32 offset); + +int ispresizer_set_outaddr(struct isp_res_device *isp_res, u32 addr); + +void ispresizer_enable(struct isp_res_device *isp_res, int enable); + +int ispresizer_busy(struct isp_res_device *isp_res); + +void ispresizer_save_context(struct device *dev); + +void ispresizer_restore_context(struct device *dev); + +void ispresizer_print_status(struct isp_res_device *isp_res); + +#endif /* OMAP_ISP_RESIZER_H */ diff --git a/drivers/media/video/isp/ispstat.c b/drivers/media/video/isp/ispstat.c new file mode 100644 index 00000000000..2d0e64f5e5f --- /dev/null +++ b/drivers/media/video/isp/ispstat.c @@ -0,0 +1,258 @@ +/* + * ispstat.c + * + * STAT module for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * + * 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 +#include + +#include "isp.h" + +/* Get next free buffer to write the statistics to and mark it active. */ +struct ispstat_buffer *ispstat_buf_next(struct ispstat *stat) +{ + unsigned long flags; + struct ispstat_buffer *found = NULL; + int i; + + if (stat->active_buf) + do_gettimeofday(&stat->active_buf->ts); + + spin_lock_irqsave(&stat->lock, flags); + + if (stat->active_buf) { + stat->active_buf->config_counter = stat->config_counter; + stat->active_buf->frame_number = stat->frame_number; + } + + for (i = 0; i < stat->nbufs; i++) { + struct ispstat_buffer *curr = &stat->buf[i]; + + /* + * Don't select the buffer which is being copied to + * userspace. + */ + if (curr == stat->locked_buf) + continue; + + if (!found + || (curr->frame_number > found->frame_number + && (curr->frame_number - found->frame_number + > stat->max_frame / 2)) + || (curr->frame_number < found->frame_number + && (found->frame_number - curr->frame_number + < stat->max_frame / 2))) { + found = curr; + } + } + + stat->active_buf = found; + + stat->frame_number++; + if (stat->frame_number == stat->max_frame) + stat->frame_number = 0; + + spin_unlock_irqrestore(&stat->lock, flags); + + return found; +} + +/* Get buffer to userspace. */ +static struct ispstat_buffer *ispstat_buf_find( + struct ispstat *stat, u32 frame_number) +{ + struct ispstat_buffer *latest = NULL; + int i; + + for (i = 0; i < stat->nbufs; i++) { + struct ispstat_buffer *curr = &stat->buf[i]; + + /* We cannot deal with the active buffer. */ + if (curr == stat->active_buf) + continue; + + /* Don't take uninitialised buffers. */ + if (curr->frame_number == stat->max_frame) + continue; + + /* Found correct number. */ + if (curr->frame_number == frame_number) { + latest = curr; + break; + } + + /* Select first buffer or a better one. */ + if (!latest + || (curr->frame_number < latest->frame_number + && (latest->frame_number - curr->frame_number + > stat->max_frame / 2)) + || (curr->frame_number > latest->frame_number + && (curr->frame_number - latest->frame_number + < stat->max_frame / 2))) + latest = curr; + } + + return latest; +} + +/** + * ispstat_stats_available - Check for stats available of specified frame. + * @aewbdata: Pointer to return AE AWB statistics data + * + * Returns 0 if successful, or -1 if statistics are unavailable. + **/ +struct ispstat_buffer *ispstat_buf_get(struct ispstat *stat, + void __user *ptr, + unsigned int frame_number) +{ + int rval = 0; + unsigned long flags; + struct ispstat_buffer *buf; + + spin_lock_irqsave(&stat->lock, flags); + + buf = ispstat_buf_find(stat, frame_number); + if (!buf) { + spin_unlock_irqrestore(&stat->lock, flags); + return ERR_PTR(-EBUSY); + } + + stat->locked_buf = buf; + + spin_unlock_irqrestore(&stat->lock, flags); + + rval = copy_to_user((void *)ptr, + buf->virt_addr, + stat->buf_size); + + if (rval) { + dev_info(stat->dev, + "failed copying %d bytes of stat data\n", rval); + buf = ERR_PTR(-EFAULT); + ispstat_buf_release(stat); + } + + return buf; +} + +void ispstat_buf_release(struct ispstat *stat) +{ + unsigned long flags; + + spin_lock_irqsave(&stat->lock, flags); + stat->locked_buf = NULL; + spin_unlock_irqrestore(&stat->lock, flags); +} + +void ispstat_bufs_free(struct ispstat *stat) +{ + struct isp_device *isp = dev_get_drvdata(stat->dev); + int i; + + for (i = 0; i < stat->nbufs; i++) { + struct ispstat_buffer *buf = &stat->buf[i]; + + if (!buf->iommu_addr) + continue; + + iommu_vfree(isp->iommu, buf->iommu_addr); + buf->iommu_addr = 0; + } + + stat->buf_alloc_size = 0; +} + +int ispstat_bufs_alloc(struct ispstat *stat, + unsigned int size) +{ + struct isp_device *isp = dev_get_drvdata(stat->dev); + unsigned long flags; + int i; + + spin_lock_irqsave(&stat->lock, flags); + + BUG_ON(stat->locked_buf != NULL); + + /* Are the old buffers big enough? */ + if (stat->buf_alloc_size >= size) { + for (i = 0; i < stat->nbufs; i++) + stat->buf[i].frame_number = stat->max_frame; + spin_unlock_irqrestore(&stat->lock, flags); + goto out; + } + + if (isp->running != ISP_STOPPED) { + dev_info(stat->dev, "stat: trying to configure when busy\n"); + spin_unlock_irqrestore(&stat->lock, flags); + return -EBUSY; + } + + spin_unlock_irqrestore(&stat->lock, flags); + + ispstat_bufs_free(stat); + + for (i = 0; i < stat->nbufs; i++) { + struct ispstat_buffer *buf = &stat->buf[i]; + + buf->iommu_addr = iommu_vmalloc(isp->iommu, 0, size, + IOMMU_FLAG); + if (buf->iommu_addr == 0) { + dev_info(stat->dev, "stat: Can't acquire memory for " + "buffer %d\n", i); + ispstat_bufs_free(stat); + return -ENOMEM; + } + buf->virt_addr = da_to_va(isp->iommu, (u32)buf->iommu_addr); + buf->frame_number = stat->max_frame; + } + + stat->buf_alloc_size = size; + +out: + stat->buf_size = size; + stat->active_buf = NULL; + + return 0; +} + +int ispstat_init(struct device *dev, struct ispstat *stat, + unsigned int nbufs, unsigned int max_frame) +{ + BUG_ON(nbufs < 2); + BUG_ON(max_frame < 2); + BUG_ON(nbufs >= max_frame); + + memset(stat, 0, sizeof(*stat)); + + stat->buf = kcalloc(nbufs, sizeof(*stat->buf), GFP_KERNEL); + if (!stat->buf) + return -ENOMEM; + + spin_lock_init(&stat->lock); + stat->nbufs = nbufs; + stat->dev = dev; + stat->max_frame = max_frame; + + return 0; +} + +void ispstat_free(struct ispstat *stat) +{ + ispstat_bufs_free(stat); + kfree(stat->buf); +} diff --git a/drivers/media/video/isp/ispstat.h b/drivers/media/video/isp/ispstat.h new file mode 100644 index 00000000000..12ab4d5ed83 --- /dev/null +++ b/drivers/media/video/isp/ispstat.h @@ -0,0 +1,65 @@ +/* + * ispstat.h + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Sakari Ailus + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef ISPSTAT_H +#define ISPSTAT_H + +#include "isp.h" + +struct ispstat_buffer { + unsigned long iommu_addr; + void *virt_addr; + struct timeval ts; + u32 config_counter; + u32 frame_number; +}; + +struct ispstat { + spinlock_t lock; /* Lock for this struct */ + + unsigned int nbufs; + struct ispstat_buffer *buf; + unsigned int buf_size; + unsigned int buf_alloc_size; + struct ispstat_buffer *active_buf; + struct ispstat_buffer *locked_buf; + unsigned int frame_number; + unsigned int max_frame; + unsigned int config_counter; + + struct device *dev; +}; + +struct ispstat_buffer *ispstat_buf_next(struct ispstat *stat); +struct ispstat_buffer *ispstat_buf_get(struct ispstat *stat, + void __user *ptr, + unsigned int frame_number); +void ispstat_buf_release(struct ispstat *stat); +void ispstat_bufs_free(struct ispstat *stat); +int ispstat_bufs_alloc(struct ispstat *stat, + unsigned int size); +int ispstat_init(struct device *dev, struct ispstat *stat, + unsigned int nbufs, unsigned int max_frame); +void ispstat_free(struct ispstat *stat); + +#endif /* ISPSTAT_H */ diff --git a/drivers/media/video/isp/luma_enhance_table.h b/drivers/media/video/isp/luma_enhance_table.h new file mode 100644 index 00000000000..99c8b05ed13 --- /dev/null +++ b/drivers/media/video/isp/luma_enhance_table.h @@ -0,0 +1,144 @@ +/* + * luma_enhance_table.h + * + * Luminance Enhancement table values for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 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. + */ + +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1047552, +1048575, +1047551, +1046527, +1045503, +1044479, +1043455, +1042431, +1041407, +1040383, +1039359, +1038335, +1037311, +1036287, +1035263, +1034239, +1033215, +1032191, +1031167, +1030143, +1028096, +1028096, +1028096, +1028096, +1028096, +1028096, +1028096, +1028096, +1028096, +1028096, +1028100, +1032196, +1036292, +1040388, +1044484, +0, +0, +0, +5, +5125, +10245, +15365, +20485, +25605, +30720, +30720, +30720, +30720, +30720, +30720, +30720, +30720, +30720, +30720, +30720, +31743, +30719, +29695, +28671, +27647, +26623, +25599, +24575, +23551, +22527, +21503, +20479, +19455, +18431, +17407, +16383, +15359, +14335, +13311, +12287, +11263, +10239, +9215, +8191, +7167, +6143, +5119, +4095, +3071, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024, +1024 diff --git a/drivers/media/video/isp/noise_filter_table.h b/drivers/media/video/isp/noise_filter_table.h new file mode 100644 index 00000000000..7345f90bad3 --- /dev/null +++ b/drivers/media/video/isp/noise_filter_table.h @@ -0,0 +1,79 @@ +/* + * noise_filter_table.h + * + * Noise Filter Table values for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 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. + */ +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +16, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31, +31 diff --git a/drivers/media/video/isp/omap_previewer.c b/drivers/media/video/isp/omap_previewer.c new file mode 100644 index 00000000000..647e1129655 --- /dev/null +++ b/drivers/media/video/isp/omap_previewer.c @@ -0,0 +1,1251 @@ +/* + * drivers/media/video/isp/omap_previewer.c + * + * Wrapper for Preview module in TI's OMAP3430 ISP + * + * Copyright (C) 2008 Texas Instruments, Inc. + * + * Contributors: + * Leonides Martinez + * Sergio Aguirre + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "isp.h" +#include "omap_previewer.h" + +#define OMAP_PREV_NAME "omap-previewer" + +#define BIT_SET(var, shift, mask, val) \ + do { \ + var = (var & ~(mask << shift)) \ + | (val << shift); \ + } while (0) + +/* +#define OMAP_ISP_PREVIEWER_DEBUG +*/ +#undef OMAP_ISP_PREVIEWER_DEBUG + +#ifdef OMAP_ISP_PREVIEWER_DEBUG +#define DPRINTK_PREVIEWER(format, ...) \ + printk(KERN_DEBUG "PREV: " format, ## __VA_ARGS__) +#else +#define DPRINTK_PREVIEWER(format, ...) +#endif + +#define ISP_CTRL_SBL_SHARED_RPORTB (1 << 28) +#define ISP_CTRL_SBL_SHARED_RPORTA (1 << 27) +#define SBL_RD_RAM_EN 18 + +static struct isp_interface_config prevwrap_config = { + .ccdc_par_ser = ISP_NONE, + .dataline_shift = 0, + .hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE, + .strobe = 0, + .prestrobe = 0, + .shutter = 0, + .wait_hs_vs = 0, +}; + +static u32 isp_ctrl; +static u32 prv_wsdr_addr; +static int prev_major = -1; +static struct device *prev_dev; +static struct class *prev_class; +static struct prev_device *prevdevice; +static struct platform_driver omap_previewer_driver; + +static u32 prev_bufsize; +static u32 lsc_bufsize; + +/** + * prev_calculate_crop - Calculate crop size according to device parameters + * @device: Structure containing ISP preview wrapper global information + * @crop: Structure containing crop size + * + * This function is used to calculate frame size reduction depending on + * the features enabled by the application. + **/ +static int prev_calculate_crop(struct prev_device *device, + struct prev_cropsize *crop) +{ + struct isp_device *isp = dev_get_drvdata(device->isp); + int ret; + struct isp_pipeline pipe; + + pipe.ccdc_out_w = pipe.ccdc_out_w_img = + device->size_params.hsize; + pipe.ccdc_out_h = device->size_params.vsize; + pipe.prv_in = PRV_RAW_MEM; + pipe.prv_out = PREVIEW_MEM; + + ret = isppreview_try_pipeline(&isp->isp_prev, &pipe); + + crop->hcrop = pipe.prv_out_w_img; + crop->vcrop = pipe.prv_out_h_img; + + return ret; +} + +/** + * prev_get_status - Get status of ISP preview module + * @status: Structure containing the busy state. + * + * Checks if the ISP preview module is busy. + * + * Returns 0 if successful, or -EINVAL if the status parameter is invalid. + **/ +static int prev_get_status(struct prev_device *device, + struct prev_status *status) +{ + struct isp_device *isp = dev_get_drvdata(device->isp); + + if (!status) { + dev_err(prev_dev, "get_status: invalid parameter\n"); + return -EINVAL; + } + status->hw_busy = (char)isppreview_busy(&isp->isp_prev); + return 0; +} + +/** + * prev_hw_setup - Stores the desired configuration in the proper HW registers + * @config: Structure containing the desired configuration for ISP preview + * module. + * + * Reads the structure sent, and modifies the desired registers. + * + * Always returns 0. + **/ +static int prev_hw_setup(struct prev_device *device, + struct prev_params *config) +{ + struct isp_device *isp = dev_get_drvdata(device->isp); + + if (config->features & PREV_AVERAGER) + isppreview_config_averager(&isp->isp_prev, config->average); + else + isppreview_config_averager(&isp->isp_prev, 0); + + if (config->features & PREV_INVERSE_ALAW) + isppreview_enable_invalaw(&isp->isp_prev, 1); + else + isppreview_enable_invalaw(&isp->isp_prev, 0); + + if (config->features & PREV_HORZ_MEDIAN_FILTER) { + isppreview_config_hmed(&isp->isp_prev, config->hmf_params); + isppreview_enable_hmed(&isp->isp_prev, 1); + } else + isppreview_enable_hmed(&isp->isp_prev, 0); + + if (config->features & PREV_DARK_FRAME_SUBTRACT) { + DPRINTK_PREVIEWER("[%s] darkaddr %08x, darklineoffset %d\n", + __func__, + config->drkf_params.addr, + config->drkf_params.offset); + isppreview_set_darkaddr(&isp->isp_prev, + config->drkf_params.addr); + isppreview_config_darklineoffset(&isp->isp_prev, + config->drkf_params.offset); + isppreview_enable_drkframe(&isp->isp_prev, 1); + } else + isppreview_enable_drkframe(&isp->isp_prev, 0); + + if (config->features & PREV_LENS_SHADING) { + isppreview_config_drkf_shadcomp(&isp->isp_prev, + config->lens_shading_shift); + isppreview_enable_shadcomp(&isp->isp_prev, 1); + } else + isppreview_enable_shadcomp(&isp->isp_prev, 0); + + if (config->ytable) + isppreview_set_luma_enhancement(&isp->isp_prev, + config->ytable); + + dev_dbg(prev_dev, "prev_hw_setup L\n"); + return 0; +} + +/** + * prev_validate_params - Validate configuration parameters for Preview Wrapper + * @params: Structure containing configuration parameters + * + * Validate configuration parameters for Preview Wrapper + * + * Returns 0 if successful, or -EINVAL if a parameter value is invalid. + **/ +static int prev_validate_params(struct prev_params *params) +{ + if (!params) { + dev_err(prev_dev, "validate_params: error in argument"); + goto err_einval; + } + + if ((params->features & PREV_AVERAGER) == PREV_AVERAGER) { + if ((params->average != NO_AVE) + && (params->average != AVE_2_PIX) + && (params->average != AVE_4_PIX) + && (params->average != AVE_8_PIX)) { + dev_err(prev_dev, "validate_params: wrong pix " + "average\n"); + goto err_einval; + } else if (((params->average == AVE_2_PIX) + && (params->size_params.hsize % 2)) + || ((params->average == AVE_4_PIX) + && (params->size_params.hsize % 4)) + || ((params->average == AVE_8_PIX) + && (params->size_params.hsize % 8))) { + dev_err(prev_dev, "validate_params: " + "wrong pix average for input size\n"); + goto err_einval; + } + } + + if ((params->size_params.pixsize != PREV_INWIDTH_8BIT) + && (params->size_params.pixsize + != PREV_INWIDTH_10BIT)) { + dev_err(prev_dev, "validate_params: wrong pixsize\n"); + goto err_einval; + } + + if (params->size_params.hsize > MAX_IMAGE_WIDTH + || params->size_params.hsize < 0) { + dev_err(prev_dev, "validate_params: wrong hsize\n"); + goto err_einval; + } + + if (params->size_params.hsize % 32) { + dev_err(prev_dev, "validate_params: width must be multiple of" + " 64 bytes\n"); + goto err_einval; + } + + if ((params->pix_fmt != YCPOS_YCrYCb) + && (YCPOS_YCbYCr != params->pix_fmt) + && (YCPOS_CbYCrY != params->pix_fmt) + && (YCPOS_CrYCbY != params->pix_fmt)) { + dev_err(prev_dev, "validate_params: wrong pix_fmt"); + goto err_einval; + } + + if ((params->features & PREV_DARK_FRAME_SUBTRACT) + && (params->features + & PREV_DARK_FRAME_CAPTURE)) { + dev_err(prev_dev, "validate_params: DARK FRAME CAPTURE and " + "SUBSTRACT cannot be enabled " + "at same time\n"); + goto err_einval; + } + + if ((params->size_params.in_pitch <= 0) + || (params->size_params.in_pitch % 32)) { + params->size_params.in_pitch = + (params->size_params.hsize * 2) & 0xFFE0; + dev_err(prev_dev, "Error in in_pitch; new value = %d\n", + params->size_params.in_pitch); + } + + return 0; +err_einval: + return -EINVAL; +} + +/** + * preview_isr - Callback from ISP driver for ISP Preview Interrupt + * @status: ISP IRQ0STATUS register value + * @arg1: Structure containing ISP preview wrapper global information + * @arg2: Currently not used + **/ +static void prev_isr(unsigned long status, isp_vbq_callback_ptr arg1, + void *arg2) +{ + struct prev_device *device = (struct prev_device *)arg1; + + if ((status & PREV_DONE) != PREV_DONE) + return; + + if (device) + complete(&device->wfc); +} + +/* + * Set shared ports for using dark frame (lens shading) + */ +static void prev_set_isp_ctrl(u16 mode) +{ + struct prev_device *device = prevdevice; + u32 val; + + val = isp_reg_readl(device->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + + isp_ctrl = val; + + /* Read port used by preview module data read */ + val &= ~ISP_CTRL_SBL_SHARED_RPORTA; + + /* Read port used by preview module dark frame read */ + if (mode & (PREV_DARK_FRAME_SUBTRACT | PREV_LENS_SHADING)) + val &= ~ISP_CTRL_SBL_SHARED_RPORTB; + + BIT_SET(val, SBL_RD_RAM_EN, 0x1, 0x1); + + /* write ISP CTRL register */ + isp_reg_writel(device->isp, val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + + prv_wsdr_addr = isp_reg_readl(device->isp, OMAP3_ISP_IOMEM_PREV, + ISPPRV_WSDR_ADDR); +} + +/* + * Set old isp shared port configuration + */ +static void prev_unset_isp_ctrl(void) +{ + struct prev_device *device = prevdevice; + struct isp_device *isp = dev_get_drvdata(device->isp); + u32 val; + + val = isp_reg_readl(device->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + + if (isp_ctrl & ISP_CTRL_SBL_SHARED_RPORTB) + val |= ISP_CTRL_SBL_SHARED_RPORTB; + + if (isp_ctrl & ISP_CTRL_SBL_SHARED_RPORTA) + val |= ISP_CTRL_SBL_SHARED_RPORTA; + + if (isp_ctrl & (1 << SBL_RD_RAM_EN)) + val &= ~(1 << SBL_RD_RAM_EN); + + /* write ISP CTRL register */ + isp_reg_writel(device->isp, val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); + + /* disable dark frame and shading compensation */ + isppreview_enable_drkframe(&isp->isp_prev, 0); + isppreview_enable_shadcomp(&isp->isp_prev, 0); + + /* Set output and input adresses to 0 */ + isppreview_set_outaddr(&isp->isp_prev, prv_wsdr_addr); +} + +static void isp_enable_interrupts(struct device *dev, int is_raw) +{ + isp_reg_writel(dev, IRQ0ENABLE_PRV_DONE_IRQ, + OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); +} + +/** + * prev_do_preview - Performs the Preview process + * @device: Structure containing ISP preview wrapper global information + * + * Returns 0 if successful, or -EINVAL if the sent parameters are invalid. + **/ +static int prev_do_preview(struct prev_device *device) +{ + struct isp_device *isp = dev_get_drvdata(device->isp); + struct isp_prev_device *isp_prev = &isp->isp_prev; + struct prev_params *config = &isp_prev->params; + int bpp, size; + int ret = 0; + struct isp_pipeline pipe; + + memset(&pipe, 0, sizeof(pipe)); + pipe.pix.pixelformat = V4L2_PIX_FMT_UYVY; + + prev_set_isp_ctrl(config->features); + + if (device->size_params.pixsize == PREV_INWIDTH_8BIT) + bpp = 1; + else + bpp = 2; + + size = device->size_params.hsize * device->size_params.vsize * bpp; + + pipe.prv_in = PRV_RAW_MEM; + pipe.prv_out = PREVIEW_MEM; + + isppreview_set_skip(isp_prev, 2, 0); + + pipe.ccdc_out_w = pipe.ccdc_out_w_img = device->size_params.hsize; + pipe.ccdc_out_h = device->size_params.vsize & ~0xf; + + ret = isppreview_try_pipeline(isp_prev, &pipe); + if (ret) { + dev_err(prev_dev, "ERROR while try size!\n"); + goto out; + } + + ret = isppreview_s_pipeline(isp_prev, &pipe); + if (ret) { + dev_err(prev_dev, "ERROR while config size!\n"); + goto out; + } + + ret = isppreview_config_inlineoffset(isp_prev, + pipe.prv_out_w * bpp); + if (ret) + goto out; + + ret = isppreview_config_outlineoffset(isp_prev, + (pipe.prv_out_w * bpp) - 32); + if (ret) + goto out; + + config->drkf_params.addr = device->isp_addr_lsc; + + prev_hw_setup(device, config); + + ret = isppreview_set_inaddr(isp_prev, device->isp_addr_read); + if (ret) + goto out; + + ret = isppreview_set_outaddr(isp_prev, device->isp_addr_read); + if (ret) + goto out; + + ret = isp_set_callback(device->isp, CBK_PREV_DONE, prev_isr, + (void *)device, (void *)NULL); + if (ret) { + dev_err(prev_dev, "ERROR while setting Previewer callback!\n"); + goto out; + } + + isp_configure_interface(device->isp, &prevwrap_config); + + isp_start(device->isp); + + isp_enable_interrupts(device->isp, 0); + + isppreview_enable(isp_prev); + + wait_for_completion_interruptible(&device->wfc); + + ret = isp_unset_callback(device->isp, CBK_PREV_DONE); + + prev_unset_isp_ctrl(); +out: + return ret; +} + +/** + * previewer_vbq_release - Videobuffer queue release + * @q: Structure containing the videobuffer queue. + * @vb: Structure containing the videobuffer used for previewer processing. + **/ +static void previewer_vbq_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct prev_fh *fh = q->priv_data; + struct prev_device *device = fh->device; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + ispmmu_vunmap(device->isp, device->isp_addr_read); + device->isp_addr_read = 0; + spin_lock(&device->inout_vbq_lock); + vb->state = VIDEOBUF_NEEDS_INIT; + spin_unlock(&device->inout_vbq_lock); + } else if (q->type == V4L2_BUF_TYPE_PRIVATE) { + ispmmu_vunmap(device->isp, device->isp_addr_lsc); + device->isp_addr_lsc = 0; + spin_lock(&device->lsc_vbq_lock); + vb->state = VIDEOBUF_NEEDS_INIT; + spin_unlock(&device->lsc_vbq_lock); + } + + if (vb->memory != V4L2_MEMORY_MMAP) { + videobuf_dma_unmap(q, videobuf_to_dma(vb)); + videobuf_dma_free(videobuf_to_dma(vb)); + } + + dev_dbg(prev_dev, "previewer_vbq_release\n"); +} + +/** + * previewer_vbq_setup - Sets up the videobuffer size and validates count. + * @q: Structure containing the videobuffer queue. + * @cnt: Number of buffers requested + * @size: Size in bytes of the buffer used for previewing + * + * Always returns 0. + **/ +static int previewer_vbq_setup(struct videobuf_queue *q, + unsigned int *cnt, + unsigned int *size) +{ + struct prev_fh *fh = q->priv_data; + struct prev_device *device = fh->device; + u32 bpp = 1; + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + spin_lock(&device->inout_vbq_lock); + + if (*cnt <= 0) + *cnt = 1; + + if (*cnt > VIDEO_MAX_FRAME) + *cnt = VIDEO_MAX_FRAME; + + if (!device->size_params.hsize || + !device->size_params.vsize) { + dev_err(prev_dev, "Can't setup inout buffer size\n"); + spin_unlock(&device->inout_vbq_lock); + return -EINVAL; + } + + if (device->size_params.pixsize == PREV_INWIDTH_10BIT) + bpp = 2; + *size = prev_bufsize = bpp * device->size_params.hsize * + device->size_params.vsize; + spin_unlock(&device->inout_vbq_lock); + } else if (q->type == V4L2_BUF_TYPE_PRIVATE) { + spin_lock(&device->lsc_vbq_lock); + if (*cnt <= 0) + *cnt = 1; + + if (*cnt > 1) + *cnt = 1; + + if (!device->size_params.hsize || + !device->size_params.vsize) { + dev_err(prev_dev, "Can't setup lsc buffer size\n"); + spin_unlock(&device->lsc_vbq_lock); + return -EINVAL; + } + + /* upsampled lsc table size - for now bpp = 2 */ + bpp = 2; + *size = lsc_bufsize = bpp * device->size_params.hsize * + device->size_params.vsize; + + spin_unlock(&device->lsc_vbq_lock); + } else { + return -EINVAL; + } + + dev_dbg(prev_dev, "previewer_vbq_setup\n"); + return 0; +} + +/** + * previewer_vbq_prepare - Videobuffer is prepared and mmapped. + * @q: Structure containing the videobuffer queue. + * @vb: Structure containing the videobuffer used for previewer processing. + * @field: Type of field to set in videobuffer device. + * + * Returns 0 if successful, or -EINVAL if buffer couldn't get allocated, or + * -EIO if the ISP MMU mapping fails + **/ +static int previewer_vbq_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct prev_fh *fh = q->priv_data; + struct prev_device *device = fh->device; + int err = -EINVAL; + unsigned int isp_addr; + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + dev_dbg(prev_dev, "previewer_vbq_prepare E\n"); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + spin_lock(&device->inout_vbq_lock); + + if (vb->baddr) { + vb->size = prev_bufsize; + vb->bsize = prev_bufsize; + DPRINTK_PREVIEWER("[%s] bsize = %d\n", __func__, + vb->bsize); + } else { + spin_unlock(&device->inout_vbq_lock); + dev_err(prev_dev, "No user buffer allocated\n"); + goto out; + } + + vb->width = device->size_params.hsize; + vb->height = device->size_params.vsize; + vb->field = field; + spin_unlock(&device->inout_vbq_lock); + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + DPRINTK_PREVIEWER("[%s] baddr = %08x\n", __func__, + (int)vb->baddr); + err = videobuf_iolock(q, vb, NULL); + if (!err) { + isp_addr = ispmmu_vmap(device->isp, + dma->sglist, + dma->sglen); + + if (!isp_addr) { + err = -EIO; + } else { + device->isp_addr_read = isp_addr; + DPRINTK_PREVIEWER("[%s] isp_addr_read =" + " %08x\n", __func__, isp_addr); + } + } + } + + if (!err) { + vb->state = VIDEOBUF_PREPARED; + flush_cache_user_range(NULL, vb->baddr, (vb->baddr + + vb->bsize)); + } else { + previewer_vbq_release(q, vb); + } + + } else if (q->type == V4L2_BUF_TYPE_PRIVATE) { + + spin_lock(&device->lsc_vbq_lock); + + if (vb->baddr) { + vb->size = lsc_bufsize; + vb->bsize = lsc_bufsize; + DPRINTK_PREVIEWER("[%s] bsize = %d\n", __func__, + vb->bsize); + } else { + spin_unlock(&device->lsc_vbq_lock); + dev_err(prev_dev, "No user buffer allocated\n"); + goto out; + } + + vb->width = device->size_params.hsize; + vb->height = device->size_params.vsize; + vb->field = field; + spin_unlock(&device->lsc_vbq_lock); + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + DPRINTK_PREVIEWER("[%s] baddr = %08x\n", __func__, + (int)vb->baddr); + err = videobuf_iolock(q, vb, NULL); + if (!err) { + isp_addr = ispmmu_vmap(device->isp, + dma->sglist, + dma->sglen); + if (!isp_addr) { + err = -EIO; + } else { + device->isp_addr_lsc = isp_addr; + DPRINTK_PREVIEWER("[%s] isp_addr_lsc =" + " %08x\n", __func__, isp_addr); + } + } + } + + if (!err) { + vb->state = VIDEOBUF_PREPARED; + flush_cache_user_range(NULL, vb->baddr, (vb->baddr + + vb->bsize)); + } else { + previewer_vbq_release(q, vb); + } + + } else { + return -EINVAL; + } + + dev_dbg(prev_dev, "previewer_vbq_prepare L\n"); +out: + return err; +} + +static void previewer_vbq_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + return; +} + +/** + * previewer_open - Initializes and opens the Preview Wrapper + * @inode: Inode structure associated with the Preview Wrapper + * @filp: File structure associated with the Preview Wrapper + * + * Returns 0 if successful, -EACCES if its unable to initialize default config, + * -EBUSY if its already opened or the ISP module is not available, or -ENOMEM + * if its unable to allocate the device in kernel space memory. + **/ +static int previewer_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct prev_device *device = prevdevice; + struct prev_fh *fh; + struct device *isp; + struct isp_device *isp_dev; + + if (device->opened || (filp->f_flags & O_NONBLOCK)) { + dev_err(prev_dev, "previewer_open: device is already " + "opened\n"); + return -EBUSY; + } + + fh = kzalloc(sizeof(struct prev_fh), GFP_KERNEL); + if (NULL == fh) { + ret = -ENOMEM; + goto err_fh; + } + + isp = isp_get(); + if (!isp) { + printk(KERN_ERR "Can't enable ISP clocks (ret %d)\n", ret); + ret = -EACCES; + goto err_isp; + } + device->isp = isp; + isp_dev = dev_get_drvdata(isp); + + ret = isppreview_request(&isp_dev->isp_prev); + if (ret) { + dev_err(prev_dev, "Can't acquire isppreview\n"); + goto err_prev; + } + + device->opened = 1; + + filp->private_data = fh; + fh->inout_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->lsc_type = V4L2_BUF_TYPE_PRIVATE; + fh->device = device; + + videobuf_queue_sg_init(&fh->inout_vbq, &device->vbq_ops, NULL, + &device->inout_vbq_lock, fh->inout_type, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), fh); + + videobuf_queue_sg_init(&fh->lsc_vbq, &device->vbq_ops, NULL, + &device->lsc_vbq_lock, fh->lsc_type, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), fh); + + init_completion(&device->wfc); + device->wfc.done = 0; + mutex_init(&device->prevwrap_mutex); + + return 0; + +err_prev: + isp_put(); +err_isp: + kfree(fh); +err_fh: + return ret; +} + +/** + * previewer_release - Releases Preview Wrapper and frees up allocated memory + * @inode: Inode structure associated with the Preview Wrapper + * @filp: File structure associated with the Preview Wrapper + * + * Always returns 0. + **/ +static int previewer_release(struct inode *inode, struct file *filp) +{ + struct prev_fh *fh = filp->private_data; + struct prev_device *device = fh->device; + struct isp_device *isp = dev_get_drvdata(device->isp); + struct videobuf_queue *q1 = &fh->inout_vbq; + struct videobuf_queue *q2 = &fh->lsc_vbq; + + device->opened = 0; + videobuf_mmap_free(q1); + videobuf_mmap_free(q2); + videobuf_queue_cancel(q1); + videobuf_queue_cancel(q2); + isppreview_free(&isp->isp_prev); + isp_put(); + prev_bufsize = 0; + lsc_bufsize = 0; + filp->private_data = NULL; + kfree(fh); + + dev_dbg(prev_dev, "previewer_release\n"); + return 0; +} + +/** + * previewer_mmap - Memory maps the Preview Wrapper module. + * @file: File structure associated with the Preview Wrapper + * @vma: Virtual memory area structure. + * + * Returns 0 if successful, or returned value by the videobuf_mmap_mapper() + * function. + **/ +static int previewer_mmap(struct file *file, struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static int previewer_get_param(struct prev_device *device, + struct prev_params __user *uparams) +{ + struct isp_device *isp_dev = dev_get_drvdata(device->isp); + struct prev_params *config = &isp_dev->isp_prev.params; + struct prev_params params; + __u32 *cfa_table; + __u32 *ytable; + __u32 *redtable; + __u32 *greentable; + __u32 *bluetable; + int ret; + + ret = copy_from_user(¶ms, uparams, sizeof(params)); + if (ret) { + dev_err(prev_dev, "GET_PARAM: nocopy: %d\n", ret); + return -EFAULT; + } + + /* Backup user pointers */ + cfa_table = params.cfa.cfa_table; + ytable = params.ytable; + redtable = params.gtable.redtable; + greentable = params.gtable.greentable; + bluetable = params.gtable.bluetable; + + memcpy(¶ms, config, sizeof(config)); + + /* Restore user pointers */ + params.cfa.cfa_table = cfa_table; + params.ytable = ytable; + params.gtable.redtable = redtable; + params.gtable.greentable = greentable; + params.gtable.bluetable = bluetable; + + ret = copy_to_user(uparams, ¶ms, sizeof(struct prev_params)); + if (ret) { + dev_err(prev_dev, "GET_PARAM: nocopy: %d\n", ret); + return -EFAULT; + } + + return ret; +} + +#define COPY_USERTABLE(dst, src, size) \ + if (src) { \ + if (!dst) \ + return -EACCES; \ + if (copy_from_user(dst, src, (size) * sizeof(*(dst)))) \ + return -EFAULT; \ + } + +/* Copy preview module configuration into use */ +static int previewer_set_param(struct prev_device *device, + struct prev_params __user *uparams) +{ + struct isp_device *isp_dev = dev_get_drvdata(device->isp); + struct prev_params *config = &isp_dev->isp_prev.params; + /* Here it should be safe to allocate 420 bytes from stack */ + struct prev_params p; + struct prev_params *params = &p; + int ret; + + ret = copy_from_user(params, uparams, sizeof(*params)); + if (ret) { + dev_err(prev_dev, "SET_PARAM: nocopy: %d\n", ret); + return -EFAULT; + } + ret = prev_validate_params(params); + if (ret < 0) + return -EINVAL; + + config->features = params->features; + config->pix_fmt = params->pix_fmt; + config->cfa.cfafmt = params->cfa.cfafmt; + + /* struct ispprev_cfa */ + config->cfa.cfa_gradthrs_vert = params->cfa.cfa_gradthrs_vert; + config->cfa.cfa_gradthrs_horz = params->cfa.cfa_gradthrs_horz; + COPY_USERTABLE(config->cfa.cfa_table, params->cfa.cfa_table, + ISPPRV_CFA_TBL_SIZE); + + /* struct ispprev_csup csup */ + config->csup.gain = params->csup.gain; + config->csup.thres = params->csup.thres; + config->csup.hypf_en = params->csup.hypf_en; + + COPY_USERTABLE(config->ytable, params->ytable, ISPPRV_YENH_TBL_SIZE); + + /* struct ispprev_nf nf */ + config->nf.spread = params->nf.spread; + memcpy(&config->nf.table, ¶ms->nf.table, sizeof(config->nf.table)); + + /* struct ispprev_dcor dcor */ + config->dcor.couplet_mode_en = params->dcor.couplet_mode_en; + memcpy(&config->dcor.detect_correct, ¶ms->dcor.detect_correct, + sizeof(config->dcor.detect_correct)); + + /* struct ispprev_gtable gtable */ + COPY_USERTABLE(config->gtable.redtable, params->gtable.redtable, + ISPPRV_GAMMA_TBL_SIZE); + COPY_USERTABLE(config->gtable.greentable, params->gtable.greentable, + ISPPRV_GAMMA_TBL_SIZE); + COPY_USERTABLE(config->gtable.bluetable, params->gtable.bluetable, + ISPPRV_GAMMA_TBL_SIZE); + + /* struct ispprev_wbal wbal */ + config->wbal.dgain = params->wbal.dgain; + config->wbal.coef3 = params->wbal.coef3; + config->wbal.coef2 = params->wbal.coef2; + config->wbal.coef1 = params->wbal.coef1; + config->wbal.coef0 = params->wbal.coef0; + + /* struct ispprev_blkadj blk_adj */ + config->blk_adj.red = params->blk_adj.red; + config->blk_adj.green = params->blk_adj.green; + config->blk_adj.blue = params->blk_adj.blue; + + /* struct ispprev_rgbtorgb rgb2rgb */ + memcpy(&config->rgb2rgb.matrix, ¶ms->rgb2rgb.matrix, + sizeof(config->rgb2rgb.matrix)); + memcpy(&config->rgb2rgb.offset, ¶ms->rgb2rgb.offset, + sizeof(config->rgb2rgb.offset)); + + /* struct ispprev_csc rgb2ycbcr */ + memcpy(&config->rgb2ycbcr.matrix, ¶ms->rgb2ycbcr.matrix, + sizeof(config->rgb2ycbcr.matrix)); + memcpy(&config->rgb2ycbcr.offset, ¶ms->rgb2ycbcr.offset, + sizeof(config->rgb2ycbcr.offset)); + + /* struct ispprev_hmed hmf_params */ + config->hmf_params.odddist = params->hmf_params.odddist; + config->hmf_params.evendist = params->hmf_params.evendist; + config->hmf_params.thres = params->hmf_params.thres; + + /* struct prev_darkfrm_params drkf_params not set here */ + + config->lens_shading_shift = params->lens_shading_shift; + config->average = params->average; + config->contrast = params->contrast; + config->brightness = params->brightness; + + device->size_params = params->size_params; + + return 0; +} + +/** + * previewer_ioctl - I/O control function for Preview Wrapper + * @inode: Inode structure associated with the Preview Wrapper. + * @file: File structure associated with the Preview Wrapper. + * @cmd: Type of command to execute. + * @arg: Argument to send to requested command. + * + * Returns 0 if successful, -1 if bad command passed or access is denied, + * -EFAULT if copy_from_user() or copy_to_user() fails, -EINVAL if parameter + * validation fails or parameter structure is not present + **/ +static int previewer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct prev_fh *fh = file->private_data; + struct prev_device *device = fh->device; + struct v4l2_buffer b; + struct v4l2_requestbuffers req; + + dev_dbg(prev_dev, "Entering previewer_ioctl()\n"); + + if ((_IOC_TYPE(cmd) != PREV_IOC_BASE) + || (_IOC_NR(cmd) > PREV_IOC_MAXNR)) { + dev_err(prev_dev, "Bad command Value \n"); + goto err_minusone; + } + + if (_IOC_DIR(cmd) & _IOC_READ) + ret = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + ret = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); + if (ret) { + dev_err(prev_dev, "access denied\n"); + goto err_minusone; + } + + switch (cmd) { + case PREV_REQBUF: + if (copy_from_user(&req, (struct v4l2_requestbuffers *)arg, + sizeof(struct v4l2_requestbuffers))) + return -EFAULT; + + if (mutex_lock_interruptible(&device->prevwrap_mutex)) + goto err_eintr; + + if (req.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + ret = videobuf_reqbufs(&fh->inout_vbq, &req); + else if (req.type == V4L2_BUF_TYPE_PRIVATE) + ret = videobuf_reqbufs(&fh->lsc_vbq, &req); + else + ret = -EINVAL; + + if (!ret && copy_to_user((struct v4l2_requestbuffers *)arg, + &req, sizeof(struct v4l2_requestbuffers))) + ret = -EFAULT; + + mutex_unlock(&device->prevwrap_mutex); + break; + + case PREV_QUERYBUF: + if (copy_from_user(&b, (struct v4l2_buffer *)arg, + sizeof(struct v4l2_buffer))) + return -EFAULT; + + if (mutex_lock_interruptible(&device->prevwrap_mutex)) + goto err_eintr; + + if (b.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + ret = videobuf_querybuf(&fh->inout_vbq, &b); + else if (b.type == V4L2_BUF_TYPE_PRIVATE) + ret = videobuf_querybuf(&fh->lsc_vbq, &b); + else + ret = -EINVAL; + + if (!ret && copy_to_user((struct v4l2_buffer *)arg, &b, + sizeof(struct v4l2_buffer))) + ret = -EFAULT; + + mutex_unlock(&device->prevwrap_mutex); + break; + + case PREV_QUEUEBUF: + if (copy_from_user(&b, (struct v4l2_buffer *)arg, + sizeof(struct v4l2_buffer))) + return -EFAULT; + + if (mutex_lock_interruptible(&device->prevwrap_mutex)) + goto err_eintr; + + if (b.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + ret = videobuf_qbuf(&fh->inout_vbq, &b); + else if (b.type == V4L2_BUF_TYPE_PRIVATE) + ret = videobuf_qbuf(&fh->lsc_vbq, &b); + else + ret = -EINVAL; + + mutex_unlock(&device->prevwrap_mutex); + break; + + case PREV_SET_PARAM: + if (mutex_lock_interruptible(&device->prevwrap_mutex)) + goto err_eintr; + ret = previewer_set_param(device, (struct prev_params *)arg); + mutex_unlock(&device->prevwrap_mutex); + break; + + case PREV_GET_PARAM: + if (mutex_lock_interruptible(&device->prevwrap_mutex)) + goto err_eintr; + ret = previewer_get_param(device, (struct prev_params *)arg); + mutex_unlock(&device->prevwrap_mutex); + break; + + case PREV_GET_STATUS: + { + struct prev_status status; + + ret = prev_get_status(device, &status); + if (ret) + break; + + if (copy_to_user((struct prev_status *)arg, &status, + sizeof(struct prev_status))) + ret = -EFAULT; + break; + } + + case PREV_PREVIEW: + if (mutex_lock_interruptible(&device->prevwrap_mutex)) + goto err_eintr; + ret = prev_do_preview(device); + mutex_unlock(&device->prevwrap_mutex); + break; + + case PREV_GET_CROPSIZE: { + struct prev_cropsize outputsize; + + memset(&outputsize, 0, sizeof(outputsize)); + ret = prev_calculate_crop(device, &outputsize); + if (ret) + break; + + if (copy_to_user((struct prev_cropsize *)arg, &outputsize, + sizeof(struct prev_cropsize))) + ret = -EFAULT; + break; + } + + default: + dev_err(prev_dev, "previewer_ioctl: Invalid Command Value\n"); + ret = -EINVAL; + } + + return ret; +err_minusone: + return -1; +err_eintr: + return -EINTR; +} + +/** + * previewer_platform_release - Acts when Reference count is zero + * @device: Structure containing ISP preview wrapper global information + * + * This is called when the reference count goes to zero + **/ +static void previewer_platform_release(struct device *device) +{ + dev_dbg(prev_dev, "previewer_platform_release()\n"); +} + +static const struct file_operations prev_fops = { + .owner = THIS_MODULE, + .open = previewer_open, + .release = previewer_release, + .mmap = previewer_mmap, + .ioctl = previewer_ioctl, +}; + +static struct platform_device omap_previewer_device = { + .name = OMAP_PREV_NAME, + .id = -1, + .dev = { + .release = previewer_platform_release, + } +}; + +/** + * previewer_probe - Checks for device presence + * @pdev: Structure containing details of the current device. + * + * Always returns 0 + **/ +static int previewer_probe(struct platform_device *pdev) +{ + return 0; +} + +/** + * previewer_remove - Handles the removal of the driver + * @pdev: Structure containing details of the current device. + * + * Always returns 0. + **/ +static int previewer_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver omap_previewer_driver = { + .probe = previewer_probe, + .remove = previewer_remove, + .driver = { + .owner = THIS_MODULE, + .name = OMAP_PREV_NAME, + }, +}; + +/** + * omap_previewer_init - Initialization of Preview Wrapper + * + * Returns 0 if successful, -ENOMEM if could not allocate memory, -ENODEV if + * could not register the wrapper as a character device, or other errors if the + * device or driver can't register. + **/ +static int __init omap_previewer_init(void) +{ + int ret; + struct prev_device *device; + + device = kzalloc(sizeof(struct prev_device), GFP_KERNEL); + if (!device) { + dev_err(prev_dev, OMAP_PREV_NAME ": could not allocate" + " memory\n"); + return -ENOMEM; + } + prev_major = register_chrdev(0, OMAP_PREV_NAME, &prev_fops); + + if (prev_major < 0) { + dev_err(prev_dev, OMAP_PREV_NAME ": initialization " + "failed. could not register character " + "device\n"); + return -ENODEV; + } + + ret = platform_driver_register(&omap_previewer_driver); + if (ret) { + dev_err(prev_dev, OMAP_PREV_NAME + ": failed to register platform driver!\n"); + goto fail2; + } + ret = platform_device_register(&omap_previewer_device); + if (ret) { + dev_err(prev_dev, OMAP_PREV_NAME + ": failed to register platform device!\n"); + goto fail3; + } + + prev_class = class_create(THIS_MODULE, OMAP_PREV_NAME); + if (!prev_class) + goto fail4; + + prev_dev = device_create(prev_class, prev_dev, + MKDEV(prev_major, 0), NULL, + OMAP_PREV_NAME); + dev_dbg(prev_dev, OMAP_PREV_NAME ": Registered Previewer Wrapper\n"); + device->opened = 0; + + device->vbq_ops.buf_setup = previewer_vbq_setup; + device->vbq_ops.buf_prepare = previewer_vbq_prepare; + device->vbq_ops.buf_release = previewer_vbq_release; + device->vbq_ops.buf_queue = previewer_vbq_queue; + spin_lock_init(&device->inout_vbq_lock); + spin_lock_init(&device->lsc_vbq_lock); + prevdevice = device; + return 0; + +fail4: + platform_device_unregister(&omap_previewer_device); +fail3: + platform_driver_unregister(&omap_previewer_driver); +fail2: + unregister_chrdev(prev_major, OMAP_PREV_NAME); + + return ret; +} + +/** + * omap_previewer_exit - Close of Preview Wrapper + **/ +static void __exit omap_previewer_exit(void) +{ + device_destroy(prev_class, MKDEV(prev_major, 0)); + class_destroy(prev_class); + platform_device_unregister(&omap_previewer_device); + platform_driver_unregister(&omap_previewer_driver); + unregister_chrdev(prev_major, OMAP_PREV_NAME); + + kfree(prevdevice); + prev_major = -1; +} + +module_init(omap_previewer_init); +module_exit(omap_previewer_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP ISP Previewer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/isp/omap_previewer.h b/drivers/media/video/isp/omap_previewer.h new file mode 100644 index 00000000000..179b570fe7c --- /dev/null +++ b/drivers/media/video/isp/omap_previewer.h @@ -0,0 +1,171 @@ +/* + * drivers/media/video/isp/omap_previewer.h + * + * Header file for Preview module wrapper in TI's OMAP3430 ISP + * + * Copyright (C) 2008 Texas Instruments, Inc. + * + * Contributors: + * Leonides Martinez + * Sergio Aguirre + * + * 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 "isppreview.h" + +#ifndef OMAP_ISP_PREVIEW_WRAP_H +#define OMAP_ISP_PREVIEW_WRAP_H + +#define PREV_IOC_BASE 'P' +#define PREV_REQBUF _IOWR(PREV_IOC_BASE, 1,\ + struct v4l2_requestbuffers) +#define PREV_QUERYBUF _IOWR(PREV_IOC_BASE, 2,\ + struct v4l2_buffer) +#define PREV_SET_PARAM _IOW(PREV_IOC_BASE, 3,\ + struct prev_params) +#define PREV_GET_PARAM _IOWR(PREV_IOC_BASE, 4,\ + struct prev_params) +#define PREV_PREVIEW _IOR(PREV_IOC_BASE, 5, int) +#define PREV_GET_STATUS _IOR(PREV_IOC_BASE, 6, char) +#define PREV_GET_CROPSIZE _IOR(PREV_IOC_BASE, 7,\ + struct prev_cropsize) +#define PREV_QUEUEBUF _IOWR(PREV_IOC_BASE, 8,\ + struct v4l2_buffer) +#define PREV_IOC_MAXNR 8 + +#define LUMA_TABLE_SIZE 128 +#define GAMMA_TABLE_SIZE 1024 +#define CFA_COEFF_TABLE_SIZE 576 +#define NOISE_FILTER_TABLE_SIZE 256 + +#define MAX_IMAGE_WIDTH 3300 + +#define PREV_INWIDTH_8BIT 0 /* pixel width of 8 bits */ +#define PREV_INWIDTH_10BIT 1 /* pixel width of 10 bits */ + +#define PREV_32BYTES_ALIGN_MASK 0xFFFFFFE0 +#define PREV_16PIX_ALIGN_MASK 0xFFFFFFF0 + +/** + * struct prev_rgbblending - Structure for RGB2RGB blending parameters + * @blending: Color correlation 3x3 matrix. + * @offset: Color correlation offsets. + */ +struct prev_rgbblending { + short blending[RGB_MAX][RGB_MAX]; /* color correlation 3x3 + * matrix. + */ + short offset[RGB_MAX]; /* color correlation offsets */ +}; + +/** + * struct prev_cfa_coeffs - Structure for CFA coefficients + * @hthreshold: Horizontal threshold. + * @vthreshold: Vertical threshold. + * @coeffs: CFA coefficients + */ +struct prev_cfa_coeffs { + char hthreshold, vthreshold; + int coeffs[CFA_COEFF_TABLE_SIZE]; +}; + +/** + * struct prev_gamma_coeffs - Structure for Gamma Coefficients + * @red: Table of gamma correction values for red color. + * @green: Table of gamma correction values for green color. + * @blue: Table of gamma correction values for blue color. + */ +struct prev_gamma_coeffs { + unsigned char red[GAMMA_TABLE_SIZE]; + unsigned char green[GAMMA_TABLE_SIZE]; + unsigned char blue[GAMMA_TABLE_SIZE]; +}; + +/** + * struct prev_noiseflt_coeffs - Structure for Noise Filter Coefficients. + * @noise: Noise filter table. + * @strength: Used to find out weighted average. + */ +struct prev_noiseflt_coeffs { + unsigned char noise[NOISE_FILTER_TABLE_SIZE]; + unsigned char strength; +}; + +/** + * struct prev_chroma_spr - Structure for Chroma Suppression. + * @hpfy: High passed version of Y or normal Y. + * @threshold: Threshold for chroma suppress. + * @gain: Chroma suppression gain + */ +struct prev_chroma_spr { + unsigned char hpfy; + char threshold; + unsigned char gain; +}; + +/** + * struct prev_status - Structure to know status of the hardware + * @hw_busy: Flag to indicate if Hardware is Busy. + */ +struct prev_status { + char hw_busy; +}; + +/** + * struct prev_cropsize - Structure to know crop size. + * @hcrop: Horizontal size of crop window. + * @vcrop: Vertical size of crop window. + */ +struct prev_cropsize { + int hcrop; + int vcrop; +}; + +/** + * struct prev_device - Global device information structure. + * @params: Pointer to structure containing preview parameters. + * @opened: State of the device. + * @wfc: Wait for completion. Used for locking operations. + * @prevwrap_mutex: Mutex for preview wrapper use. + * @inout_vbq_lock: Spinlock for in/out videobuf queues. + * @lsc_vbq_lock: Spinlock for LSC videobuf queues. + * @vbq_ops: Videobuf queue operations + * @isp_addr_read: Input/Output address + * @isp_addr_read: LSC address + */ +struct prev_device { + unsigned char opened; + struct completion wfc; + struct mutex prevwrap_mutex; + spinlock_t inout_vbq_lock; /* Spinlock for in/out videobuf queues. */ + spinlock_t lsc_vbq_lock; /* Spinlock for LSC videobuf queues. */ + struct videobuf_queue_ops vbq_ops; + dma_addr_t isp_addr_read; + dma_addr_t isp_addr_lsc; + struct device *isp; + struct prev_size_params size_params; +}; + +/** + * struct prev_fh - Per-filehandle data structure + * @inout_type: Used buffer type for I/O. + * @inout_vbq: I/O Videobuffer queue. + * @lsc_type: Used buffer type for LSC. + * @lsc_vbq: LSC Videobuffer queue. + * @device: Pointer to device information structure. + */ +struct prev_fh { + enum v4l2_buf_type inout_type; + struct videobuf_queue inout_vbq; + enum v4l2_buf_type lsc_type; + struct videobuf_queue lsc_vbq; + struct prev_device *device; +}; +#endif diff --git a/drivers/media/video/isp/omap_resizer.c b/drivers/media/video/isp/omap_resizer.c new file mode 100644 index 00000000000..ed0aa6c8167 --- /dev/null +++ b/drivers/media/video/isp/omap_resizer.c @@ -0,0 +1,1886 @@ +/* + * drivers/media/video/isp/omap_resizer.c + * + * Wrapper for Resizer module in TI's OMAP3430 ISP + * + * Copyright (C) 2008 Texas Instruments, Inc. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "isp.h" +#include "ispreg.h" +#include "ispresizer.h" +#include + +#define OMAP_REZR_NAME "omap-resizer" + +/* Defines and Constants*/ +#define MAX_CHANNELS 16 +#define MAX_IMAGE_WIDTH 2047 +#define MAX_IMAGE_WIDTH_HIGH 2047 +#define ALIGNMENT 16 +#define CHANNEL_BUSY 1 +#define CHANNEL_FREE 0 +#define PIXEL_EVEN 2 +#define RATIO_MULTIPLIER 256 +/* Bit position Macro */ +/* macro for bit set and clear */ +#define BITSET(variable, bit) ((variable) | (1 << bit)) +#define BITRESET(variable, bit) ((variable) & ~(0x00000001 << (bit))) +#define SET_BIT_INPUTRAM 28 +#define SET_BIT_CBLIN 29 +#define SET_BIT_INPTYP 27 +#define SET_BIT_YCPOS 26 +#define INPUT_RAM 1 +#define UP_RSZ_RATIO 64 +#define DOWN_RSZ_RATIO 512 +#define UP_RSZ_RATIO1 513 +#define DOWN_RSZ_RATIO1 1024 +#define RSZ_IN_SIZE_VERT_SHIFT 16 +#define MAX_HORZ_PIXEL_8BIT 31 +#define MAX_HORZ_PIXEL_16BIT 15 +#define NUM_PHASES 8 +#define NUM_TAPS 4 +#define NUM_D2PH 4 /* for downsampling * 2+x ~ 4x, + * number of phases + */ +#define NUM_D2TAPS 7 /* for downsampling * 2+x ~ 4x, + * number of taps + */ +#define ALIGN32 32 +#define MAX_COEF_COUNTER 16 +#define COEFF_ADDRESS_OFFSET 0x04 + +#define RSZ_DEF_REQ_EXP 0x0 /* Default read operation expand + * for the Resizer driver + */ +/* Global structure which contains information about number of channels + and protection variables */ +struct device_params { + + unsigned char opened; /* state of the device */ + struct completion compl_isr; /* Completion for interrupt */ + struct mutex reszwrap_mutex; /* Semaphore for array */ + + struct videobuf_queue_ops vbq_ops; /* videobuf queue operations */ + unsigned long extra_page_addr; +}; + +/* Register mapped structure which contains the every register + information */ +struct resizer_config { + u32 rsz_pcr; /* pcr register mapping + * variable. + */ + u32 rsz_in_start; /* in_start register mapping + * variable. + */ + u32 rsz_in_size; /* in_size register mapping + * variable. + */ + u32 rsz_out_size; /* out_size register mapping + * variable. + */ + u32 rsz_cnt; /* rsz_cnt register mapping + * variable. + */ + u32 rsz_sdr_inadd; /* sdr_inadd register mapping + * variable. + */ + u32 rsz_sdr_inoff; /* sdr_inoff register mapping + * variable. + */ + u32 rsz_sdr_outadd; /* sdr_outadd register mapping + * variable. + */ + u32 rsz_sdr_outoff; /* sdr_outbuff register + * mapping variable. + */ + u32 rsz_coeff_horz[16]; /* horizontal coefficients + * mapping array. + */ + u32 rsz_coeff_vert[16]; /* vertical coefficients + * mapping array. + */ + u32 rsz_yehn; /* yehn(luma)register mapping + * variable. + */ + u32 sdr_req_exp; /* Configuration for Read + * cycle Expand + */ +}; + +struct rsz_mult { + int in_hsize; /* input frame horizontal + * size. + */ + int in_vsize; /* input frame vertical size. + */ + int out_hsize; /* output frame horizontal + * size. + */ + int out_vsize; /* output frame vertical + * size. + */ + int in_pitch; /* offset between two rows of + * input frame. + */ + int out_pitch; /* offset between two rows of + * output frame. + */ + int end_hsize; + int end_vsize; + int num_htap; /* 0 = 7tap; 1 = 4tap */ + int num_vtap; /* 0 = 7tap; 1 = 4tap */ + int active; + int inptyp; + int vrsz; + int hrsz; + int hstph; /* for specifying horizontal + * starting phase. + */ + int vstph; + int pix_fmt; /* # defined, UYVY or YUYV. */ + int cbilin; /* # defined, filter with luma + * or bi-linear. + */ + u16 tap4filt_coeffs[32]; /* horizontal filter + * coefficients. + */ + u16 tap7filt_coeffs[32]; /* vertical filter + * coefficients. + */ +}; +/* Channel specific structure contains information regarding + the every channel */ +struct channel_config { + struct resizer_config register_config; /* Instance of register set + * mapping structure + */ + int status; /* Specifies whether the + * channel is busy or not + */ + struct mutex chanprotection_mutex; + int buf_address[VIDEO_MAX_FRAME]; + enum config_done config_state; + u8 input_buf_index; + u8 output_buf_index; + +}; + +/* per-filehandle data structure */ +struct rsz_fh { + struct rsz_params *params; + struct channel_config *config; + struct rsz_mult *multipass; /* Multipass to support + * resizing ration outside + * of 0.25x to 4x + */ + spinlock_t vbq_lock; /* spinlock for videobuf + * queues. + */ + enum v4l2_buf_type type; + struct videobuf_queue vbq; + struct device_params *device; + + u32 rsz_bufsize; /* channel specific buffersize + */ + struct device *dev; /* + * Device structure contains + * whole ISP related data + */ +}; + +static struct device_params *device_config; +static struct device *rsz_device; +static bool is_vm_io; +static int rsz_major = -1; +/* functions declaration */ +static void rsz_hardware_setup(struct channel_config *rsz_conf_chan); +static int rsz_set_params(struct rsz_mult *multipass, struct rsz_params *, + struct channel_config *); +static int rsz_get_params(struct rsz_params *, struct channel_config *); +static void rsz_copy_data(struct rsz_mult *multipass, + struct rsz_params *params); +static void rsz_isr(unsigned long status, isp_vbq_callback_ptr arg1, + void *arg2); +static void rsz_calculate_crop(struct channel_config *rsz_conf_chan, + struct rsz_cropsize *cropsize); +static int rsz_set_multipass(struct rsz_mult *multipass, + struct channel_config *rsz_conf_chan); +static int rsz_set_ratio(struct rsz_mult *multipass, + struct channel_config *rsz_conf_chan); +static void rsz_config_ratio(struct rsz_mult *multipass, + struct channel_config *rsz_conf_chan); + +static inline void rsz_set_exp(unsigned int exp) +{ + omap_writel(((exp & 0x3FF) << 10), OMAP3ISP_SBL_REG(0xF8)); +} +/** + * rsz_hardware_setup - Sets hardware configuration registers + * @rsz_conf_chan: Structure containing channel configuration + * + * Set hardware configuration registers + **/ +static void rsz_hardware_setup(struct channel_config *rsz_conf_chan) +{ + int coeffcounter; + int coeffoffset = 0; + + omap_writel(rsz_conf_chan->register_config.rsz_cnt, + OMAP3ISP_RESZ_REG(ISPRSZ_CNT)); + + omap_writel(rsz_conf_chan->register_config.rsz_in_start, + OMAP3ISP_RESZ_REG(ISPRSZ_IN_START)); + omap_writel(rsz_conf_chan->register_config.rsz_in_size, + OMAP3ISP_RESZ_REG(ISPRSZ_IN_SIZE)); + + omap_writel(rsz_conf_chan->register_config.rsz_out_size, + OMAP3ISP_RESZ_REG(ISPRSZ_OUT_SIZE)); + omap_writel(rsz_conf_chan->register_config.rsz_sdr_inadd, + OMAP3ISP_RESZ_REG(ISPRSZ_SDR_INADD)); + omap_writel(rsz_conf_chan->register_config.rsz_sdr_inoff, + OMAP3ISP_RESZ_REG(ISPRSZ_SDR_INOFF)); + omap_writel(rsz_conf_chan->register_config.rsz_sdr_outadd, + OMAP3ISP_RESZ_REG(ISPRSZ_SDR_OUTADD)); + omap_writel(rsz_conf_chan->register_config.rsz_sdr_outoff, + OMAP3ISP_RESZ_REG(ISPRSZ_SDR_OUTOFF)); + omap_writel(rsz_conf_chan->register_config.rsz_yehn, + OMAP3ISP_RESZ_REG(ISPRSZ_YENH)); + + for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER; + coeffcounter++) { + omap_writel(rsz_conf_chan->register_config. + rsz_coeff_horz[coeffcounter], + OMAP3ISP_RESZ_REG(ISPRSZ_HFILT10 + + coeffoffset)); + + omap_writel(rsz_conf_chan->register_config. + rsz_coeff_vert[coeffcounter], + OMAP3ISP_RESZ_REG(ISPRSZ_VFILT10 + + coeffoffset)); + coeffoffset = coeffoffset + COEFF_ADDRESS_OFFSET; + } + /* Configure the read expand register */ + rsz_set_exp(rsz_conf_chan->register_config.sdr_req_exp); +} + +/** + * rsz_start - Enables Resizer Wrapper + * @arg: Currently not used. + * @fh: File structure containing ISP resizer information specific to + * channel opened. + * + * Submits a resizing task specified by the rsz_resize structure. The call can + * either be blocked until the task is completed or returned immediately based + * on the value of the blocking argument in the rsz_resize structure. If it is + * blocking, the status of the task can be checked by calling ioctl + * RSZ_G_STATUS. Only one task can be outstanding for each logical channel. + * + * Returns 0 if successful, or -EINVAL if could not set callback for RSZR IRQ + * event or the state of the channel is not configured. + **/ +int rsz_start(int *arg, struct rsz_fh *fh) +{ + struct channel_config *rsz_conf_chan = fh->config; + struct rsz_mult *multipass = fh->multipass; + struct videobuf_queue *q = &fh->vbq; + struct videobuf_buffer *buf; + struct isp_device *isp = dev_get_drvdata(fh->dev); + int ret; + + if (rsz_conf_chan->config_state) { + dev_err(rsz_device, "State not configured \n"); + goto err_einval; + } + if (!rsz_conf_chan->register_config.rsz_sdr_inadd || + !rsz_conf_chan->register_config.rsz_sdr_outadd) { + dev_err(rsz_device, "address is null\n"); + goto err_einval; + } + + rsz_conf_chan->status = CHANNEL_BUSY; + + rsz_hardware_setup(rsz_conf_chan); + + if (isp_set_callback(fh->dev, CBK_RESZ_DONE, rsz_isr, (void *) NULL, + (void *)NULL)) { + dev_err(rsz_device, "No callback for RSZR\n"); + goto err_einval; + } +mult: + device_config->compl_isr.done = 0; + + ispresizer_enable(&isp->isp_res, 1); + + ret = wait_for_completion_interruptible(&device_config->compl_isr); + if (ret != 0) { + dev_dbg(rsz_device, "Unexpected exit from " + "wait_for_completion_interruptible\n"); + wait_for_completion(&device_config->compl_isr); + } + + if (multipass->active) { + rsz_set_multipass(multipass, rsz_conf_chan); + goto mult; + } + + rsz_conf_chan->status = CHANNEL_FREE; + rsz_conf_chan->register_config.rsz_sdr_outadd = 0; + rsz_conf_chan->register_config.rsz_sdr_inadd = 0; + + isp_unset_callback(fh->dev, CBK_RESZ_DONE); + + /* Empty the Videobuf queue which was filled during the qbuf */ + buf = q->bufs[rsz_conf_chan->input_buf_index]; + buf->state = VIDEOBUF_IDLE; + list_del(&buf->stream); + if (rsz_conf_chan->input_buf_index != rsz_conf_chan->output_buf_index) { + buf = q->bufs[rsz_conf_chan->output_buf_index]; + buf->state = VIDEOBUF_IDLE; + list_del(&buf->stream); + } + + return 0; +err_einval: + return -EINVAL; +} + +/** + * rsz_set_multipass - Set resizer multipass + * @multipass: Structure containing channel configuration + for multipass support + * @rsz_conf_chan: Structure containing channel configuration + * + * Returns always 0 + **/ +static int rsz_set_multipass(struct rsz_mult *multipass, + struct channel_config *rsz_conf_chan) +{ + multipass->in_hsize = multipass->out_hsize; + multipass->in_vsize = multipass->out_vsize; + multipass->out_hsize = multipass->end_hsize; + multipass->out_vsize = multipass->end_vsize; + + multipass->out_pitch = (multipass->inptyp ? multipass->out_hsize + : (multipass->out_hsize * 2)); + multipass->in_pitch = (multipass->inptyp ? multipass->in_hsize + : (multipass->in_hsize * 2)); + + rsz_set_ratio(multipass, rsz_conf_chan); + rsz_config_ratio(multipass, rsz_conf_chan); + rsz_hardware_setup(rsz_conf_chan); + return 0; +} + +/** + * rsz_copy_data - Copy data + * @multipass: Structure containing channel configuration + for multipass support + * @params: Structure containing the Resizer Wrapper parameters + * + * Copy data + **/ +static void rsz_copy_data(struct rsz_mult *multipass, struct rsz_params *params) +{ + int i; + multipass->in_hsize = params->in_hsize; + multipass->in_vsize = params->in_vsize; + multipass->out_hsize = params->out_hsize; + multipass->out_vsize = params->out_vsize; + multipass->end_hsize = params->out_hsize; + multipass->end_vsize = params->out_vsize; + multipass->in_pitch = params->in_pitch; + multipass->out_pitch = params->out_pitch; + multipass->hstph = params->hstph; + multipass->vstph = params->vstph; + multipass->inptyp = params->inptyp; + multipass->pix_fmt = params->pix_fmt; + multipass->cbilin = params->cbilin; + + for (i = 0; i < 32; i++) { + multipass->tap4filt_coeffs[i] = params->tap4filt_coeffs[i]; + multipass->tap7filt_coeffs[i] = params->tap7filt_coeffs[i]; + } +} + +/** + * rsz_set_params - Set parameters for resizer wrapper + * @multipass: Structure containing channel configuration + for multipass support + * @params: Structure containing the Resizer Wrapper parameters + * @rsz_conf_chan: Structure containing channel configuration + * + * Used to set the parameters of the Resizer hardware, including input and + * output image size, horizontal and vertical poly-phase filter coefficients, + * luma enchancement filter coefficients, etc. + **/ +static int rsz_set_params(struct rsz_mult *multipass, struct rsz_params *params, + struct channel_config *rsz_conf_chan) +{ + int mul = 1; + if ((params->yenh_params.type < 0) || (params->yenh_params.type > 2)) { + dev_err(rsz_device, "rsz_set_params: Wrong yenh type\n"); + return -EINVAL; + } + if ((params->in_vsize <= 0) || (params->in_hsize <= 0) || + (params->out_vsize <= 0) || (params->out_hsize <= 0) || + (params->in_pitch <= 0) || (params->out_pitch <= 0)) { + dev_err(rsz_device, "rsz_set_params: Invalid size params\n"); + return -EINVAL; + } + if ((params->inptyp != RSZ_INTYPE_YCBCR422_16BIT) && + (params->inptyp != RSZ_INTYPE_PLANAR_8BIT)) { + dev_err(rsz_device, "rsz_set_params: Invalid input type\n"); + return -EINVAL; + } + if ((params->pix_fmt != RSZ_PIX_FMT_UYVY) && + (params->pix_fmt != RSZ_PIX_FMT_YUYV)) { + dev_err(rsz_device, "rsz_set_params: Invalid pixel format\n"); + return -EINVAL; + } + if (params->inptyp == RSZ_INTYPE_YCBCR422_16BIT) + mul = 2; + else + mul = 1; + if (params->in_pitch < (params->in_hsize * mul)) { + dev_err(rsz_device, "rsz_set_params: Pitch is incorrect\n"); + return -EINVAL; + } + if (params->out_pitch < (params->out_hsize * mul)) { + dev_err(rsz_device, "rsz_set_params: Out pitch cannot be less" + " than out hsize\n"); + return -EINVAL; + } + /* Output H size should be even */ + if ((params->out_hsize % PIXEL_EVEN) != 0) { + dev_err(rsz_device, "rsz_set_params: Output H size should" + " be even\n"); + return -EINVAL; + } + if (params->horz_starting_pixel < 0) { + dev_err(rsz_device, "rsz_set_params: Horz start pixel cannot" + " be less than zero\n"); + return -EINVAL; + } + + rsz_copy_data(multipass, params); + if (0 != rsz_set_ratio(multipass, rsz_conf_chan)) + goto err_einval; + + if (params->yenh_params.type) { + if ((multipass->num_htap && multipass->out_hsize > + 1280) || + (!multipass->num_htap && multipass->out_hsize > + 640)) + goto err_einval; + } + + if (INPUT_RAM) + params->vert_starting_pixel = 0; + + rsz_conf_chan->register_config.rsz_in_start = + (params->vert_starting_pixel + << ISPRSZ_IN_SIZE_VERT_SHIFT) + & ISPRSZ_IN_SIZE_VERT_MASK; + + if (params->inptyp == RSZ_INTYPE_PLANAR_8BIT) { + if (params->horz_starting_pixel > MAX_HORZ_PIXEL_8BIT) + goto err_einval; + } + if (params->inptyp == RSZ_INTYPE_YCBCR422_16BIT) { + if (params->horz_starting_pixel > MAX_HORZ_PIXEL_16BIT) + goto err_einval; + } + + rsz_conf_chan->register_config.rsz_in_start |= + params->horz_starting_pixel + & ISPRSZ_IN_START_HORZ_ST_MASK; + + rsz_conf_chan->register_config.rsz_yehn = + (params->yenh_params.type + << ISPRSZ_YENH_ALGO_SHIFT) + & ISPRSZ_YENH_ALGO_MASK; + + if (params->yenh_params.type) { + rsz_conf_chan->register_config.rsz_yehn |= + params->yenh_params.core + & ISPRSZ_YENH_CORE_MASK; + + rsz_conf_chan->register_config.rsz_yehn |= + (params->yenh_params.gain + << ISPRSZ_YENH_GAIN_SHIFT) + & ISPRSZ_YENH_GAIN_MASK; + + rsz_conf_chan->register_config.rsz_yehn |= + (params->yenh_params.slop + << ISPRSZ_YENH_SLOP_SHIFT) + & ISPRSZ_YENH_SLOP_MASK; + } + + rsz_config_ratio(multipass, rsz_conf_chan); + /* Default value for read expand:Taken from Davinci */ + rsz_conf_chan->register_config.sdr_req_exp = RSZ_DEF_REQ_EXP; + + rsz_conf_chan->config_state = STATE_CONFIGURED; + + return 0; +err_einval: + return -EINVAL; +} + +/** + * rsz_set_ratio - Set ratio + * @multipass: Structure containing channel configuration + for multipass support + * @rsz_conf_chan: Structure containing channel configuration + * + * Returns 0 if successful, -EINVAL if invalid output size, upscaling ratio is + * being requested, or other ratio configuration value is out of bounds + **/ +static int rsz_set_ratio(struct rsz_mult *multipass, + struct channel_config *rsz_conf_chan) +{ + int alignment = 0; + + rsz_conf_chan->register_config.rsz_cnt = 0; + + if ((multipass->out_hsize > MAX_IMAGE_WIDTH) || + (multipass->out_vsize > MAX_IMAGE_WIDTH)) { + dev_err(rsz_device, "Invalid output size! - %d", \ + multipass->out_hsize); + goto err_einval; + } + if (multipass->cbilin) { + rsz_conf_chan->register_config.rsz_cnt = + BITSET(rsz_conf_chan->register_config.rsz_cnt, + SET_BIT_CBLIN); + } + if (INPUT_RAM) { + rsz_conf_chan->register_config.rsz_cnt = + BITSET(rsz_conf_chan->register_config.rsz_cnt, + SET_BIT_INPUTRAM); + } + if (multipass->inptyp == RSZ_INTYPE_PLANAR_8BIT) { + rsz_conf_chan->register_config.rsz_cnt = + BITSET(rsz_conf_chan->register_config.rsz_cnt, + SET_BIT_INPTYP); + } else { + rsz_conf_chan->register_config.rsz_cnt = + BITRESET(rsz_conf_chan->register_config. + rsz_cnt, SET_BIT_INPTYP); + + if (multipass->pix_fmt == RSZ_PIX_FMT_UYVY) { + rsz_conf_chan->register_config.rsz_cnt = + BITRESET(rsz_conf_chan->register_config. + rsz_cnt, SET_BIT_YCPOS); + } else if (multipass->pix_fmt == RSZ_PIX_FMT_YUYV) { + rsz_conf_chan->register_config.rsz_cnt = + BITSET(rsz_conf_chan->register_config. + rsz_cnt, SET_BIT_YCPOS); + } + + } + multipass->vrsz = + (multipass->in_vsize * RATIO_MULTIPLIER) / multipass->out_vsize; + multipass->hrsz = + (multipass->in_hsize * RATIO_MULTIPLIER) / multipass->out_hsize; + if (UP_RSZ_RATIO > multipass->vrsz || UP_RSZ_RATIO > multipass->hrsz) { + dev_err(rsz_device, "Upscaling ratio not supported!"); + goto err_einval; + } + multipass->vrsz = (multipass->in_vsize - NUM_D2TAPS) * RATIO_MULTIPLIER + / (multipass->out_vsize - 1); + multipass->hrsz = ((multipass->in_hsize - NUM_D2TAPS) + * RATIO_MULTIPLIER) / + (multipass->out_hsize - 1); + + if (multipass->hrsz <= 512) { + multipass->hrsz = (multipass->in_hsize - NUM_TAPS) + * RATIO_MULTIPLIER + / (multipass->out_hsize - 1); + if (multipass->hrsz < 64) + multipass->hrsz = 64; + if (multipass->hrsz > 512) + multipass->hrsz = 512; + if (multipass->hstph > NUM_PHASES) + goto err_einval; + multipass->num_htap = 1; + } else if (multipass->hrsz >= 513 && multipass->hrsz <= 1024) { + if (multipass->hstph > NUM_D2PH) + goto err_einval; + multipass->num_htap = 0; + } + + if (multipass->vrsz <= 512) { + multipass->vrsz = (multipass->in_vsize - NUM_TAPS) + * RATIO_MULTIPLIER + / (multipass->out_vsize - 1); + if (multipass->vrsz < 64) + multipass->vrsz = 64; + if (multipass->vrsz > 512) + multipass->vrsz = 512; + if (multipass->vstph > NUM_PHASES) + goto err_einval; + multipass->num_vtap = 1; + } else if (multipass->vrsz >= 513 && multipass->vrsz <= 1024) { + if (multipass->vstph > NUM_D2PH) + goto err_einval; + multipass->num_vtap = 0; + } + + if ((multipass->in_pitch) % ALIGN32) { + dev_err(rsz_device, "Invalid input pitch: %d \n", + multipass->in_pitch); + goto err_einval; + } + if ((multipass->out_pitch) % ALIGN32) { + dev_err(rsz_device, "Invalid output pitch %d \n", + multipass->out_pitch); + goto err_einval; + } + + if (multipass->vrsz < 256 && + (multipass->in_vsize < multipass->out_vsize)) { + if (multipass->inptyp == RSZ_INTYPE_PLANAR_8BIT) + alignment = ALIGNMENT; + else if (multipass->inptyp == RSZ_INTYPE_YCBCR422_16BIT) + alignment = (ALIGNMENT / 2); + else + dev_err(rsz_device, "Invalid input type\n"); + + if (!(((multipass->out_hsize % PIXEL_EVEN) == 0) + && (multipass->out_hsize % alignment) == 0)) { + dev_err(rsz_device, "wrong hsize\n"); + goto err_einval; + } + } + if (multipass->hrsz >= 64 && multipass->hrsz <= 1024) { + if (multipass->out_hsize > MAX_IMAGE_WIDTH) { + dev_err(rsz_device, "wrong width\n"); + goto err_einval; + } + multipass->active = 0; + + } else if (multipass->hrsz > 1024) { + if (multipass->out_hsize > MAX_IMAGE_WIDTH) { + dev_err(rsz_device, "wrong width\n"); + goto err_einval; + } + if (multipass->hstph > NUM_D2PH) + goto err_einval; + multipass->num_htap = 0; + multipass->out_hsize = multipass->in_hsize * 256 / 1024; + if (multipass->out_hsize % ALIGN32) { + multipass->out_hsize += + abs((multipass->out_hsize % ALIGN32) - ALIGN32); + } + multipass->out_pitch = ((multipass->inptyp) ? + multipass->out_hsize : + (multipass->out_hsize * 2)); + multipass->hrsz = ((multipass->in_hsize - NUM_D2TAPS) + * RATIO_MULTIPLIER) + / (multipass->out_hsize - 1); + multipass->active = 1; + + } + + if (multipass->vrsz > 1024) { + if (multipass->out_vsize > MAX_IMAGE_WIDTH_HIGH) { + dev_err(rsz_device, "wrong width\n"); + goto err_einval; + } + + multipass->out_vsize = multipass->in_vsize * 256 / 1024; + multipass->vrsz = ((multipass->in_vsize - NUM_D2TAPS) + * RATIO_MULTIPLIER) + / (multipass->out_vsize - 1); + multipass->active = 1; + multipass->num_vtap = 0; + + } + rsz_conf_chan->register_config.rsz_out_size = + multipass->out_hsize + & ISPRSZ_OUT_SIZE_HORZ_MASK; + + rsz_conf_chan->register_config.rsz_out_size |= + (multipass->out_vsize + << ISPRSZ_OUT_SIZE_VERT_SHIFT) + & ISPRSZ_OUT_SIZE_VERT_MASK; + + rsz_conf_chan->register_config.rsz_sdr_inoff = + multipass->in_pitch + & ISPRSZ_SDR_INOFF_OFFSET_MASK; + + rsz_conf_chan->register_config.rsz_sdr_outoff = + multipass->out_pitch + & ISPRSZ_SDR_OUTOFF_OFFSET_MASK; + + if (multipass->hrsz >= 64 && multipass->hrsz <= 512) { + if (multipass->hstph > NUM_PHASES) + goto err_einval; + } else if (multipass->hrsz >= 64 && multipass->hrsz <= 512) { + if (multipass->hstph > NUM_D2PH) + goto err_einval; + } + + rsz_conf_chan->register_config.rsz_cnt |= + (multipass->hstph + << ISPRSZ_CNT_HSTPH_SHIFT) + & ISPRSZ_CNT_HSTPH_MASK; + + if (multipass->vrsz >= 64 && multipass->hrsz <= 512) { + if (multipass->vstph > NUM_PHASES) + goto err_einval; + } else if (multipass->vrsz >= 64 && multipass->vrsz <= 512) { + if (multipass->vstph > NUM_D2PH) + goto err_einval; + } + + rsz_conf_chan->register_config.rsz_cnt |= + (multipass->vstph + << ISPRSZ_CNT_VSTPH_SHIFT) + & ISPRSZ_CNT_VSTPH_MASK; + + rsz_conf_chan->register_config.rsz_cnt |= + (multipass->hrsz - 1) + & ISPRSZ_CNT_HRSZ_MASK; + + rsz_conf_chan->register_config.rsz_cnt |= + ((multipass->vrsz - 1) + << ISPRSZ_CNT_VRSZ_SHIFT) + & ISPRSZ_CNT_VRSZ_MASK; + + return 0; +err_einval: + return -EINVAL; +} + +/** + * rsz_config_ratio - Configure ratio + * @multipass: Structure containing channel configuration + for multipass support + * @rsz_conf_chan: Structure containing channel configuration + * + * Configure ratio + **/ +static void rsz_config_ratio(struct rsz_mult *multipass, + struct channel_config *rsz_conf_chan) +{ + int hsize; + int vsize; + int coeffcounter; + + if (multipass->hrsz <= 512) { + hsize = ((32 * multipass->hstph + (multipass->out_hsize - 1) + * multipass->hrsz + 16) >> 8) + 7; + } else { + hsize = ((64 * multipass->hstph + (multipass->out_hsize - 1) + * multipass->hrsz + 32) >> 8) + 7; + } + if (multipass->vrsz <= 512) { + vsize = ((32 * multipass->vstph + (multipass->out_vsize - 1) + * multipass->vrsz + 16) >> 8) + 4; + } else { + vsize = ((64 * multipass->vstph + (multipass->out_vsize - 1) + * multipass->vrsz + 32) >> 8) + 7; + } + rsz_conf_chan->register_config.rsz_in_size = hsize; + + rsz_conf_chan->register_config.rsz_in_size |= + ((vsize << ISPRSZ_IN_SIZE_VERT_SHIFT) + & ISPRSZ_IN_SIZE_VERT_MASK); + + /* This is another workaround for the ISP-MMU translation fault. + For the parameters whose image size comes exactly to PAGE_SIZE + generates ISP-MMU translation fault. The root-cause is the equation + input width = (32*sph + (ow - 1)*hrsz + 16) >> 8 + 7 + = (64*sph + (ow - 1)*hrsz + 32) >> 8 + 7 + input height = (32*spv + (oh - 1)*vrsz + 16) >> 8 + 4 + = (64*spv + (oh - 1)*vrsz + 32) >> 8 + 7 + + we are adjusting the input width to suit for Resizer module, + application should use this configuration henceforth. + */ + multipass->in_hsize = hsize; + multipass->in_vsize = vsize; + + for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER; + coeffcounter++) { + if (multipass->num_htap) { + rsz_conf_chan->register_config. + rsz_coeff_horz[coeffcounter] = + (multipass->tap4filt_coeffs[2 + * coeffcounter] + & ISPRSZ_HFILT10_COEF0_MASK); + rsz_conf_chan->register_config. + rsz_coeff_horz[coeffcounter] |= + ((multipass->tap4filt_coeffs[2 + * coeffcounter + 1] + << ISPRSZ_HFILT10_COEF1_SHIFT) + & ISPRSZ_HFILT10_COEF1_MASK); + } else { + rsz_conf_chan->register_config. + rsz_coeff_horz[coeffcounter] = + (multipass->tap7filt_coeffs[2 + * coeffcounter] + & ISPRSZ_HFILT10_COEF0_MASK); + + rsz_conf_chan->register_config. + rsz_coeff_horz[coeffcounter] |= + ((multipass->tap7filt_coeffs[2 + * coeffcounter + 1] + << ISPRSZ_HFILT10_COEF1_SHIFT) + & ISPRSZ_HFILT10_COEF1_MASK); + } + + if (multipass->num_vtap) { + rsz_conf_chan->register_config. + rsz_coeff_vert[coeffcounter] = + (multipass->tap4filt_coeffs[2 + * coeffcounter] + & ISPRSZ_VFILT10_COEF0_MASK); + + rsz_conf_chan->register_config. + rsz_coeff_vert[coeffcounter] |= + ((multipass->tap4filt_coeffs[2 + * coeffcounter + 1] + << ISPRSZ_VFILT10_COEF1_SHIFT) & + ISPRSZ_VFILT10_COEF1_MASK); + } else { + rsz_conf_chan->register_config. + rsz_coeff_vert[coeffcounter] = + (multipass->tap7filt_coeffs[2 + * coeffcounter] + & ISPRSZ_VFILT10_COEF0_MASK); + rsz_conf_chan->register_config. + rsz_coeff_vert[coeffcounter] |= + ((multipass->tap7filt_coeffs[2 + * coeffcounter + 1] + << ISPRSZ_VFILT10_COEF1_SHIFT) + & ISPRSZ_VFILT10_COEF1_MASK); + } + } +} + +/** + * rsz_get_params - Gets the parameter values + * @params: Structure containing the Resizer Wrapper parameters + * @rsz_conf_chan: Structure containing channel configuration + * + * Used to get the Resizer hardware settings associated with the + * current logical channel represented by fd. + **/ +static int rsz_get_params(struct rsz_params *params, + struct channel_config *rsz_conf_chan) +{ + int coeffcounter; + + if (rsz_conf_chan->config_state) { + dev_err(rsz_device, "state not configured\n"); + return -EINVAL; + } + + params->in_hsize = rsz_conf_chan->register_config.rsz_in_size + & ISPRSZ_IN_SIZE_HORZ_MASK; + params->in_vsize = (rsz_conf_chan->register_config.rsz_in_size + & ISPRSZ_IN_SIZE_VERT_MASK) + >> ISPRSZ_IN_SIZE_VERT_SHIFT; + + params->in_pitch = rsz_conf_chan->register_config.rsz_sdr_inoff + & ISPRSZ_SDR_INOFF_OFFSET_MASK; + + params->out_hsize = rsz_conf_chan->register_config.rsz_out_size + & ISPRSZ_OUT_SIZE_HORZ_MASK; + + params->out_vsize = (rsz_conf_chan->register_config.rsz_out_size + & ISPRSZ_OUT_SIZE_VERT_MASK) + >> ISPRSZ_OUT_SIZE_VERT_SHIFT; + + params->out_pitch = rsz_conf_chan->register_config.rsz_sdr_outoff + & ISPRSZ_SDR_OUTOFF_OFFSET_MASK; + + params->cbilin = (rsz_conf_chan->register_config.rsz_cnt + & SET_BIT_CBLIN) >> SET_BIT_CBLIN; + + params->inptyp = (rsz_conf_chan->register_config.rsz_cnt + & ISPRSZ_CNT_INPTYP_MASK) + >> SET_BIT_INPTYP; + params->horz_starting_pixel = ((rsz_conf_chan->register_config. + rsz_in_start + & ISPRSZ_IN_START_HORZ_ST_MASK)); + params->vert_starting_pixel = ((rsz_conf_chan->register_config. + rsz_in_start + & ISPRSZ_IN_START_VERT_ST_MASK) + >> ISPRSZ_IN_START_VERT_ST_SHIFT); + + params->hstph = ((rsz_conf_chan->register_config.rsz_cnt + & ISPRSZ_CNT_HSTPH_MASK + >> ISPRSZ_CNT_HSTPH_SHIFT)); + params->vstph = ((rsz_conf_chan->register_config.rsz_cnt + & ISPRSZ_CNT_VSTPH_MASK + >> ISPRSZ_CNT_VSTPH_SHIFT)); + + for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER; + coeffcounter++) { + params->tap4filt_coeffs[2 * coeffcounter] = + rsz_conf_chan->register_config. + rsz_coeff_horz[coeffcounter] + & ISPRSZ_HFILT10_COEF0_MASK; + + params->tap4filt_coeffs[2 * coeffcounter + 1] = + (rsz_conf_chan->register_config. + rsz_coeff_horz[coeffcounter] + & ISPRSZ_HFILT10_COEF1_MASK) + >> ISPRSZ_HFILT10_COEF1_SHIFT; + + params->tap7filt_coeffs[2 * coeffcounter] = + rsz_conf_chan->register_config. + rsz_coeff_vert[coeffcounter] + & ISPRSZ_VFILT10_COEF0_MASK; + + params->tap7filt_coeffs[2 * coeffcounter + 1] = + (rsz_conf_chan->register_config. + rsz_coeff_vert[coeffcounter] + & ISPRSZ_VFILT10_COEF1_MASK) + >> ISPRSZ_VFILT10_COEF1_SHIFT; + + } + + params->yenh_params.type = (rsz_conf_chan->register_config.rsz_yehn + & ISPRSZ_YENH_ALGO_MASK) + >> ISPRSZ_YENH_ALGO_SHIFT; + + params->yenh_params.core = rsz_conf_chan->register_config.rsz_yehn + & ISPRSZ_YENH_CORE_MASK; + + params->yenh_params.gain = (rsz_conf_chan->register_config.rsz_yehn + & ISPRSZ_YENH_GAIN_MASK) + >> ISPRSZ_YENH_GAIN_SHIFT; + + params->yenh_params.slop = (rsz_conf_chan->register_config.rsz_yehn + & ISPRSZ_YENH_SLOP_MASK) + >> ISPRSZ_YENH_SLOP_SHIFT; + + params->pix_fmt = ((rsz_conf_chan->register_config.rsz_cnt + & ISPRSZ_CNT_PIXFMT_MASK) + >> SET_BIT_YCPOS); + + if (params->pix_fmt) + params->pix_fmt = RSZ_PIX_FMT_UYVY; + else + params->pix_fmt = RSZ_PIX_FMT_YUYV; + + return 0; +} + +/** + * rsz_calculate_crop - Calculate Crop values + * @rsz_conf_chan: Structure containing channel configuration + * @cropsize: Structure containing crop parameters + * + * Calculate Crop values + **/ +static void rsz_calculate_crop(struct channel_config *rsz_conf_chan, + struct rsz_cropsize *cropsize) +{ + int luma_enable; + + cropsize->hcrop = 0; + cropsize->vcrop = 0; + + luma_enable = (rsz_conf_chan->register_config.rsz_yehn + & ISPRSZ_YENH_ALGO_MASK) + >> ISPRSZ_YENH_ALGO_SHIFT; + + if (luma_enable) + cropsize->hcrop += 2; +} + +/** + * rsz_vbq_release - Videobuffer queue release + * @q: Structure containing the videobuffer queue file handle, and device + * structure which contains the actual configuration. + * @vb: Structure containing the videobuffer used for resizer processing. + **/ +static void rsz_vbq_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct rsz_fh *fh = q->priv_data; + struct videobuf_dmabuf *dma = NULL; + + dma = videobuf_to_dma(q->bufs[vb->i]); + videobuf_dma_unmap(q, dma); + videobuf_dma_free(dma); + ispmmu_vunmap(fh->dev, fh->config->buf_address[vb->i]); + fh->config->buf_address[vb->i] = 0; + + spin_lock(&fh->vbq_lock); + vb->state = VIDEOBUF_NEEDS_INIT; + spin_unlock(&fh->vbq_lock); + +} + +/** + * rsz_vbq_setup - Sets up the videobuffer size and validates count. + * @q: Structure containing the videobuffer queue file handle, and device + * structure which contains the actual configuration. + * @cnt: Number of buffers requested + * @size: Size in bytes of the buffer used for previewing + * + * Always returns 0. + **/ +static int rsz_vbq_setup(struct videobuf_queue *q, unsigned int *cnt, + unsigned int *size) +{ + struct rsz_fh *fh = q->priv_data; + struct rsz_mult *multipass = fh->multipass; + u32 insize, outsize; + + spin_lock(&fh->vbq_lock); + if (*cnt <= 0) + *cnt = VIDEO_MAX_FRAME; + + if (*cnt > VIDEO_MAX_FRAME) + *cnt = VIDEO_MAX_FRAME; + + outsize = multipass->out_pitch * multipass->out_vsize; + insize = multipass->in_pitch * multipass->in_vsize; + if (*cnt == 1 && (outsize > insize)) { + dev_err(rsz_device, "2 buffers are required for Upscaling " + "mode\n"); + goto err_einval; + } + if (!fh->params->in_hsize || !fh->params->in_vsize) { + dev_err(rsz_device, "Can't setup buffer size\n"); + goto err_einval; + } else { + if (outsize > insize) + *size = outsize; + else + *size = insize; + + fh->rsz_bufsize = *size; + } + spin_unlock(&fh->vbq_lock); + + return 0; +err_einval: + spin_unlock(&fh->vbq_lock); + return -EINVAL; +} +/* + * This function is work around for the videobuf_iolock API, + * for User memory allocated with ioremap (VM_IO flag) the API + * get_user_pages fails. + * + * To fulfill this requirement, we have completely ignored VM layer of + * Linux, and configuring the ISP MMU with physical address. + */ +static int omap_videobuf_dma_init_user(struct videobuf_buffer *vb, + unsigned long physp, unsigned long asize) +{ + struct videobuf_dmabuf *dma; + struct scatterlist *sglist; + unsigned long data, first, last; + int len, i = 0; + + dma = videobuf_to_dma(vb); + data = vb->baddr; + + first = (data & PAGE_MASK) >> PAGE_SHIFT; + last = ((data+asize-1) & PAGE_MASK) >> PAGE_SHIFT; + dma->offset = data & ~PAGE_MASK; + dma->nr_pages = last-first+1; + + dma->direction = PCI_DMA_FROMDEVICE; + /* + * Allocate array of sglen + 1, to add entry of extra page + * for input buffer. Driver always uses 0th buffer as input buffer. + */ + len = dma->nr_pages + (vb->i ? 0 : 1); + sglist = vmalloc(len * sizeof(*sglist)); + if (NULL == sglist) + return -ENOMEM; + + sglist[0].offset = 0; + sglist[0].length = PAGE_SIZE - dma->offset; + sglist[0].dma_address = (dma_addr_t)physp; + physp += sglist[0].length; + /* + * Iterate in a loop for the number of pages + */ + for (i = 1; i < (len - (vb->i ? 0 : 1)); i++) { + sglist[i].offset = 0; + sglist[i].length = PAGE_SIZE; + sglist[i].dma_address = (dma_addr_t)physp; + physp += PAGE_SIZE; + } + if (0 == vb->i) { + sglist[i].offset = 0; + sglist[i].length = PAGE_SIZE; + sglist[i].dma_address = + (dma_addr_t)device_config->extra_page_addr; + } + dma->sglist = sglist; + dma->sglen = len; + + return 0; + +} + +/* + * This function is workaround for the issue, where ISP-MMU generated + * translation fault for specific params whose size is aligned to PAGE_SIZE. + + * As a workaround we are padding one extra page for input buffer. This page + * we are allocating during init time and will not be released through-out + * life time of resizer driver. Please note that Resizer module only reads + * from this extra page. + */ +int omap_create_sg(struct videobuf_queue *q, struct videobuf_dmabuf *dma) +{ + struct scatterlist *sglist; + int sglen; + + if (0 == dma->nr_pages) + return EINVAL; + sglen = dma->sglen; + sglist = vmalloc((dma->nr_pages + 1) * sizeof(*sglist)); + if (NULL == sglist) + return -ENOMEM; + + sg_init_table(sglist, dma->nr_pages + 1); + /* + * Copy the sglist locally + */ + memcpy(sglist, dma->sglist, sglen * sizeof(*sglist)); + /* + * Release the old sglist, since we already copied it locally + */ + videobuf_dma_unmap(q, dma); + /* + * Add extra entry to sglist to work with specific params, whose + * buffer address alined to PAGE_SIZE. + */ + sglist[sglen].offset = 0; + sglist[sglen].length = PAGE_SIZE; + sglist[sglen].dma_address = (dma_addr_t)device_config->extra_page_addr; + sglen++; + /* + * Save the sglist for mapping to ISP-MMU space + */ + dma->sglist = sglist; + dma->sglen = sglen; + return 0; +} +/** + * rsz_vbq_prepare - Videobuffer is prepared and mmapped. + * @q: Structure containing the videobuffer queue file handle, and device + * structure which contains the actual configuration. + * @vb: Structure containing the videobuffer used for resizer processing. + * @field: Type of field to set in videobuffer device. + * + * Returns 0 if successful, or -EINVAL if buffer couldn't get allocated, or + * -EIO if the ISP MMU mapping fails + **/ +static int rsz_vbq_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct rsz_fh *fh = q->priv_data; + struct channel_config *rsz_conf_chan = fh->config; + struct rsz_mult *multipass = fh->multipass; + int err = 0; + unsigned int isp_addr, insize, outsize; + + spin_lock(&fh->vbq_lock); + if (vb->baddr) { + /* Check for 32 byte alignement */ + if (vb->baddr != (vb->baddr & ~0x1F)) { + spin_unlock(&fh->vbq_lock); + dev_err(rsz_device, "Buffer address should be aligned \ + to 32 byte\n"); + return -EINVAL; + } + vb->size = fh->rsz_bufsize; + vb->bsize = fh->rsz_bufsize; + } else { + spin_unlock(&fh->vbq_lock); + dev_err(rsz_device, "No user buffer allocated\n"); + return -EINVAL; + } + if (vb->i) { + vb->width = fh->params->out_hsize; + vb->height = fh->params->out_vsize; + } else { + vb->width = fh->params->in_hsize; + vb->height = fh->params->in_vsize; + } + + vb->field = field; + spin_unlock(&fh->vbq_lock); + /* + * Calculate input and output sizes, will be used while mapping + * user pages + */ + outsize = multipass->out_pitch * multipass->out_vsize; + insize = multipass->in_pitch * multipass->in_vsize; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + struct videobuf_dmabuf *dma; + struct vm_area_struct *vma; + spin_lock(&fh->vbq_lock); + dma = videobuf_to_dma(vb); + vma = find_vma(current->mm, vb->baddr); + if ((vma) && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) { + /* This will catch ioremaped buffers to the kernel. + * It gives two possible scenarios - + * - Driver allocates buffer using either + * dma_alloc_coherent or get_free_pages, + * and maps to user space using + * io_remap_pfn_range/remap_pfn_range + * - Drivers maps memory outside from Linux using + * io_remap + */ + unsigned long physp = 0, asize; + asize = vb->i ? outsize : insize; + if ((vb->baddr + asize) > vma->vm_end) { + spin_unlock(&fh->vbq_lock); + dev_err(rsz_device, "User Buffer Allocation:" \ + "err=%lu[%lu]\n",\ + (vma->vm_end - vb->baddr), asize); + return -ENOMEM; + } + physp = (vma->vm_pgoff << PAGE_SHIFT) + + (vb->baddr - vma->vm_start); + err = omap_videobuf_dma_init_user(vb, physp, asize); + spin_unlock(&fh->vbq_lock); + if (0 != err) + return err; + is_vm_io = 1; + } else { + err = videobuf_iolock(q, vb, NULL); + /* + * In case of user pointer mode, the get_user_pages + * will fail if user has allocated less memory than + * vb->size. But it is not error from resizer driver + * point of view. so handled seperately + */ + if ((err < 0) && (dma->nr_pages > 0)) + err = videobuf_dma_map(q, dma); + if (err) + goto buf_release; + /* + * Add one extra page for input buffer + */ + if (0 == vb->i) + err = omap_create_sg(q, dma); + if (err) + goto buf_release; + spin_unlock(&fh->vbq_lock); + is_vm_io = 0; + } + isp_addr = ispmmu_vmap(fh->dev, dma->sglist, dma->sglen); + if (!isp_addr) + err = -EIO; + else { + if (vb->i) { + rsz_conf_chan->buf_address[vb->i] = isp_addr; + rsz_conf_chan->register_config. + rsz_sdr_outadd + = isp_addr; + rsz_conf_chan->output_buf_index = vb->i; + } else { + rsz_conf_chan->buf_address[vb->i] = isp_addr; + rsz_conf_chan->register_config. + rsz_sdr_inadd + = isp_addr; + rsz_conf_chan->input_buf_index = vb->i; + if (outsize < insize && rsz_conf_chan-> + register_config. + rsz_sdr_outadd == 0) { + rsz_conf_chan->register_config. + rsz_sdr_outadd + = isp_addr; + rsz_conf_chan-> + output_buf_index = + vb->i; + } + } + } + + } else { + if(vb->i) { + rsz_conf_chan->register_config. + rsz_sdr_outadd = + rsz_conf_chan->buf_address[vb->i]; + rsz_conf_chan->output_buf_index = vb->i; + } else { + rsz_conf_chan->register_config. + rsz_sdr_inadd = + rsz_conf_chan->buf_address[vb->i]; + rsz_conf_chan->input_buf_index = vb->i; + if(outsize < insize && rsz_conf_chan-> + register_config. + rsz_sdr_outadd == 0) { + rsz_conf_chan->register_config. + rsz_sdr_outadd + = rsz_conf_chan->buf_address[vb->i]; + rsz_conf_chan->output_buf_index = vb->i; + } + + } + + } + if (!err) { + spin_lock(&fh->vbq_lock); + vb->state = VIDEOBUF_PREPARED; + spin_unlock(&fh->vbq_lock); + } else + rsz_vbq_release(q, vb); + + return err; +buf_release: + spin_unlock(&fh->vbq_lock); + rsz_vbq_release(q, vb); + return err; +} + +static void rsz_vbq_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + return; +} + +/** + * rsz_open - Initializes and opens the Resizer Wrapper + * @inode: Inode structure associated with the Resizer Wrapper + * @filp: File structure associated with the Resizer Wrapper + * + * Returns 0 if successful, -EBUSY if its already opened or the ISP module is + * not available, or -ENOMEM if its unable to allocate the device in kernel + * space memory. + **/ +static int rsz_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct channel_config *rsz_conf_chan; + struct rsz_fh *fh; + struct device_params *device = device_config; + struct rsz_params *params; + struct rsz_mult *multipass; + + if ((filp->f_flags & O_NONBLOCK) == O_NONBLOCK) { + printk(KERN_DEBUG "omap-resizer: Device is opened in " + "non blocking mode\n"); + } else { + printk(KERN_DEBUG "omap-resizer: Device is opened in blocking " + "mode\n"); + } + fh = kzalloc(sizeof(struct rsz_fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + + fh->dev = isp_get(); + if (fh->dev == NULL) + return -EINVAL; + + isp_start(fh->dev); + + rsz_conf_chan = kzalloc(sizeof(struct channel_config), GFP_KERNEL); + if (rsz_conf_chan == NULL) { + dev_err(rsz_device, "\n cannot allocate memory to config"); + ret = -ENOMEM; + goto err_enomem0; + } + params = kzalloc(sizeof(struct rsz_params), GFP_KERNEL); + if (params == NULL) { + dev_err(rsz_device, "\n cannot allocate memory to params"); + ret = -ENOMEM; + goto err_enomem1; + } + multipass = kzalloc(sizeof(struct rsz_mult), GFP_KERNEL); + if (multipass == NULL) { + dev_err(rsz_device, "\n cannot allocate memory to multipass"); + ret = -ENOMEM; + goto err_enomem2; + } + + fh->multipass = multipass; + fh->params = params; + fh->config = rsz_conf_chan; + + if (mutex_lock_interruptible(&device->reszwrap_mutex)) { + ret = -EINTR; + goto err_enomem2; + } + device->opened++; + mutex_unlock(&device->reszwrap_mutex); + + rsz_conf_chan->config_state = STATE_NOT_CONFIGURED; + rsz_conf_chan->status = CHANNEL_FREE; + + filp->private_data = fh; + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->device = device; + + videobuf_queue_sg_init(&fh->vbq, &device->vbq_ops, NULL, + &fh->vbq_lock, fh->type, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), fh); + + spin_lock_init(&fh->vbq_lock); + mutex_init(&rsz_conf_chan->chanprotection_mutex); + + return 0; +err_enomem2: + kfree(params); +err_enomem1: + kfree(rsz_conf_chan); +err_enomem0: + kfree(fh); + return ret; +} + +/** + * rsz_release - Releases Resizer Wrapper and frees up allocated memory + * @inode: Inode structure associated with the Resizer Wrapper + * @filp: File structure associated with the Resizer Wrapper + * + * Returns 0 if successful, or -EBUSY if channel is being used. + **/ +static int rsz_release(struct inode *inode, struct file *filp) +{ + int i; + unsigned int timeout = 0; + struct rsz_fh *fh = filp->private_data; + struct channel_config *rsz_conf_chan = fh->config; + struct rsz_params *params = fh->params; + struct rsz_mult *multipass = fh->multipass; + struct videobuf_queue *q = &fh->vbq; + + while ((rsz_conf_chan->status != CHANNEL_FREE) && (timeout < 20)) { + timeout++; + schedule(); + } + /* Free memory allocated to the buffers */ + for (i = 0 ; i < VIDEO_MAX_FRAME ; i++) { + struct videobuf_dmabuf *dma = NULL; + + if (!q->bufs[i]) + continue; + dma = videobuf_to_dma(q->bufs[i]); + if (is_vm_io) { + vfree(dma->sglist); + dma->sglist = NULL; + dma->sglen = 0; + } else { + videobuf_dma_unmap(q, dma); + videobuf_dma_free(dma); + } + } + + videobuf_mmap_free(q); + fh->rsz_bufsize = 0; + filp->private_data = NULL; + + kfree(rsz_conf_chan); + kfree(params); + kfree(multipass); + kfree(fh); + + isp_put(); + fh->params = NULL; + fh->config = NULL; + return 0; +} + +/** + * rsz_mmap - Memory maps the Resizer Wrapper module. + * @file: File structure associated with the Resizer Wrapper + * @vma: Virtual memory area structure. + * + * Returns 0 if successful, or returned value by the videobuf_mmap_mapper() + * function. + **/ +static int rsz_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct rsz_fh *fh = file->private_data; + + return videobuf_mmap_mapper(&fh->vbq, vma); +} + +/** + * rsz_ioctl - I/O control function for Resizer Wrapper + * @inode: Inode structure associated with the Resizer Wrapper. + * @file: File structure associated with the Resizer Wrapper. + * @cmd: Type of command to execute. + * @arg: Argument to send to requested command. + * + * Returns 0 if successful, -EBUSY if channel is being used, -1 if bad command + * passed or access is denied, -EFAULT if copy_from_user() or copy_to_user() + * fails, -EINVAL if parameter validation fails or parameter structure is not + * present. + **/ +static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct rsz_fh *fh = file->private_data; + struct device_params *device = fh->device; + struct channel_config *rsz_conf_chan = fh->config; + + if ((_IOC_TYPE(cmd) != RSZ_IOC_BASE) + || (_IOC_NR(cmd) > RSZ_IOC_MAXNR)) { + dev_err(rsz_device, "Bad command value \n"); + return -1; + } + + if (_IOC_DIR(cmd) & _IOC_READ) + ret = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + ret = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); + + if (ret) { + dev_err(rsz_device, "Access denied\n"); + return -1; + } + + switch (cmd) { + case RSZ_REQBUF: + { + struct v4l2_requestbuffers req_buf; + if (copy_from_user(&req_buf, (struct v4l2_requestbuffers *)arg, + sizeof(struct v4l2_requestbuffers))) { + return -EFAULT; + } + if (mutex_lock_interruptible(&rsz_conf_chan-> + chanprotection_mutex)) + return -EINTR; + ret = videobuf_reqbufs(&fh->vbq, (void *)&req_buf); + if (ret >= 0) { + if (copy_to_user((struct v4l2_requestbuffers *)arg, + &req_buf, sizeof(struct + v4l2_requestbuffers))) + return -EFAULT; + } + mutex_unlock(&rsz_conf_chan->chanprotection_mutex); + break; + } + case RSZ_QUERYBUF: + { + struct v4l2_buffer buf; + if (copy_from_user(&buf, (struct v4l2_buffer *)arg, + sizeof(struct v4l2_buffer))) { + return -EFAULT; + } + if (mutex_lock_interruptible(&rsz_conf_chan-> + chanprotection_mutex)) + return -EINTR; + ret = videobuf_querybuf(&fh->vbq, (void *)&buf); + mutex_unlock(&rsz_conf_chan->chanprotection_mutex); + if (copy_to_user((struct v4l2_buffer *)arg, &buf, + sizeof(struct v4l2_buffer))) + return -EFAULT; + break; + } + case RSZ_QUEUEBUF: + { + struct v4l2_buffer buf; + if (copy_from_user(&buf, (struct v4l2_buffer *)arg, + sizeof(struct v4l2_buffer))) { + return -EFAULT; + } + if (mutex_lock_interruptible(&rsz_conf_chan-> + chanprotection_mutex)) + return -EINTR; + ret = videobuf_qbuf(&fh->vbq, (void *)&buf); + mutex_unlock(&rsz_conf_chan->chanprotection_mutex); + break; + } + case RSZ_S_PARAM: + { + struct rsz_params *params = fh->params; + if (copy_from_user(params, (struct rsz_params *)arg, + sizeof(struct rsz_params))) { + return -EFAULT; + } + ret = rsz_set_params(fh->multipass, fh->params, rsz_conf_chan); + break; + } + case RSZ_G_PARAM: + ret = rsz_get_params((struct rsz_params *)arg, rsz_conf_chan); + break; + + case RSZ_G_STATUS: + { + struct rsz_status *status; + struct isp_device *isp = dev_get_drvdata(fh->dev); + status = (struct rsz_status *)arg; + status->chan_busy = rsz_conf_chan->status; + status->hw_busy = ispresizer_busy(&isp->isp_res); + status->src = INPUT_RAM; + break; + } + case RSZ_RESIZE: + { + struct isp_device *isp = dev_get_drvdata(fh->dev); + if (file->f_flags & O_NONBLOCK) { + if (ispresizer_busy(&isp->isp_res)) + return -EBUSY; + else { + if (!mutex_trylock(&device->reszwrap_mutex)) + return -EBUSY; + } + } else { + if (mutex_lock_interruptible(&device->reszwrap_mutex)) + return -EINTR; + } + ret = rsz_start((int *)arg, fh); + mutex_unlock(&device->reszwrap_mutex); + break; + } + case RSZ_GET_CROPSIZE: + rsz_calculate_crop(rsz_conf_chan, (struct rsz_cropsize *)arg); + break; + case RSZ_S_EXP: + if (mutex_lock_interruptible(&rsz_conf_chan-> + chanprotection_mutex)) + return -EINTR; + rsz_conf_chan->register_config.sdr_req_exp = + *((unsigned int *)arg); + mutex_unlock(&rsz_conf_chan->chanprotection_mutex); + break; + + default: + dev_err(rsz_device, "resizer_ioctl: Invalid Command Value"); + return -EINVAL; + } + + return (long)ret; +} + +static const struct file_operations rsz_fops = { + .owner = THIS_MODULE, + .open = rsz_open, + .release = rsz_release, + .mmap = rsz_mmap, + .unlocked_ioctl = rsz_unlocked_ioctl, +}; + +/** + * rsz_isr - Interrupt Service Routine for Resizer wrapper + * @status: ISP IRQ0STATUS register value + * @arg1: Currently not used + * @arg2: Currently not used + * + * Interrupt Service Routine for Resizer wrapper + **/ +static void rsz_isr(unsigned long status, isp_vbq_callback_ptr arg1, void *arg2) +{ + + if ((status & RESZ_DONE) != RESZ_DONE) + return; + + complete(&(device_config->compl_isr)); + +} + +/** + * resizer_platform_release - Acts when Reference count is zero + * @device: Structure containing ISP resizer wrapper global information + * + * This is called when the reference count goes to zero. + **/ +static void resizer_platform_release(struct device *device) +{ +} + +/** + * resizer_probe - Checks for device presence + * @device: Structure containing details of the current device. + * + * Always returns 0. + **/ +static int __init resizer_probe(struct platform_device *device) +{ + return 0; +} + +/** + * resizer_remove - Handles the removal of the driver + * @omap_resizer_device: Structure containing details of the current device. + * + * Always returns 0. + **/ +static int resizer_remove(struct platform_device *omap_resizer_device) +{ + return 0; +} + +static struct class *rsz_class; +static struct cdev c_dev; +static dev_t dev; +static struct platform_device omap_resizer_device = { + .name = OMAP_REZR_NAME, + .id = 2, + .dev = { + .release = resizer_platform_release,} +}; + +static struct platform_driver omap_resizer_driver = { + .probe = resizer_probe, + .remove = resizer_remove, + .driver = { + .bus = &platform_bus_type, + .name = OMAP_REZR_NAME, + }, +}; + +/** + * omap_rsz_init - Initialization of Resizer Wrapper + * + * Returns 0 if successful, -ENOMEM if could not allocate memory, -ENODEV if + * could not register the wrapper as a character device, or other errors if the + * device or driver can't register. + **/ +static int __init omap_rsz_init(void) +{ + int ret = 0; + struct device_params *device; + device = kzalloc(sizeof(struct device_params), GFP_KERNEL); + if (!device) { + printk(OMAP_REZR_NAME ": could not allocate memory\n"); + return -ENOMEM; + } + device->extra_page_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, 0); + if (!device->extra_page_addr) { + printk(OMAP_REZR_NAME ":Allocation failed. "); + kfree(device); + return -ENOMEM; + } + + ret = alloc_chrdev_region(&dev, 0, 1, OMAP_REZR_NAME); + if (ret < 0) { + printk(OMAP_REZR_NAME ": intialization failed. " + "Could not allocate region for character device\n"); + goto fail1; + } + + /* Register the driver in the kernel */ + /* Initialize of character device */ + cdev_init(&c_dev, &rsz_fops); + c_dev.owner = THIS_MODULE; + c_dev.ops = &rsz_fops; + + /* Addding character device */ + ret = cdev_add(&c_dev, dev, 1); + if (ret) { + printk(OMAP_REZR_NAME ": Error adding device - %d\n", ret); + goto fail2; + } + rsz_major = MAJOR(dev); + + /* register driver as a platform driver */ + ret = platform_driver_register(&omap_resizer_driver); + if (ret) { + printk(OMAP_REZR_NAME + ": Failed to register platform driver!\n"); + goto fail3; + } + + /* Register the drive as a platform device */ + ret = platform_device_register(&omap_resizer_device); + if (ret) { + printk(OMAP_REZR_NAME + ": Failed to register platform device!\n"); + goto fail4; + } + + rsz_class = class_create(THIS_MODULE, OMAP_REZR_NAME); + if (!rsz_class) { + printk(OMAP_REZR_NAME ": Failed to create class!\n"); + goto fail5; + } + + /* make entry in the devfs */ + rsz_device = device_create(rsz_class, rsz_device, + MKDEV(rsz_major, 0), NULL, + OMAP_REZR_NAME); + dev_dbg(rsz_device, OMAP_REZR_NAME ": Registered Resizer Wrapper\n"); + device->opened = 0; + + device->vbq_ops.buf_setup = rsz_vbq_setup; + device->vbq_ops.buf_prepare = rsz_vbq_prepare; + device->vbq_ops.buf_release = rsz_vbq_release; + device->vbq_ops.buf_queue = rsz_vbq_queue; + init_completion(&device->compl_isr); + mutex_init(&device->reszwrap_mutex); + + device_config = device; + return 0; + +fail5: + platform_device_unregister(&omap_resizer_device); +fail4: + platform_driver_unregister(&omap_resizer_driver); +fail3: + cdev_del(&c_dev); +fail2: + unregister_chrdev_region(dev, 1); +fail1: + free_pages((unsigned long)device->extra_page_addr, 0); + kfree(device); + return ret; +} + +/** + * omap_rsz_exit - Close of Resizer Wrapper + **/ +void __exit omap_rsz_exit(void) +{ + device_destroy(rsz_class, dev); + class_destroy(rsz_class); + platform_device_unregister(&omap_resizer_device); + platform_driver_unregister(&omap_resizer_driver); + cdev_del(&c_dev); + unregister_chrdev_region(dev, 1); + free_pages((unsigned long)device_config->extra_page_addr, 0); + kfree(device_config); +} + +module_init(omap_rsz_init) +module_exit(omap_rsz_exit) + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP ISP Resizer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/isp/redgamma_table.h b/drivers/media/video/isp/redgamma_table.h new file mode 100644 index 00000000000..ad0232a77f5 --- /dev/null +++ b/drivers/media/video/isp/redgamma_table.h @@ -0,0 +1,1040 @@ +/* + * redgamma_table.h + * + * Gamma Table values for RED for TI's OMAP3 Camera ISP + * + * Copyright (C) 2009 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. + */ + +0, +0, +1, +2, +3, +3, +4, +5, +6, +8, +10, +12, +14, +16, +18, +20, +22, +23, +25, +26, +28, +29, +31, +32, +34, +35, +36, +37, +39, +40, +41, +42, +43, +44, +45, +46, +47, +48, +49, +50, +51, +52, +52, +53, +54, +55, +56, +57, +58, +59, +60, +61, +62, +63, +63, +64, +65, +66, +66, +67, +68, +69, +69, +70, +71, +72, +72, +73, +74, +75, +75, +76, +77, +78, +78, +79, +80, +81, +81, +82, +83, +84, +84, +85, +86, +87, +88, +88, +89, +90, +91, +91, +92, +93, +94, +94, +95, +96, +97, +97, +98, +98, +99, +99, +100, +100, +101, +101, +102, +103, +104, +104, +105, +106, +107, +108, +108, +109, +110, +111, +111, +112, +113, +114, +114, +115, +116, +117, +117, +118, +119, +119, +120, +120, +121, +121, +122, +122, +123, +123, +124, +124, +125, +125, +126, +126, +127, +127, +128, +128, +129, +129, +130, +130, +131, +131, +132, +132, +133, +133, +134, +134, +135, +135, +136, +136, +137, +137, +138, +138, +139, +139, +140, +140, +141, +141, +142, +142, +143, +143, +144, +144, +145, +145, +146, +146, +147, +147, +148, +148, +149, +149, +150, +150, +151, +151, +152, +152, +153, +153, +153, +153, +154, +154, +154, +154, +155, +155, +156, +156, +157, +157, +158, +158, +158, +159, +159, +159, +160, +160, +160, +161, +161, +162, +162, +163, +163, +164, +164, +164, +164, +165, +165, +165, +165, +166, +166, +167, +167, +168, +168, +169, +169, +170, +170, +170, +170, +171, +171, +171, +171, +172, +172, +173, +173, +174, +174, +175, +175, +176, +176, +176, +176, +177, +177, +177, +177, +178, +178, +178, +178, +179, +179, +179, +179, +180, +180, +180, +180, +181, +181, +181, +181, +182, +182, +182, +182, +183, +183, +183, +183, +184, +184, +184, +184, +185, +185, +185, +185, +186, +186, +186, +186, +187, +187, +187, +187, +188, +188, +188, +188, +189, +189, +189, +189, +190, +190, +190, +190, +191, +191, +191, +191, +192, +192, +192, +192, +193, +193, +193, +193, +194, +194, +194, +194, +195, +195, +195, +195, +196, +196, +196, +196, +197, +197, +197, +197, +198, +198, +198, +198, +199, +199, +199, +199, +200, +200, +200, +200, +201, +201, +201, +201, +202, +202, +202, +203, +203, +203, +203, +204, +204, +204, +204, +205, +205, +205, +205, +206, +206, +206, +206, +207, +207, +207, +207, +208, +208, +208, +208, +209, +209, +209, +209, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +210, +211, +211, +211, +211, +211, +211, +211, +211, +211, +211, +211, +212, +212, +212, +212, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +213, +214, +214, +214, +214, +215, +215, +215, +215, +215, +215, +215, +215, +215, +215, +215, +216, +216, +216, +216, +217, +217, +217, +217, +218, +218, +218, +218, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +219, +220, +220, +220, +220, +221, +221, +221, +221, +221, +221, +221, +221, +221, +221, +221, +222, +222, +222, +222, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +223, +224, +224, +224, +224, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +225, +226, +226, +226, +226, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +227, +228, +228, +228, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +229, +230, +230, +230, +230, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +231, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +232, +233, +233, +233, +233, +234, +234, +234, +234, +234, +234, +234, +234, +234, +234, +234, +235, +235, +235, +235, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +236, +237, +237, +237, +237, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +238, +239, +239, +239, +239, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +240, +241, +241, +241, +241, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +242, +243, +243, +243, +243, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +244, +245, +245, +245, +245, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +246, +247, +247, +247, +247, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +248, +249, +249, +249, +249, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +250, +251, +251, +251, +251, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +252, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +253, +254, +254, +254, +254, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255, +255 diff --git a/drivers/media/video/lv8093.c b/drivers/media/video/lv8093.c new file mode 100644 index 00000000000..ea3320d5512 --- /dev/null +++ b/drivers/media/video/lv8093.c @@ -0,0 +1,604 @@ +/* + * drivers/media/video/lv8093.c + * + * LV8093 Piezo Motor (LENS) driver + * + * Copyright (C) 2008-2009 Texas Instruments. + * Copyright (C) 2009 Hewlett-Packard. + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "lv8093_regs.h" + +static int +lv8093_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int __exit lv8093_remove(struct i2c_client *client); + +struct lv8093_device { + const struct lv8093_platform_data *pdata; + struct v4l2_int_device *v4l2_int_device; + struct i2c_client *i2c_client; + int state; + int power_state; +}; + +static const struct i2c_device_id lv8093_id[] = { + {LV8093_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lv8093_id); + +static struct i2c_driver lv8093_i2c_driver = { + .driver = { + .name = LV8093_NAME, + .owner = THIS_MODULE, + }, + .probe = lv8093_probe, + .remove = __exit_p(lv8093_remove), + .id_table = lv8093_id, +}; + +static struct lv8093_device lv8093 = { + .state = LENS_NOT_DETECTED, +}; + +static struct vcontrol { + struct v4l2_queryctrl qc; +} video_control[] = { + { + { + .id = V4L2_CID_FOCUS_RELATIVE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Lens Relative Position", + .minimum = 0, + .maximum = 0, + .step = LV8093_MAX_RELATIVE_STEP, + .default_value = 0, + } + ,} +}; + +static struct i2c_driver lv8093_i2c_driver; + +static struct lv8093_lens_settings { + u8 reg; + u8 val; +} lens_settings[] = { + { /* Set control register */ + .reg = CAMAF_LV8093_CTL_REG, + .val = CAMAF_LV8093_GATE0 | + CAMAF_LV8093_ENIN | + CAMAF_LV8093_CKSEL_ONE | + CAMAF_LV8093_RET2 | + CAMAF_LV8093_INIT_OFF, + }, + { /* Specify number of clocks per period */ + .reg = CAMAF_LV8093_RST_REG, + .val = (LV8093_CLK_PER_PERIOD - 1), + }, + { /* Set the GATE_A pulse set value */ + .reg = CAMAF_LV8093_GTAS_REG, + .val = (LV8093_TIME_GATEA + 1), + }, + { /* Set the GATE_B pulse reset value */ + .reg = CAMAF_LV8093_GTBR_REG, + .val = (LV8093_TIME_GATEA + 1 + LV8093_TIME_OFF), + }, + { /* Set the GATE_B pulse set value */ + .reg = CAMAF_LV8093_GTBS_REG, + .val = (LV8093_TIME_GATEA + 1 + + LV8093_TIME_OFF + LV8093_TIME_GATEB), + }, + { /* Specific the number of output pulse steps */ + .reg = CAMAF_LV8093_STP_REG, + .val = LV8093_STP, + }, + { /* Set the number of swing back of init sequence performed */ + .reg = CAMAF_LV8093_MOV_REG, + .val = 0, + }, +}; + +/** + * find_vctrl - Finds the requested ID in the video control structure array + * @id: ID of control to search the video control array for + * + * Returns the index of the requested ID from the control structure array + */ +static int find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--) + if (video_control[i].qc.id == id) + break; + if (i < 0) + i = -EINVAL; + return i; +} + +/** + * lv8093_reg_read - Reads a value from a register in LV8093 Piezo + * driver device. + * @client: Pointer to structure of I2C client. + * @value: Pointer to u16 for returning value of register to read. + * + * Returns zero if successful, or non-zero otherwise. + **/ +static int lv8093_reg_read(struct i2c_client *client, u8 *value) +{ + int err; + struct i2c_msg msg[1]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = I2C_M_RD; + msg->len = 1; + msg->buf = value; + + err = i2c_transfer(client->adapter, msg, 1); + + if (err < 0) + v4l_err(client, "i2c read failed with error %i", err); + + return err; +} + +/** + * lv8093_reg_write - Writes a value to a register in LV8093 Piezo + * driver device. + * @client: Pointer to structure of I2C client. + * @reg: Register to write. + * @value: Value of register to write. + * + * Returns zero or +ve if successful, -ve for error. + **/ +static int lv8093_reg_write(struct i2c_client *client, u8 reg, u8 value) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + + data[0] = reg; + data[1] = value; + + err = i2c_transfer(client->adapter, msg, 1); + if (err < 0) + v4l_err(client, "i2c write failed with error %i", err); + + return err; +} + +/** + * lv8093_detect - Detects LV8093 Piezo driver device. + * @client: Pointer to structure of I2C client. + * + * Returns 0 if successful, -1 if camera is off or if test register value + * wasn't stored properly, or return error from lv8093_reg_write function. + **/ +static int lv8093_detect(struct i2c_client *client) +{ + int err = 0; + u8 rposn = 0; + + err = lv8093_reg_write(client, CAMAF_LV8093_CTL_REG, + CAMAF_LV8093_GATE0 | + CAMAF_LV8093_CKSEL_ONE | + CAMAF_LV8093_RET2 | + CAMAF_LV8093_INIT_OFF); + + if (err < 0) { + v4l_err(client, "Unable to write LV8093\n"); + return err; + } + + err = lv8093_reg_read(client, &rposn); + if (err < 0) { + v4l_err(client, "Unable to read LV8093\n"); + return err; + } + + return err; +} + +/** + * lv8093_reginit - Initializes LV8093 Piezo driver device. + * @client: Pointer to structure of I2C client. + * + * Returns 0 if successful, or returns errors from lv8093_reg_write. + **/ +static int lv8093_reginit(struct i2c_client *client) +{ + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(lens_settings); i++) { + + err = lv8093_reg_write(client, + lens_settings[i].reg, lens_settings[i].val); + + if (err < 0) { + v4l_err(client, "Unable to initialize LV8093\n"); + return err; + } + } + + return 0; +} + +/** + * lv8093_af_setfocus - Sets the desired focus. + * @relpos: Relative focus position: + * -ve - Direction INFINITY. + * +ve - Direction MACRO. + * abs(relpos) gives number of steps in desired direction. + * + * Returns 0 on success, -EINVAL if camera is off or returned errors + * from lv8093_reg_write function. + **/ +int lv8093_af_setfocus(s16 relpos) +{ + struct lv8093_device *af_dev = &lv8093; + struct i2c_client *client = af_dev->i2c_client; + u8 num_pulses = abs(relpos); + int ret = 0; + + if ((af_dev->power_state == V4L2_POWER_OFF) || + (af_dev->power_state == V4L2_POWER_STANDBY)) + return -EINVAL; + + if (relpos >= 0) { + /* Move lens in Macro direction */ + ret |= lv8093_reg_write(client, CAMAF_LV8093_DRVPLS_REG, + 0 & (~CAMAF_LV8093_MAC_DIR)); + ret |= lv8093_reg_write(client, CAMAF_LV8093_DRVPLS_REG, + num_pulses | CAMAF_LV8093_MAC_DIR); + + } else { + /* Move lens in Infinite direction */ + ret |= lv8093_reg_write(client, CAMAF_LV8093_DRVPLS_REG, + 0 | CAMAF_LV8093_MAC_DIR); + ret |= lv8093_reg_write(client, CAMAF_LV8093_DRVPLS_REG, + num_pulses & (~CAMAF_LV8093_MAC_DIR)); + + } + + if (ret < 0) { + v4l_err(client, "Unable to write " LV8093_NAME + " lens HW\n"); + return -EINVAL; + } + + return 0; +} + +/** + * lv8093_is_busy - Read busy bit. + * + * Returns: + * 0 for READY, -EBUSY for device busy, -EINVAL on error. + **/ +int lv8093_is_busy(void) +{ + struct lv8093_device *af_dev = &lv8093; + struct i2c_client *client = af_dev->i2c_client; + int ret; + u8 regval; + + ret = lv8093_reg_read(client, ®val); + + if (ret < 0) { + dev_err(&client->dev, "Unable to read " LV8093_NAME + " lens HW\n"); + return -EINVAL; + } + + if (regval & CAMAF_LV8093_BUSY) + return -EBUSY; + + return 0; +} + +/** + * ioctl_queryctrl - V4L2 lens interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the video_control[] array. Otherwise, returns -EINVAL if the + * control is not supported. + */ +static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc) +{ + int i; + + i = find_vctrl(qc->id); + if (i == -EINVAL) + qc->flags = V4L2_CTRL_FLAG_DISABLED; + + if (i < 0) + return -EINVAL; + + *qc = video_control[i].qc; + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 LV8093 lens interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = -EINVAL; + int i; + struct vcontrol *lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_FOCUS_RELATIVE: + retval = lv8093_af_setfocus((s16)vc->value); + break; + } + + return retval; +} + +/** + * ioctl_g_ctrl - V4L2 LV8093 lens interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * For V4L2_CID_FOCUS_RELATIVE control always returns the control's value + * as zero. However, the return value is used to return whether the device + * is busy still moving the lens. It will do this by returning -EBUSY (busy) + * or 0 (ready). + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int i, retval = -EINVAL; + struct vcontrol *lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_FOCUS_RELATIVE: + retval = lv8093_is_busy(); + vc->value = 0; + break; + } + return retval; +} + +/** + * ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold sensor's private data address + * + * Returns device's (sensor's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct lv8093_device *lens = s->priv; + + return lens->pdata->priv_data_set(p); +} + +/** + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +{ + struct lv8093_device *lens = s->priv; + struct i2c_client *c = lens->i2c_client; + int rval; + + if (lens->pdata->power_set) + rval = lens->pdata->power_set(on); + + lens->power_state = on; + + if ((on == V4L2_POWER_ON) && (lens->state == LENS_NOT_DETECTED)) { + rval = lv8093_detect(c); + if (rval < 0) { + v4l_err(c, "Unable to detect " LV8093_NAME + " lens HW\n"); + lens->state = LENS_NOT_DETECTED; + return rval; + } + lens->state = LENS_DETECTED; + pr_info(LV8093_NAME " Lens HW detected\n"); + + rval = lv8093_reginit(c); + if (rval < 0) { + v4l_err(c, "Unable to initialize " LV8093_NAME + " lens HW\n"); + lens->state = LENS_NOT_DETECTED; + return rval; + } + } + + if ((lens->power_state == V4L2_POWER_STANDBY) && (on == V4L2_POWER_ON) + && (lens->state == LENS_DETECTED)) { + rval = lv8093_reginit(c); + if (rval < 0) { + v4l_err(c, "Unable to initialize " LV8093_NAME + " lens HW\n"); + lens->state = LENS_NOT_DETECTED; + return rval; + } + } + return 0; +} + +static struct v4l2_int_ioctl_desc lv8093_ioctl_desc[] = { + {.num = vidioc_int_s_power_num, + .func = (v4l2_int_ioctl_func *) ioctl_s_power}, + {.num = vidioc_int_g_priv_num, + .func = (v4l2_int_ioctl_func *) ioctl_g_priv}, + {.num = vidioc_int_queryctrl_num, + .func = (v4l2_int_ioctl_func *) ioctl_queryctrl}, + {.num = vidioc_int_s_ctrl_num, + .func = (v4l2_int_ioctl_func *) ioctl_s_ctrl}, + {.num = vidioc_int_g_ctrl_num, + .func = (v4l2_int_ioctl_func *) ioctl_g_ctrl}, +}; + +static struct v4l2_int_slave lv8093_slave = { + .ioctls = lv8093_ioctl_desc, + .num_ioctls = ARRAY_SIZE(lv8093_ioctl_desc), +}; + +static struct v4l2_int_device lv8093_int_device = { + .module = THIS_MODULE, + .name = LV8093_NAME, + .priv = &lv8093, + .type = v4l2_int_type_slave, + .u = { + .slave = &lv8093_slave, + }, +}; + +/** + * lv8093_probe - Probes the driver for valid I2C attachment. + * @client: Pointer to structure of I2C client. + * + * Returns 0 if successful, or -EBUSY if unable to get client attached data. + **/ +static int +lv8093_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct lv8093_device *lens = &lv8093; + int err; + + if (i2c_get_clientdata(client)) { + v4l_err(client, "DTA BUSY %s\n", client->name); + return -EBUSY; + } + + lens->pdata = client->dev.platform_data; + + if (!lens->pdata) { + v4l_err(client, "no platform data?\n"); + return -ENODEV; + } + + lens->v4l2_int_device = &lv8093_int_device; + + lens->i2c_client = client; + i2c_set_clientdata(client, lens); + + err = v4l2_int_device_register(lens->v4l2_int_device); + if (err) { + v4l_err(client, "Failed to Register " + LV8093_NAME " as V4L2 device.\n"); + i2c_set_clientdata(client, NULL); + } else { + v4l_info(client, "Registered " LV8093_NAME + " as V4L2 device.\n"); + } + + return err; +} + +/** + * lv8093_remove - Routine when device its unregistered from I2C + * @client: Pointer to structure of I2C client. + * + * Returns 0 if successful, or -ENODEV if the client isn't attached. + **/ +static int __exit lv8093_remove(struct i2c_client *client) +{ + struct lv8093_device *lens = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; + + v4l2_int_device_unregister(lens->v4l2_int_device); + i2c_set_clientdata(client, NULL); + return 0; +} + +/** + * lv8093_init - Module initialisation. + * + * Returns 0 if successful, or -EINVAL if device couldn't be initialized, or + * added as a character device. + **/ +static int __init lv8093_init(void) +{ + int err; + + err = i2c_add_driver(&lv8093_i2c_driver); + if (err) + goto fail; + pr_info("Registered " LV8093_NAME " as i2c device.\n"); + + return err; +fail: + pr_err("Failed to register " LV8093_NAME " as i2c driver.\n"); + return err; +} + +late_initcall(lv8093_init); + +/** + * lv8093_cleanup - Module cleanup. + **/ +static void __exit lv8093_cleanup(void) +{ + i2c_del_driver(&lv8093_i2c_driver); +} + +module_exit(lv8093_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LV8093 LENS driver"); diff --git a/drivers/media/video/lv8093_regs.h b/drivers/media/video/lv8093_regs.h new file mode 100644 index 00000000000..0a208509f21 --- /dev/null +++ b/drivers/media/video/lv8093_regs.h @@ -0,0 +1,76 @@ +/* + * lv8093_regs.h + * + * Copyright (C) 2008-2009 Texas Instruments. + * Copyright (C) 2009 Hewlett-Packard. + * + * 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. + * + * Register defines for Lens piezo-actuator device + * + */ +#ifndef LV8093_REGS_H +#define LV8093_REGS_H + +#include + +#define LV8093_I2C_RETRY_COUNT 5 + +#define CAMAF_LV8093_DISABLE 0x1 +#define CAMAF_LV8093_ENABLE 0x0 +#define CAMAF_LV8093_DRVPLS_REG 0x0 +#define CAMAF_LV8093_CTL_REG 0x1 +#define CAMAF_LV8093_RST_REG 0x2 +#define CAMAF_LV8093_GTAS_REG 0x3 +#define CAMAF_LV8093_GTBR_REG 0x4 +#define CAMAF_LV8093_GTBS_REG 0x5 +#define CAMAF_LV8093_STP_REG 0x6 +#define CAMAF_LV8093_MOV_REG 0x7 +#define CAMAF_LV8093_MAC_DIR 0x80 +#define CAMAF_LV8093_INF_DIR 0x00 +#define CAMAF_LV8093_GATE0 0x00 +#define CAMAF_LV8093_GATE1 0x80 +#define CAMAF_LV8093_ENIN 0x20 +#define CAMAF_LV8093_CKSEL_ONE 0x18 +#define CAMAF_LV8093_CKSEL_HALF 0x08 +#define CAMAF_LV8093_CKSEL_QTR 0x00 +#define CAMAF_LV8093_RET2 0x00 +#define CAMAF_LV8093_RET1 0x02 +#define CAMAF_LV8093_RET3 0x04 +#define CAMAF_LV8093_RET4 0x06 +#define CAMAF_LV8093_INIT_OFF 0x01 +#define CAMAF_LV8093_INIT_ON 0x00 +#define CAMAF_LV8093_BUSY 0x80 +#define CAMAF_LV8093_REGDATA(REG, DATA) (((REG) << 8) | (DATA)) + +#define CAMAF_LV8093_POWERDN(ARG) (((ARG) & 0x1) << 15) +#define CAMAF_LV8093_POWERDN_R(ARG) (((ARG) >> 15) & 0x1) + +#define CAMAF_LV8093_DATA(ARG) (((ARG) & 0xFF) << 6) +#define CAMAF_LV8093_DATA_R(ARG) (((ARG) >> 6) & 0xFF) +#define CAMAF_FREQUENCY_EQ1(mclk) ((u16)(mclk/16000)) + +/* State of lens */ +#define LENS_DETECTED 1 +#define LENS_NOT_DETECTED 0 + +/* Focus control values */ +#define LV8093_MAX_RELATIVE_STEP 127 + +/* Initialization Mode Settings */ +#define LV8093_TIME_GATEA 23 /* First pulse width. */ +#define LV8093_TIME_OFF 2 /* Off time between pulses. */ +#define LV8093_TIME_GATEB 29 /* Second pulse width. */ +#define LV8093_STP 24 /* Pulse repetitions. */ +/* Numbers of clock periods per cycle: */ +/* 18MHz clock, period = 55.6 nsec */ +#define LV8093_CLK_PER_PERIOD 104 + +#endif /* End of of LV8093_REGS_H */ + diff --git a/drivers/media/video/mt9p012.c b/drivers/media/video/mt9p012.c new file mode 100644 index 00000000000..938041feaab --- /dev/null +++ b/drivers/media/video/mt9p012.c @@ -0,0 +1,1888 @@ +/* + * mt9p012.c - mt9p012 sensor driver + * + * Copyright (C) 2009 Texas Instruments. + * + * Contributors: + * Sameer Venkatraman + * Sergio Aguirre + * Martinez Leonides + * + * Leverage OV9640.c + * + * 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. + */ + +#include +#include +#include + +#include +#include "mt9p012_regs.h" + +#define DRIVER_NAME "mt9p012" + +/* MT9P012 has 8/16/32 registers */ +#define MT9P012_8BIT 1 +#define MT9P012_16BIT 2 +#define MT9P012_32BIT 4 + +/* terminating token for reg list */ +#define MT9P012_TOK_TERM 0xFF + +/* delay token for reg list */ +#define MT9P012_TOK_DELAY 100 + +/* The ID values we are looking for */ +#define MT9P012_MOD_ID 0x2800 +#define MT9P012_MFR_ID 0x0006 + +/* FPS Capabilities */ +#define MT9P012_MIN_FPS 11 +#define MT9P012_DEF_FPS 15 +#define MT9P012_MAX_FPS 30 + +#define MT9P012_I2C_RETRY_COUNT 5 + +#define MT9P012_XCLK_NOM_1 12000000 +#define MT9P012_XCLK_NOM_2 24000000 + +/* Still capture 5 MP */ +#define MT9P012_IMAGE_WIDTH_MAX 2592 +#define MT9P012_IMAGE_HEIGHT_MAX 1944 +/* Still capture 3 MP and down to VGA, using ISP resizer */ +#define MT9P012_IMAGE_WIDTH_MIN 2048 +#define MT9P012_IMAGE_HEIGHT_MIN 1536 + +/* Video mode, for D1 NTSC, D1 PAL */ +#define MT9P012_VIDEO_WIDTH_2X_BINN 1296 +#define MT9P012_VIDEO_HEIGHT_2X_BINN 972 + +/* Sensor Video mode size for VGA, CIF, QVGA in 4x binning mode */ +#define MT9P012_VIDEO_WIDTH_4X_BINN 648 +#define MT9P012_VIDEO_HEIGHT_4X_BINN 486 +/* To improve image quality in VGA */ +#define MT9P012_CIF_PIXELS (352 * 288) +#define MT9P012_QQVGA_PIXELS (160 * 120) + +/* Video mode, for QCIF, SQCIF */ +#define MT9P012_VIDEO_WIDTH_4X_BINN_SCALED 216 +#define MT9P012_VIDEO_HEIGHT_4X_BINN_SCALED 162 + +/* Default coarse integration times to get a good exposure */ +#define MT9P012_COARSE_INT_TIME_216 550 +#define MT9P012_COARSE_INT_TIME_648 550 +#define MT9P012_COARSE_INT_TIME_216_30FPS 1350 +#define MT9P012_COARSE_INT_TIME_648_30FPS 1350 +#define MT9P012_COARSE_INT_TIME_1296 1000 +#define MT9P012_COARSE_INT_TIME_3MP 1700 +#define MT9P012_COARSE_INT_TIME_5MP 1700 +#define MT9P012_COARSE_INT_TIME_INDEX 1 +#define MT9P012_TST_PAT 0x0 + +/* Analog gain values */ +#define MT9P012_MIN_GAIN 0x08 +#define MT9P012_MAX_GAIN 0x7F +#define MT9P012_DEF_GAIN 0x43 +#define MT9P012_GAIN_STEP 0x1 + +#define MT9P012_GAIN_INDEX 1 + +/* Exposure time values */ +#define MT9P012_DEF_MIN_EXPOSURE 0x08 +#define MT9P012_DEF_MAX_EXPOSURE 0x7F +#define MT9P012_DEF_EXPOSURE 0x43 +#define MT9P012_EXPOSURE_STEP 1 + +/** + * struct mt9p012_reg - mt9p012 register format + * @length: length of the register + * @reg: 16-bit offset to register + * @val: 8/16/32-bit register value + * + * Define a structure for MT9P012 register initialization values + */ +struct mt9p012_reg { + u16 length; + u16 reg; + u32 val; +}; + +enum mt9p012_image_size { + MT9P012_BIN4XSCALE, + MT9P012_BIN4X, + MT9P012_BIN2X, + MT9P012_THREE_MP, + MT9P012_FIVE_MP +}; + +#define MT9P012_NUM_IMAGE_SIZES 5 +#define MT9P012_NUM_PIXEL_FORMATS 1 +#define MT9P012_NUM_FPS 2 /* 2 ranges */ +#define MT9P012_FPS_LOW_RANGE 0 +#define MT9P012_FPS_HIGH_RANGE 1 + +/** + * struct capture_size - image capture size information + * @width: image width in pixels + * @height: image height in pixels + */ +struct mt9p012_capture_size { + unsigned long width; + unsigned long height; +}; + +/** + * struct mt9p012_pll_settings - struct for storage of sensor pll values + * @vt_pix_clk_div: vertical pixel clock divider + * @vt_sys_clk_div: veritcal system clock divider + * @pre_pll_div: pre pll divider + * @fine_int_tm: fine resolution interval time + * @frame_lines: number of lines in frame + * @line_len: number of pixels in line + * @min_pll: minimum pll multiplier + * @max_pll: maximum pll multiplier + */ +struct mt9p012_pll_settings { + u16 vt_pix_clk_div; + u16 vt_sys_clk_div; + u16 pre_pll_div; + + u16 fine_int_tm; + u16 frame_lines; + u16 line_len; + + u16 min_pll; + u16 max_pll; +}; + +/* + * Array of image sizes supported by MT9P012. These must be ordered from + * smallest image size to largest. + */ +const static struct mt9p012_capture_size mt9p012_sizes[] = { + { 216, 162 }, /* 4X BINNING+SCALING */ + { 648, 486 }, /* 4X BINNING */ + { 1296, 972 }, /* 2X BINNING */ + { 2048, 1536}, /* 3 MP */ + { 2592, 1944}, /* 5 MP */ +}; + +/* PLL settings for MT9P012 */ +enum mt9p012_pll_type { + MT9P012_PLL_5MP = 0, + MT9P012_PLL_3MP, + MT9P012_PLL_1296_15FPS, + MT9P012_PLL_1296_30FPS, + MT9P012_PLL_648_15FPS, + MT9P012_PLL_648_30FPS, + MT9P012_PLL_216_15FPS, + MT9P012_PLL_216_30FPS +}; + +/* Debug functions */ +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug Enabled (0-1)"); + +/** + * struct mt9p012_sensor - main structure for storage of sensor information + * @dev: + * @pdata: access functions and data for platform level information + * @v4l2_int_device: V4L2 device structure structure + * @pix: V4L2 pixel format information structure + * @timeperframe: time per frame expressed as V4L fraction + * @scaler: + * @ver: mt9p012 chip version + * @fps: frames per second value + */ +struct mt9p012_sensor { + struct device *dev; + struct mt9p012_platform_data *pdata; + struct v4l2_int_device *v4l2_int_device; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; + int scaler; + int ver; + int fps; + int detected; + unsigned long xclk_current; +}; + +/* list of image formats supported by mt9p012 sensor */ +const static struct v4l2_fmtdesc mt9p012_formats[] = { + { + .description = "Bayer10 (GrR/BGb)", + .pixelformat = V4L2_PIX_FMT_SGRBG10, + } +}; + +#define NUM_CAPTURE_FORMATS ARRAY_SIZE(mt9p012_formats) + +/* Enters soft standby, all settings are maintained */ +const static struct mt9p012_reg stream_off_list[] = { + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + +/* Exits soft standby */ +const static struct mt9p012_reg stream_on_list[] = { + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x01}, + /* Sensor datasheet says we need 1 ms to allow PLL lock */ + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 1}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + +/* Structure which will set the exposure time */ +static struct mt9p012_reg set_exposure_time[] = { + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + /* less than frame_lines-1 */ + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, .val = 500}, + /* updating */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + +/* Structure to set analog gain */ +static struct mt9p012_reg set_analog_gain[] = { + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_ANALOG_GAIN_GLOBAL, + .val = MT9P012_MIN_GAIN}, + /* updating */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}, +}; + +/* + * Common MT9P012 register initialization for all image sizes, pixel formats, + * and frame rates + */ +const static struct mt9p012_reg mt9p012_common[] = { + {MT9P012_8BIT, REG_SOFTWARE_RESET, 0x01}, + {MT9P012_TOK_DELAY, 0x00, 5}, /* Delay = 5ms, min 2400 xcks */ + {MT9P012_16BIT, REG_RESET_REGISTER, 0x10C8}, + {MT9P012_8BIT, REG_GROUPED_PAR_HOLD, 0x01}, /* hold */ + {MT9P012_16BIT, REG_ANALOG_GAIN_GREENR, 0x0020}, + {MT9P012_16BIT, REG_ANALOG_GAIN_RED, 0x0020}, + {MT9P012_16BIT, REG_ANALOG_GAIN_BLUE, 0x0020}, + {MT9P012_16BIT, REG_ANALOG_GAIN_GREENB, 0x0020}, + {MT9P012_16BIT, REG_DIGITAL_GAIN_GREENR, 0x0100}, + {MT9P012_16BIT, REG_DIGITAL_GAIN_RED, 0x0100}, + {MT9P012_16BIT, REG_DIGITAL_GAIN_BLUE, 0x0100}, + {MT9P012_16BIT, REG_DIGITAL_GAIN_GREENB, 0x0100}, + /* Recommended values for image quality, sensor Rev 1 */ + {MT9P012_16BIT, 0x3088, 0x6FFB}, + {MT9P012_16BIT, 0x308E, 0x2020}, + {MT9P012_16BIT, 0x309E, 0x4400}, + {MT9P012_16BIT, 0x30D4, 0x9080}, + {MT9P012_16BIT, 0x3126, 0x00FF}, + {MT9P012_16BIT, 0x3154, 0x1482}, + {MT9P012_16BIT, 0x3158, 0x97C7}, + {MT9P012_16BIT, 0x315A, 0x97C6}, + {MT9P012_16BIT, 0x3162, 0x074C}, + {MT9P012_16BIT, 0x3164, 0x0756}, + {MT9P012_16BIT, 0x3166, 0x0760}, + {MT9P012_16BIT, 0x316E, 0x8488}, + {MT9P012_16BIT, 0x3172, 0x0003}, + {MT9P012_16BIT, 0x30EA, 0x3F06}, + {MT9P012_8BIT, REG_GROUPED_PAR_HOLD, 0x00}, /* update all at once */ + {MT9P012_TOK_TERM, 0, 0} +}; + +/* + * mt9p012 register configuration for all combinations of pixel format and + * image size + */ + /* 4X BINNING+SCALING */ +const static struct mt9p012_reg enter_video_216_15fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 126}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, + .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_VIDEO_HEIGHT_4X_BINN_SCALED}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 574}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 2712}, + /* 0x10/0x30 = 0.3333 */ + {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0030}, + /* enable scaler */ + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0002}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_216}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} + }; + + /* Video mode, 4x binning + scaling, range 16 - 30 fps */ +const static struct mt9p012_reg enter_video_216_30fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3}, + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 192}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_VIDEO_WIDTH_4X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_VIDEO_HEIGHT_4X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1374}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3712}, + /* 0x10/0x30 = 0.3333 */ + {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0030}, + /* enable scaler */ + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0002}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_216_30FPS}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} + }; + + + /*Video mode, 4x binning: 648 x 486, range 8 - 15 fps*/ +const static struct mt9p012_reg enter_video_648_15fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 126}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_VIDEO_WIDTH_4X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_VIDEO_HEIGHT_4X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 574}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 2712}, + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_648}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + + /* Video mode, 4x binning: 648 x 486, range 16 - 30 fps */ +const static struct mt9p012_reg enter_video_648_30fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3}, + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 192}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_VIDEO_WIDTH_4X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_VIDEO_HEIGHT_4X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1374}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3712}, + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_648_30FPS}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + + /* Video mode, scaler off: 1296 x 972, range 11 - 21 fps*/ +const static struct mt9p012_reg enter_video_1296_15fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3}, + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 134}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_VIDEO_WIDTH_2X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_VIDEO_HEIGHT_2X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2597}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1949}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x046C}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1061}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3360}, + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_1296}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + + /* YUV (YCbCr) VGA */ +const static struct mt9p012_reg enter_video_1296_30fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 1}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3}, + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 134}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_VIDEO_WIDTH_2X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_VIDEO_HEIGHT_2X_BINN}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2597}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1949}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x046C}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1061}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3360}, + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_1296}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + +const static struct mt9p012_reg enter_image_mode_3MP_10fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 4}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 1}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 5}, + /* 10 fps */ + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 184}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_IMAGE_WIDTH_MIN}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_IMAGE_HEIGHT_MIN}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2599}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1951}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x0024}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 882}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 2056}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 5372}, + /* 0x10/0x14 = 0.80 */ + {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0014}, + /* enable scaler */ + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0002}, + {.length = MT9P012_16BIT, .reg = REG_TEST_PATTERN, + .val = MT9P012_TST_PAT}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_3MP}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + +/* Image mode, 5 MP @ 10 fps */ +const static struct mt9p012_reg enter_image_mode_5MP_10fps[] = { + /* stream off */ + {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00}, + {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100}, + /* hold */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01}, + {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 4}, + {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 1}, + {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 5}, + /* 10 fps */ + {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 184}, + {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1}, + {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805}, + {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE, + .val = MT9P012_IMAGE_WIDTH_MAX}, + {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE, + .val = MT9P012_IMAGE_HEIGHT_MAX}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8}, + {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2599}, + {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1951}, + {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x0024}, + {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 882}, + {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 2056}, + {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 5372}, + {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0000}, + /* disable scaler */ + {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000}, + {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, + .val = MT9P012_COARSE_INT_TIME_5MP}, + /* update */ + {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00}, + {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0} +}; + +static u32 min_exposure_time; +static u32 max_exposure_time; +static u32 pix_clk_freq; + +/* Structure to set frame rate */ +static struct mt9p012_reg set_fps[2]; + +/** + * struct mt9p012_pll_settings - struct for storage of sensor pll values + * @vt_pix_clk_div: vertical pixel clock divider + * @vt_sys_clk_div: veritcal system clock divider + * @pre_pll_div: pre pll divider + * @fine_int_tm: fine resolution interval time + * @frame_lines: number of lines in frame + * @line_len: number of pixels in line + * @min_pll: minimum pll multiplier + * @max_pll: maximum pll multiplier + */ +const static struct mt9p012_pll_settings all_pll_settings[] = { + /* PLL_5MP */ + {.vt_pix_clk_div = 4, .vt_sys_clk_div = 1, .pre_pll_div = 5, + .fine_int_tm = 882, .frame_lines = 2056, .line_len = 5372, + .min_pll = 160, .max_pll = 200}, + /* PLL_3MP */ + {.vt_pix_clk_div = 4, .vt_sys_clk_div = 1, .pre_pll_div = 5, + .fine_int_tm = 882, .frame_lines = 2056, .line_len = 5372, + .min_pll = 160, .max_pll = 200}, + /* PLL_1296_15FPS */ + {.vt_pix_clk_div = 5, .vt_sys_clk_div = 2, .pre_pll_div = 3, + .fine_int_tm = 1794, .frame_lines = 1061, .line_len = 3360, + .min_pll = 96, .max_pll = 190}, + /* PLL_1296_30FPS */ + {.vt_pix_clk_div = 5, .vt_sys_clk_div = 1, .pre_pll_div = 3, + .fine_int_tm = 1794, .frame_lines = 1061, .line_len = 3360, + .min_pll = 96, .max_pll = 150}, + /* PLL_648_15FPS */ + {.vt_pix_clk_div = 8, .vt_sys_clk_div = 2, .pre_pll_div = 2, + .fine_int_tm = 1794, .frame_lines = 574, .line_len = 2712, + .min_pll = 92, .max_pll = 128}, + /* PLL_648_30FPS */ + {.vt_pix_clk_div = 5, .vt_sys_clk_div = 2, .pre_pll_div = 3, + .fine_int_tm = 1794, .frame_lines = 1374, .line_len = 3712, + .min_pll = 96, .max_pll = 192}, + /* PLL_216_15FPS */ + {.vt_pix_clk_div = 8, .vt_sys_clk_div = 2, .pre_pll_div = 2, + .fine_int_tm = 1794, .frame_lines = 574, .line_len = 2712, + .min_pll = 92, .max_pll = 126}, + /* PLL_216_30FPS */ + {.vt_pix_clk_div = 5, .vt_sys_clk_div = 2, .pre_pll_div = 3, + .fine_int_tm = 1794, .frame_lines = 1374, .line_len = 3712, + .min_pll = 96, .max_pll = 192} +}; + +static enum mt9p012_pll_type current_pll_video; + +const static struct mt9p012_reg + *mt9p012_reg_init[MT9P012_NUM_FPS][MT9P012_NUM_IMAGE_SIZES] = { + { + enter_video_216_15fps, + enter_video_648_15fps, + enter_video_1296_15fps, + enter_image_mode_3MP_10fps, + enter_image_mode_5MP_10fps + }, + { + enter_video_216_30fps, + enter_video_648_30fps, + enter_video_1296_30fps, + enter_image_mode_3MP_10fps, + enter_image_mode_5MP_10fps + }, +}; + +/** + * struct vcontrol - Video controls + * @v4l2_queryctrl: V4L2 VIDIOC_QUERYCTRL ioctl structure + * @current_value: current value of this control + */ +static struct vcontrol { + struct v4l2_queryctrl qc; + int current_value; +} video_control[] = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = MT9P012_DEF_MIN_EXPOSURE, + .maximum = MT9P012_DEF_MAX_EXPOSURE, + .step = MT9P012_EXPOSURE_STEP, + .default_value = MT9P012_DEF_EXPOSURE, + }, + .current_value = MT9P012_DEF_EXPOSURE, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = MT9P012_MIN_GAIN, + .maximum = MT9P012_MAX_GAIN, + .step = MT9P012_GAIN_STEP, + .default_value = MT9P012_DEF_GAIN, + }, + .current_value = MT9P012_DEF_GAIN, + } +}; + +/** + * find_vctrl - Finds the requested ID in the video control structure array + * @id: ID of control to search the video control array for + * + * Returns the index of the requested ID from the control structure array + */ +static int find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--) + if (video_control[i].qc.id == id) + break; + if (i < 0) + i = -EINVAL; + return i; +} + +/** + * mt9p012_read_reg - Read a value from a register in an mt9p012 sensor device + * @client: i2c driver client structure + * @data_length: length of data to be read + * @reg: register address / offset + * @val: stores the value that gets read + * + * Read a value from a register in an mt9p012 sensor device. + * The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int mt9p012_read_reg(struct i2c_client *client, u16 data_length, + u16 reg, u32 *val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[4]; + + if (!client->adapter) + return -ENODEV; + if (data_length != MT9P012_8BIT && data_length != MT9P012_16BIT + && data_length != MT9P012_32BIT) + return -EINVAL; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8);; + data[1] = (u8) (reg & 0xff); + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) { + msg->len = data_length; + msg->flags = I2C_M_RD; + err = i2c_transfer(client->adapter, msg, 1); + } + if (err >= 0) { + *val = 0; + /* high byte comes first */ + if (data_length == MT9P012_8BIT) + *val = data[0]; + else if (data_length == MT9P012_16BIT) + *val = data[1] + (data[0] << 8); + else + *val = data[3] + (data[2] << 8) + + (data[1] << 16) + (data[0] << 24); + return 0; + } + v4l_dbg(1, debug, client, "read from offset 0x%x error %d", reg, err); + return err; +} +/** + * mt9p012_write_reg - Write a value to a register in an mt9p012 sensor device + * @client: i2c driver client structure + * @data_length: length of data to be read + * @reg: register address / offset + * @val: value to be written to specified register + * + * Write a value to a register in an mt9p012 sensor device. + * Returns zero if successful, or non-zero otherwise. + */ +static int mt9p012_write_reg(struct i2c_client *client, u16 data_length, + u16 reg, u32 val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[6]; + int retry = 0; + + if (!client->adapter) + return -ENODEV; + + if (data_length != MT9P012_8BIT && data_length != MT9P012_16BIT + && data_length != MT9P012_32BIT) + return -EINVAL; + +again: + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2 + data_length; + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8);; + data[1] = (u8) (reg & 0xff); + + if (data_length == MT9P012_8BIT) + data[2] = (u8) (val & 0xff); + else if (data_length == MT9P012_16BIT) { + data[2] = (u8) (val >> 8); + data[3] = (u8) (val & 0xff); + } else { + data[2] = (u8) (val >> 24); + data[3] = (u8) (val >> 16); + data[4] = (u8) (val >> 8); + data[5] = (u8) (val & 0xff); + } + + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) + return 0; + + v4l_dbg(1, debug, client, "wrote 0x%x to offset 0x%x error %d", val, + reg, err); + if (retry <= MT9P012_I2C_RETRY_COUNT) { + v4l_warn(client, "retry ... %d", retry); + retry++; + mdelay(20); + goto again; + } + return err; +} + +/** + * mt9p012_write_regs - Initializes a list of MT9P012 registers + * @client: i2c driver client structure + * @reglist: list of registers to be written + * + * Initializes a list of MT9P012 registers. The list of registers is + * terminated by MT9P012_TOK_TERM. + */ +static int mt9p012_write_regs(struct i2c_client *client, + const struct mt9p012_reg reglist[]) +{ + int err; + const struct mt9p012_reg *next = reglist; + + for (; next->length != MT9P012_TOK_TERM; next++) { + if (next->length == MT9P012_TOK_DELAY) { + mdelay(next->val); + continue; + } + + err = mt9p012_write_reg(client, next->length, + next->reg, next->val); + if (err) + return err; + } + return 0; +} + +/** + * mt9p012_calc_pll - Calculate PLL settings based on input image size + * @isize: enum value corresponding to image size + * @xclk: xclk value (calculate by mt9p012sensor_calc_xclk()) + * @sensor: pointer to sensor device information structure + * + * Calculates sensor PLL related settings (scaler, fps, pll_multiplier, + * pix_clk_freq, min_exposure_time, max_exposure_time) based on input + * image size. It then applies the fps register settings based on + * these calculations. + */ +static int mt9p012_calc_pll(enum mt9p012_image_size isize, unsigned long xclk, + struct mt9p012_sensor *sensor) +{ + int err = 0, row = 1, i = 0; + unsigned int vt_pix_clk; + unsigned int pll_multiplier; + unsigned int exposure_factor, pix_clk_scaled; + struct i2c_client *client = to_i2c_client(sensor->dev); + struct vcontrol *lvc; + + /* Greater than 1296x972 + 1. Scaler is 0 + 2. fps is 10 + 3. Apply image mode settings + 4. Turn Streaming ON. + 5. Exit + */ + if (isize > MT9P012_BIN2X) { + /* Burst Mode */ + sensor->scaler = 0; + sensor->fps = 10; + current_pll_video = MT9P012_PLL_5MP; + return 0; + } + + /* Greater than 648X486 case + 1. Scaler is 0 + 2. If fps>21 then choose PLL for 30 + 3. If fps<21 then choose PLL for 15 + + Greater than 216X162 case + 1. Scaler is 1 + 2. If fps>15 then choose PLL for 30 + 3. If fps<15 then choose PLL for 15 + + Greater than 0 to 216x162 + 1. Scaler is 2. + 2. If fps>15 then choose PLL for 30 + 3. If fps<15 then choose PLL for 15 + */ + + if (isize > MT9P012_BIN4X) { + sensor->scaler = 0; + if (sensor->fps > 21) + current_pll_video = MT9P012_PLL_1296_30FPS; + else + current_pll_video = MT9P012_PLL_1296_15FPS; + } else if (isize > MT9P012_BIN4XSCALE) { + sensor->scaler = 1; + if (sensor->fps > 15) + current_pll_video = MT9P012_PLL_648_30FPS; + else + current_pll_video = MT9P012_PLL_648_15FPS; + } else { + sensor->scaler = 2; + if (sensor->fps > 15) + current_pll_video = MT9P012_PLL_216_30FPS; + else + current_pll_video = MT9P012_PLL_216_15FPS; + } + + /* Row adjustment */ + if (sensor->scaler && (sensor->fps < 16)) + row = 2; /* Adjustment when using 4x binning and 12 MHz clk */ + + /* Calculate the PLL, set fps register */ + vt_pix_clk = sensor->fps * + all_pll_settings[current_pll_video].frame_lines * + all_pll_settings[current_pll_video].line_len; + + pll_multiplier = + (((vt_pix_clk + * all_pll_settings[current_pll_video].vt_pix_clk_div + * all_pll_settings[current_pll_video].vt_sys_clk_div + * row) / xclk) + * all_pll_settings[current_pll_video].pre_pll_div) + 1; + + if (pll_multiplier < all_pll_settings[current_pll_video].min_pll) + pll_multiplier = all_pll_settings[current_pll_video].min_pll; + else if (pll_multiplier > all_pll_settings[current_pll_video].max_pll) + pll_multiplier = all_pll_settings[current_pll_video].max_pll; + + pix_clk_freq = (xclk / + (all_pll_settings[current_pll_video].pre_pll_div + * all_pll_settings[current_pll_video].vt_pix_clk_div + * all_pll_settings[current_pll_video].vt_sys_clk_div + * row)) * pll_multiplier; + min_exposure_time = (all_pll_settings[current_pll_video].fine_int_tm + * 1000000 / pix_clk_freq) + 1; + exposure_factor = (all_pll_settings[current_pll_video].frame_lines - 1) + * all_pll_settings[current_pll_video].line_len; + exposure_factor += all_pll_settings[current_pll_video].fine_int_tm; + exposure_factor *= 100; + pix_clk_scaled = pix_clk_freq / 100; + max_exposure_time = (exposure_factor / pix_clk_scaled) * 100; + + /* Apply the fps settings */ + set_fps[0].length = MT9P012_16BIT; + set_fps[0].reg = REG_PLL_MULTIPLIER; + set_fps[0].val = pll_multiplier; + set_fps[1].length = MT9P012_TOK_TERM; + set_fps[1].reg = 0; + set_fps[1].val = 0; + + /* Update min/max for query control */ + i = find_vctrl(V4L2_CID_EXPOSURE); + if (i >= 0) { + lvc = &video_control[i]; + lvc->qc.minimum = min_exposure_time; + lvc->qc.maximum = max_exposure_time; + } + + err = mt9p012_write_regs(client, set_fps); + return err; +} + +/** + * mt9p012_calc_size - Find the best match for a requested image capture size + * @width: requested image width in pixels + * @height: requested image height in pixels + * + * Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + */ +static enum mt9p012_image_size mt9p012_calc_size(unsigned int width, + unsigned int height) +{ + enum mt9p012_image_size isize; + unsigned long pixels = width * height; + + for (isize = MT9P012_BIN4XSCALE; isize <= MT9P012_FIVE_MP; isize++) { + if (mt9p012_sizes[isize].height * + mt9p012_sizes[isize].width >= pixels) { + /* To improve image quality in VGA */ + if (pixels > MT9P012_CIF_PIXELS && + isize == MT9P012_BIN4X) + isize = MT9P012_BIN2X; + else { + if ((pixels > MT9P012_QQVGA_PIXELS) && + (isize == MT9P012_BIN4XSCALE)) + isize = MT9P012_BIN4X; + } + return isize; + } + } + + return MT9P012_FIVE_MP; +} + +/** + * mt9p012_find_isize - Find the best match for a requested image capture size + * @width: requested image width in pixels + * @height: requested image height in pixels + * + * Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + */ +static enum mt9p012_image_size mt9p012_find_isize(unsigned int width) +{ + enum mt9p012_image_size isize; + + for (isize = MT9P012_BIN4XSCALE; isize <= MT9P012_FIVE_MP; isize++) { + if (mt9p012_sizes[isize].width >= width) + break; + } + + return isize; +} +/** + * mt9p012_find_fps_index - Find the best fps range match for a + * requested frame rate + * @fps: desired frame rate + * @isize: enum value corresponding to image size + * + * Find the best match for a requested frame rate. The best match + * is chosen between two fps ranges (11 - 15 and 16 - 30 fps) depending on + * the image size. For image sizes larger than BIN2X, frame rate is fixed + * at 10 fps. + */ +static unsigned int mt9p012_find_fps_index(unsigned int fps, + enum mt9p012_image_size isize) +{ + unsigned int index = MT9P012_FPS_LOW_RANGE; + + if (isize > MT9P012_BIN4X) { + if (fps > 21) + index = MT9P012_FPS_HIGH_RANGE; + } else { + if (fps > 15) + index = MT9P012_FPS_HIGH_RANGE; + } + + return index; +} + +/** + * mt9p012_calc_xclk - Calculate the required xclk frequency + * @c: i2c client driver structure + * + * Given the image capture format in pix, the nominal frame period in + * timeperframe, calculate and return the required xclk frequency + */ +static unsigned long mt9p012_calc_xclk(struct i2c_client *c) +{ + struct mt9p012_sensor *sensor = i2c_get_clientdata(c); + struct v4l2_fract *timeperframe = &sensor->timeperframe; + struct v4l2_pix_format *pix = &sensor->pix; + + if (timeperframe->numerator == 0 || + timeperframe->denominator == 0) { + /* supply a default nominal_timeperframe */ + timeperframe->numerator = 1; + timeperframe->denominator = MT9P012_DEF_FPS; + } + + sensor->fps = timeperframe->denominator / timeperframe->numerator; + if (sensor->fps < MT9P012_MIN_FPS) + sensor->fps = MT9P012_MIN_FPS; + else if (sensor->fps > MT9P012_MAX_FPS) + sensor->fps = MT9P012_MAX_FPS; + + timeperframe->numerator = 1; + timeperframe->denominator = sensor->fps; + + if ((pix->width <= MT9P012_VIDEO_WIDTH_4X_BINN) && (sensor->fps > 15)) + return MT9P012_XCLK_NOM_2; + + return MT9P012_XCLK_NOM_1; +} + +/** + * mt9p012_configure - Configure the mt9p012 for the specified image mode + * @s: pointer to standard V4L2 device structure + * + * Configure the mt9p012 for a specified image size, pixel format, and frame + * period. xclk is the frequency (in Hz) of the xclk input to the mt9p012. + * fper is the frame period (in seconds) expressed as a fraction. + * Returns zero if successful, or non-zero otherwise. + * The actual frame period is returned in fper. + */ +static int mt9p012_configure(struct v4l2_int_device *s) +{ + struct mt9p012_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &sensor->pix; + struct i2c_client *client = to_i2c_client(sensor->dev); + enum mt9p012_image_size isize; + unsigned int fps_index; + int err; + + isize = mt9p012_find_isize(pix->width); + + /* common register initialization */ + err = mt9p012_write_regs(client, mt9p012_common); + if (err) + return err; + + fps_index = mt9p012_find_fps_index(sensor->fps, isize); + + /* configure image size and pixel format */ + err = mt9p012_write_regs(client, mt9p012_reg_init[fps_index][isize]); + if (err) + return err; + + /* configure frame rate */ + err = mt9p012_calc_pll(isize, sensor->xclk_current, sensor); + if (err) + return err; + + /* configure streaming ON */ + err = mt9p012_write_regs(client, stream_on_list); + + return err; +} + +/** + * mt9p012_detect - Detect if an mt9p012 is present, and if so which revision + * @client: pointer to the i2c client driver structure + * + * Detect if an mt9p012 is present, and if so which revision. + * A device is considered to be detected if the manufacturer ID (MIDH and MIDL) + * and the product ID (PID) registers match the expected values. + * Any value of the version ID (VER) register is accepted. + * Here are the version numbers we know about: + * 0x48 --> mt9p012 Revision 1 or mt9p012 Revision 2 + * 0x49 --> mt9p012 Revision 3 + * Returns a negative error number if no device is detected, or the + * non-negative value of the version ID register if a device is detected. + */ +static int mt9p012_detect(struct i2c_client *client) +{ + u32 model_id, mfr_id, rev; + + if (!client) + return -ENODEV; + + if (mt9p012_read_reg(client, MT9P012_16BIT, REG_MODEL_ID, &model_id)) + return -ENODEV; + if (mt9p012_read_reg(client, MT9P012_8BIT, REG_MANUFACTURER_ID, + &mfr_id)) + return -ENODEV; + if (mt9p012_read_reg(client, MT9P012_8BIT, REG_REVISION_NUMBER, &rev)) + return -ENODEV; + + dev_info(&client->dev, "model id detected 0x%x mfr 0x%x\n", model_id, + mfr_id); + if ((model_id != MT9P012_MOD_ID) || (mfr_id != MT9P012_MFR_ID)) { + /* We didn't read the values we expected, so + * this must not be an MT9P012. + */ + dev_warn(&client->dev, "model id mismatch 0x%x mfr 0x%x\n", + model_id, mfr_id); + + return -ENODEV; + } + return 0; + +} + +/** + * mt9p012_set_exposure_time - sets exposure time per input value + * @exp_time: exposure time to be set on device + * @s: pointer to standard V4L2 device structure + * @lvc: pointer to V4L2 exposure entry in video_controls array + * + * If the requested exposure time is within the allowed limits, the HW + * is configured to use the new exposure time, and the video_controls + * array is updated with the new current value. + * The function returns 0 upon success. Otherwise an error code is + * returned. + */ +static int mt9p012_set_exposure_time(u32 exp_time, struct v4l2_int_device *s, + struct vcontrol *lvc) +{ + int err; + struct mt9p012_sensor *sensor = s->priv; + struct i2c_client *client = to_i2c_client(sensor->dev); + u32 coarse_int_time = 0; + + if ((exp_time < min_exposure_time) || + (exp_time > max_exposure_time)) { + dev_err(&client->dev, "Exposure time not within the " + "legal range.\n"); + dev_err(&client->dev, "Min time %d us Max time %d us", + min_exposure_time, max_exposure_time); + return -EINVAL; + } + coarse_int_time = ((((exp_time / 10) * (pix_clk_freq / 1000)) / 1000) - + (all_pll_settings[current_pll_video].fine_int_tm / 10)) / + (all_pll_settings[current_pll_video].line_len / 10); + + dev_dbg(&client->dev, "coarse_int_time calculated = %d\n", + coarse_int_time); + + set_exposure_time[MT9P012_COARSE_INT_TIME_INDEX].val = coarse_int_time; + err = mt9p012_write_regs(client, set_exposure_time); + + if (err) + dev_err(&client->dev, "Error setting exposure time %d\n", + err); + else + lvc->current_value = exp_time; + + return err; +} + +/** + * mt9p012_set_gain - sets sensor analog gain per input value + * @gain: analog gain value to be set on device + * @s: pointer to standard V4L2 device structure + * @lvc: pointer to V4L2 analog gain entry in video_controls array + * + * If the requested analog gain is within the allowed limits, the HW + * is configured to use the new gain value, and the video_controls + * array is updated with the new current value. + * The function returns 0 upon success. Otherwise an error code is + * returned. + */ +static int mt9p012_set_gain(u16 gain, struct v4l2_int_device *s, + struct vcontrol *lvc) +{ + int err; + struct mt9p012_sensor *sensor = s->priv; + struct i2c_client *client = to_i2c_client(sensor->dev); + + if ((gain < MT9P012_MIN_GAIN) || (gain > MT9P012_MAX_GAIN)) { + dev_err(&client->dev, "Gain not within the legal range"); + return -EINVAL; + } + set_analog_gain[MT9P012_GAIN_INDEX].val = gain; + err = mt9p012_write_regs(client, set_analog_gain); + if (err) { + dev_err(&client->dev, "Error setting gain.%d", err); + return err; + } else + lvc->current_value = gain; + + return err; +} + +/** + * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the video_control[] array. Otherwise, returns -EINVAL if the + * control is not supported. + */ +static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc) +{ + int i; + + i = find_vctrl(qc->id); + if (i == -EINVAL) + qc->flags = V4L2_CTRL_FLAG_DISABLED; + + if (i < 0) + return -EINVAL; + + *qc = video_control[i].qc; + return 0; +} + +/** + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + struct vcontrol *lvc; + int i; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_EXPOSURE: + vc->value = lvc->current_value; + break; + case V4L2_CID_GAIN: + vc->value = lvc->current_value; + break; + } + + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = -EINVAL; + int i; + struct vcontrol *lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_EXPOSURE: + retval = mt9p012_set_exposure_time(vc->value, s, lvc); + break; + case V4L2_CID_GAIN: + retval = mt9p012_set_gain(vc->value, s, lvc); + break; + } + + return retval; +} + +/** + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + default: + return -EINVAL; + } + + fmt->flags = mt9p012_formats[index].flags; + strlcpy(fmt->description, mt9p012_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = mt9p012_formats[index].pixelformat; + + return 0; +} + +/** + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + enum mt9p012_image_size isize; + int ifmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct mt9p012_sensor *sensor = s->priv; + struct v4l2_pix_format *pix2 = &sensor->pix; + + isize = mt9p012_calc_size(pix->width, pix->height); + + pix->width = mt9p012_sizes[isize].width; + pix->height = mt9p012_sizes[isize].height; + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (pix->pixelformat == mt9p012_formats[ifmt].pixelformat) + break; + } + if (ifmt == NUM_CAPTURE_FORMATS) + ifmt = 0; + pix->pixelformat = mt9p012_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + pix->colorspace = V4L2_COLORSPACE_JPEG; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_RGB555X: + default: + pix->colorspace = V4L2_COLORSPACE_SRGB; + break; + } + *pix2 = *pix; + return 0; +} + +/** + * ioctl_s_fmt_cap - V4L2 sensor interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct mt9p012_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + int rval; + + rval = ioctl_try_fmt_cap(s, f); + if (!rval) + sensor->pix = *pix; + + return rval; +} + +/** + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct mt9p012_sensor *sensor = s->priv; + f->fmt.pix = sensor->pix; + + return 0; +} + +/** + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct mt9p012_sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = sensor->timeperframe; + + return 0; +} + +/** + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct mt9p012_sensor *sensor = s->priv; + struct i2c_client *client = to_i2c_client(sensor->dev); + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + + sensor->timeperframe = *timeperframe; + sensor->xclk_current = mt9p012_calc_xclk(client); + *timeperframe = sensor->timeperframe; + + return 0; +} + +/** + * ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold sensor's private data address + * + * Returns device's (sensor's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct mt9p012_sensor *sensor = s->priv; + + return sensor->pdata->priv_data_set(s, p); +} + +/** + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the sensor device (call mt9p012_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * mt9p012 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct mt9p012_sensor *sensor = s->priv; + struct i2c_client *client = to_i2c_client(sensor->dev); + int err; + + err = mt9p012_detect(client); + if (err < 0) { + dev_err(&client->dev, "Unable to detect sensor\n"); + sensor->detected = 0; + return err; + } + sensor->detected = 1; + sensor->ver = err; + dev_dbg(&client->dev, "Chip version 0x%02x detected\n", sensor->ver); + + return 0; +} +/** + * ioctl_enum_framesizes - V4L2 sensor if handler for vidioc_int_enum_framesizes + * @s: pointer to standard V4L2 device structure + * @frms: pointer to standard V4L2 framesizes enumeration structure + * + * Returns possible framesizes depending on choosen pixel format + **/ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *frms) +{ + int ifmt; + + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (frms->pixel_format == mt9p012_formats[ifmt].pixelformat) + break; + } + /* Is requested pixelformat not found on sensor? */ + if (ifmt == NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Do we already reached all discrete framesizes? */ + if (frms->index >= 5) + return -EINVAL; + + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = mt9p012_sizes[frms->index].width; + frms->discrete.height = mt9p012_sizes[frms->index].height; + + return 0; +} + +const struct v4l2_fract mt9p012_frameintervals[] = { + { .numerator = 1, .denominator = 11 }, + { .numerator = 1, .denominator = 15 }, + { .numerator = 1, .denominator = 20 }, + { .numerator = 1, .denominator = 25 }, + { .numerator = 1, .denominator = 30 }, +}; + +static int ioctl_enum_frameintervals(struct v4l2_int_device *s, + struct v4l2_frmivalenum *frmi) +{ + int ifmt; + + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (frmi->pixel_format == mt9p012_formats[ifmt].pixelformat) + break; + } + /* Is requested pixelformat not found on sensor? */ + if (ifmt == NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Do we already reached all discrete framesizes? */ + + if (((frmi->width == mt9p012_sizes[4].width) && + (frmi->height == mt9p012_sizes[4].height)) || + ((frmi->width == mt9p012_sizes[3].width) && + (frmi->height == mt9p012_sizes[3].height))) { + /* FIXME: The only frameinterval supported by 5MP and 3MP + * capture sizes is 1/11 fps + */ + if (frmi->index != 0) + return -EINVAL; + } else { + if (frmi->index >= 5) + return -EINVAL; + } + + frmi->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frmi->discrete.numerator = + mt9p012_frameintervals[frmi->index].numerator; + frmi->discrete.denominator = + mt9p012_frameintervals[frmi->index].denominator; + + return 0; +} + +/** + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power new_power) +{ + struct mt9p012_sensor *sensor = s->priv; + struct i2c_client *c = to_i2c_client(sensor->dev); + int rval = 0; + + switch (new_power) { + case V4L2_POWER_ON: + rval = sensor->pdata->set_xclk(s, sensor->xclk_current); + if (rval == -EINVAL) + break; + rval = sensor->pdata->power_set(s, V4L2_POWER_ON); + if (rval) + break; + + if (sensor->detected) + mt9p012_configure(s); + else { + rval = ioctl_dev_init(s); + if (rval) + goto err_on; + } + break; + case V4L2_POWER_OFF: +err_on: + rval = sensor->pdata->power_set(s, V4L2_POWER_OFF); + sensor->pdata->set_xclk(s, 0); + break; + case V4L2_POWER_STANDBY: + if (sensor->detected) + mt9p012_write_regs(c, stream_off_list); + rval = sensor->pdata->power_set(s, V4L2_POWER_STANDBY); + sensor->pdata->set_xclk(s, 0); + break; + default: + return -EINVAL; + } + + return rval; +} + +static struct v4l2_int_ioctl_desc mt9p012_ioctl_desc[] = { + { .num = vidioc_int_enum_framesizes_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_framesizes }, + { .num = vidioc_int_enum_frameintervals_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_frameintervals }, + { .num = vidioc_int_dev_init_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_init }, + { .num = vidioc_int_dev_exit_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_exit }, + { .num = vidioc_int_s_power_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_power }, + { .num = vidioc_int_g_priv_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_priv }, + { .num = vidioc_int_init_num, + .func = (v4l2_int_ioctl_func *)ioctl_init }, + { .num = vidioc_int_enum_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, + { .num = vidioc_int_try_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, + { .num = vidioc_int_g_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, + { .num = vidioc_int_s_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, + { .num = vidioc_int_g_parm_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_parm }, + { .num = vidioc_int_s_parm_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_parm }, + { .num = vidioc_int_queryctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_queryctrl }, + { .num = vidioc_int_g_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { .num = vidioc_int_s_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_ctrl }, +}; + +static struct v4l2_int_slave mt9p012_slave = { + .ioctls = mt9p012_ioctl_desc, + .num_ioctls = ARRAY_SIZE(mt9p012_ioctl_desc), +}; + +static struct v4l2_int_device mt9p012_int_device = { + .module = THIS_MODULE, + .name = DRIVER_NAME, + .type = v4l2_int_type_slave, + .u = { + .slave = &mt9p012_slave, + }, +}; + +/** + * mt9p012_probe - sensor driver i2c probe handler + * @client: i2c driver client device structure + * + * Register sensor as an i2c client device and V4L2 + * device. + */ +static int mt9p012_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mt9p012_sensor *sensor; + struct mt9p012_platform_data *pdata; + int err; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + /* Don't keep pointer to platform data, copy elements instead */ + sensor->pdata = kzalloc(sizeof(*sensor->pdata), GFP_KERNEL); + if (!sensor->pdata) { + err = -ENOMEM; + goto on_err1; + } + + sensor->pdata->power_set = pdata->power_set; + sensor->pdata->set_xclk = pdata->set_xclk; + sensor->pdata->priv_data_set = pdata->priv_data_set; + + /* Set sensor default values */ + sensor->timeperframe.numerator = 1; + sensor->timeperframe.denominator = 15; + sensor->xclk_current = MT9P012_XCLK_NOM_1; + sensor->pix.width = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED; + sensor->pix.height = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED; + sensor->pix.pixelformat = V4L2_PIX_FMT_SGRBG10; + + sensor->v4l2_int_device = &mt9p012_int_device; + sensor->v4l2_int_device->priv = sensor; + sensor->dev = &client->dev; + + i2c_set_clientdata(client, sensor); + + err = v4l2_int_device_register(sensor->v4l2_int_device); + if (err) { + goto on_err2; + } + + return 0; +on_err2: + i2c_set_clientdata(client, NULL); + kfree(sensor->pdata); +on_err1: + kfree(sensor); + return err; +} + +/** + * mt9p012_remove - sensor driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister sensor as an i2c client device and V4L2 + * device. Complement of mt9p012_probe(). + */ +static int mt9p012_remove(struct i2c_client *client) +{ + struct mt9p012_sensor *sensor = i2c_get_clientdata(client); + + v4l2_int_device_unregister(sensor->v4l2_int_device); + i2c_set_clientdata(client, NULL); + kfree(sensor->pdata); + kfree(sensor); + + return 0; +} + +static const struct i2c_device_id mt9p012_id[] = { + { DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mt9p012_id); + +static struct i2c_driver mt9p012_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mt9p012_probe, + .remove = mt9p012_remove, + .id_table = mt9p012_id, +}; + +/** + * mt9p012sensor_init - sensor driver module_init handler + * + * Registers driver as an i2c client driver. Returns 0 on success, + * error code otherwise. + */ +static int __init mt9p012_init(void) +{ + return i2c_add_driver(&mt9p012_i2c_driver); +} +module_init(mt9p012_init); + +/** + * mt9p012sensor_cleanup - sensor driver module_exit handler + * + * Unregisters/deletes driver as an i2c client driver. + * Complement of mt9p012sensor_init. + */ +static void __exit mt9p012_cleanup(void) +{ + i2c_del_driver(&mt9p012_i2c_driver); +} +module_exit(mt9p012_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("mt9p012 camera sensor driver"); diff --git a/drivers/media/video/mt9p012_regs.h b/drivers/media/video/mt9p012_regs.h new file mode 100644 index 00000000000..37d98fe4715 --- /dev/null +++ b/drivers/media/video/mt9p012_regs.h @@ -0,0 +1,60 @@ +#ifndef __VIDEO_MT9P012_REGS_H +#define __VIDEO_MT9P012_REGS_H + +#define REG_MODEL_ID 0x0000 +#define REG_REVISION_NUMBER 0x0002 +#define REG_MANUFACTURER_ID 0x0003 + +#define REG_MODE_SELECT 0x0100 +#define REG_IMAGE_ORIENTATION 0x0101 +#define REG_SOFTWARE_RESET 0x0103 +#define REG_GROUPED_PAR_HOLD 0x0104 + +#define REG_FINE_INT_TIME 0x0200 +#define REG_COARSE_INT_TIME 0x0202 + +#define REG_ANALOG_GAIN_GLOBAL 0x0204 +#define REG_ANALOG_GAIN_GREENR 0x0206 +#define REG_ANALOG_GAIN_RED 0x0208 +#define REG_ANALOG_GAIN_BLUE 0x020A +#define REG_ANALOG_GAIN_GREENB 0x020C +#define REG_DIGITAL_GAIN_GREENR 0x020E +#define REG_DIGITAL_GAIN_RED 0x0210 +#define REG_DIGITAL_GAIN_BLUE 0x0212 +#define REG_DIGITAL_GAIN_GREENB 0x0214 + +#define REG_VT_PIX_CLK_DIV 0x0300 +#define REG_VT_SYS_CLK_DIV 0x0302 +#define REG_PRE_PLL_CLK_DIV 0x0304 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_OP_PIX_CLK_DIV 0x0308 +#define REG_OP_SYS_CLK_DIV 0x030A + +#define REG_FRAME_LEN_LINES 0x0340 +#define REG_LINE_LEN_PCK 0x0342 + +#define REG_X_ADDR_START 0x0344 +#define REG_Y_ADDR_START 0x0346 +#define REG_X_ADDR_END 0x0348 +#define REG_Y_ADDR_END 0x034A +#define REG_X_OUTPUT_SIZE 0x034C +#define REG_Y_OUTPUT_SIZE 0x034E +#define REG_X_ODD_INC 0x0382 +#define REG_Y_ODD_INC 0x0386 + +#define REG_SCALING_MODE 0x0400 +#define REG_SCALE_M 0x0404 +#define REG_SCALE_N 0x0406 + +#define REG_ROW_SPEED 0x3016 +#define REG_RESET_REGISTER 0x301A +#define REG_PIXEL_ORDER 0x3024 +#define REG_READ_MODE 0x3040 + +#define REG_DATAPATH_STATUS 0x306A +#define REG_DATAPATH_SELECT 0x306E + +#define REG_RESERVED_MFR_3064 0x3064 +#define REG_TEST_PATTERN 0x3070 + +#endif /* __VIDEO_MT9P012_REGS_H */ \ No newline at end of file diff --git a/drivers/media/video/omap34xxcam.c b/drivers/media/video/omap34xxcam.c new file mode 100644 index 00000000000..7fcc1b99cd6 --- /dev/null +++ b/drivers/media/video/omap34xxcam.c @@ -0,0 +1,2286 @@ +/* + * omap34xxcam.c + * + * Copyright (C) 2006--2009 Nokia Corporation + * Copyright (C) 2007--2009 Texas Instruments + * + * Contact: Sakari Ailus + * Tuukka Toivonen + * + * Originally based on the OMAP 2 camera driver. + * + * Written by Sakari Ailus + * Tuukka Toivonen + * Sergio Aguirre + * Mohit Jalori + * Sameer Venkatraman + * Leonides Martinez + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap34xxcam.h" +#include "isp/isp.h" + +#define OMAP34XXCAM_VERSION KERNEL_VERSION(0, 0, 0) + +/* global variables */ +static struct omap34xxcam_device *omap34xxcam; + +/* + * + * Sensor handling. + * + */ + +/** + * omap34xxcam_slave_power_set - set slave power state + * @vdev: per-video device data structure + * @power: new power state + */ +static int omap34xxcam_slave_power_set(struct omap34xxcam_videodev *vdev, + enum v4l2_power power, + int mask) +{ + int rval = 0, i = 0; + + BUG_ON(!mutex_is_locked(&vdev->mutex)); + +#ifdef OMAP34XXCAM_POWEROFF_DELAY + vdev->power_state_wish = -1; +#endif + + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + if (vdev->slave[i] == v4l2_int_device_dummy()) + continue; + + if (!(mask & (1 << i)) + || power == vdev->power_state[i]) + continue; + + rval = vidioc_int_s_power(vdev->slave[i], power); + + if (rval && power != V4L2_POWER_OFF) { + power = V4L2_POWER_OFF; + goto out; + } + + vdev->power_state[i] = power; + } + + return 0; + +out: + for (i--; i >= 0; i--) { + if (vdev->slave[i] == v4l2_int_device_dummy()) + continue; + + if (!(mask & (1 << i))) + continue; + + vidioc_int_s_power(vdev->slave[i], power); + vdev->power_state[i] = power; + } + + return rval; +} + +#ifdef OMAP34XXCAM_POWEROFF_DELAY +static void omap34xxcam_slave_power_work(struct work_struct *work) +{ + struct omap34xxcam_videodev *vdev = + container_of(work, struct omap34xxcam_videodev, poweroff_work); + + mutex_lock(&vdev->mutex); + + if (vdev->power_state_wish != -1) + omap34xxcam_slave_power_set(vdev, vdev->power_state_wish, + vdev->power_state_mask); + + mutex_unlock(&vdev->mutex); +} + +static void omap34xxcam_slave_power_timer(unsigned long ptr) +{ + struct omap34xxcam_videodev *vdev = (void *)ptr; + + schedule_work(&vdev->poweroff_work); +} + +/** + * omap34xxcam_slave_power_suggest - delayed power state change + * + * @vdev: per-video device data structure + * @power: new power state + */ +static void omap34xxcam_slave_power_suggest(struct omap34xxcam_videodev *vdev, + enum v4l2_power power, + int mask) +{ + BUG_ON(!mutex_is_locked(&vdev->mutex)); + + del_timer(&vdev->poweroff_timer); + + vdev->power_state_wish = power; + vdev->power_state_mask = mask; + + mod_timer(&vdev->poweroff_timer, jiffies + OMAP34XXCAM_POWEROFF_DELAY); +} +#else /* OMAP34XXCAM_POWEROFF_DELAY */ +#define omap34xxcam_slave_power_suggest(a, b, c) do {} while (0) +#endif /* OMAP34XXCAM_POWEROFF_DELAY */ + +/** + * omap34xxcam_update_vbq - Updates VBQ with completed input buffer + * @vb: ptr. to standard V4L2 video buffer structure + * + * Updates video buffer queue with completed buffer passed as + * input parameter. Also updates ISP H3A timestamp and field count + * statistics. + */ +void omap34xxcam_vbq_complete(struct videobuf_buffer *vb, void *priv) +{ + struct omap34xxcam_fh *fh = priv; + + do_gettimeofday(&vb->ts); + vb->field_count = atomic_add_return(2, &fh->field_count); + + wake_up(&vb->done); +} + +/** + * omap34xxcam_vbq_setup - Calcs size and num of buffs allowed in queue + * @vbq: ptr. to standard V4L2 video buffer queue structure + * @cnt: ptr to location to hold the count of buffers to be in the queue + * @size: ptr to location to hold the size of a frame + * + * Calculates the number of buffers of current image size that can be + * supported by the available capture memory. + */ +static int omap34xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, + unsigned int *size) +{ + struct omap34xxcam_fh *fh = vbq->priv_data; + struct omap34xxcam_videodev *vdev = fh->vdev; + + if (*cnt <= 0) + *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */ + + if (*cnt > VIDEO_MAX_FRAME) + *cnt = VIDEO_MAX_FRAME; + + *size = vdev->pix.sizeimage; + + while (*size * *cnt > fh->vdev->vdev_sensor_config.capture_mem) + (*cnt)--; + + return isp_vbq_setup(vdev->cam->isp, vbq, cnt, size); +} + +/** + * omap34xxcam_vbq_release - Free resources for input VBQ and VB + * @vbq: ptr. to standard V4L2 video buffer queue structure + * @vb: ptr to standard V4L2 video buffer structure + * + * Unmap and free all memory associated with input VBQ and VB, also + * unmap the address in ISP MMU. Reset the VB state. + */ +static void omap34xxcam_vbq_release(struct videobuf_queue *vbq, + struct videobuf_buffer *vb) +{ + struct omap34xxcam_fh *fh = vbq->priv_data; + struct omap34xxcam_videodev *vdev = fh->vdev; + struct device *isp = vdev->cam->isp; + + if (!vbq->streaming) { + isp_vbq_release(isp, vbq, vb); + videobuf_dma_unmap(vbq, videobuf_to_dma(vb)); + videobuf_dma_free(videobuf_to_dma(vb)); + vb->state = VIDEOBUF_NEEDS_INIT; + } + return; +} + + + +/* + * This function is work around for the videobuf_iolock API, + * for User memory allocated with ioremap (VM_IO flag) the API + * get_user_pages fails. + * + * To fulfill this requirement, we have completely ignored VM layer of + * Linux, and configuring the ISP MMU with physical address. + */ +static int omap_videobuf_dma_init_user(struct videobuf_buffer *vb, + unsigned long physp, unsigned long asize) +{ + struct videobuf_dmabuf *dma; + struct scatterlist *sglist; + unsigned long data, first, last; + int i = 0; + + dma = videobuf_to_dma(vb); + data = vb->baddr; + + first = (data & PAGE_MASK) >> PAGE_SHIFT; + last = ((data+asize-1) & PAGE_MASK) >> PAGE_SHIFT; + dma->offset = data & ~PAGE_MASK; + dma->nr_pages = last-first+1; + + dma->direction = DMA_FROM_DEVICE; + + BUG_ON(0 == dma->nr_pages); + /* + * Allocate array of sglen + 1, to add entry of extra page + * for input buffer. Driver always uses 0th buffer as input buffer. + */ + sglist = vmalloc(dma->nr_pages * sizeof(*sglist)); + if (NULL == sglist) + return -ENOMEM; + + sg_init_table(sglist, dma->nr_pages); + + sglist[0].offset = 0; + sglist[0].length = PAGE_SIZE - dma->offset; + sglist[0].dma_address = (dma_addr_t)physp; + physp += sglist[0].length; + /* + * Iterate in a loop for the number of pages + */ + for (i = 1; i < dma->nr_pages; i++) { + sglist[i].offset = 0; + sglist[i].length = PAGE_SIZE; + sglist[i].dma_address = (dma_addr_t)physp; + physp += PAGE_SIZE; + } + dma->sglist = sglist; + dma->sglen = dma->nr_pages; + + return 0; + +} + +/** + * omap34xxcam_vbq_prepare - V4L2 video ops buf_prepare handler + * @vbq: ptr. to standard V4L2 video buffer queue structure + * @vb: ptr to standard V4L2 video buffer structure + * @field: standard V4L2 field enum + * + * Verifies there is sufficient locked memory for the requested + * buffer, or if there is not, allocates, locks and initializes + * it. + */ +static int omap34xxcam_vbq_prepare(struct videobuf_queue *vbq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct omap34xxcam_fh *fh = vbq->priv_data; + struct omap34xxcam_videodev *vdev = fh->vdev; + struct device *isp = vdev->cam->isp; + + int err = 0; + + /* + * Accessing pix here is okay since it's constant while + * streaming is on (and we only get called then). + */ + if (vb->baddr) { + /* This is a userspace buffer. */ + if (vdev->pix.sizeimage > vb->bsize || + vb->baddr != (vb->baddr & ~0x1F)) + /* The buffer isn't big enough. */ + return -EINVAL; + } else { + if (vb->state != VIDEOBUF_NEEDS_INIT + && vdev->pix.sizeimage > vb->bsize) + /* + * We have a kernel bounce buffer that has + * already been allocated. + */ + omap34xxcam_vbq_release(vbq, vb); + } + + vb->size = vdev->pix.bytesperline * vdev->pix.height; + vb->width = vdev->pix.width; + vb->height = vdev->pix.height; + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + struct videobuf_dmabuf *dma; + struct vm_area_struct *vma; + dma = videobuf_to_dma(vb); + vma = find_vma(current->mm, vb->baddr); + if ((vma) && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) { + /* This will catch ioremaped buffers to the kernel. + * It gives two possible scenarios - + * - Driver allocates buffer using either + * dma_alloc_coherent or get_free_pages, + * and maps to user space using + * io_remap_pfn_range/remap_pfn_range + * - Drivers maps memory outside from Linux using + * io_remap + */ + unsigned long physp = 0; + if ((vb->baddr + vb->bsize) > vma->vm_end) { + dev_err(&vdev->vfd->dev, + "User Buffer Allocation:" \ + "err=%lu[%u]\n",\ + (vma->vm_end - vb->baddr), + vb->bsize); + return -ENOMEM; + } + physp = (vma->vm_pgoff << PAGE_SHIFT) + + (vb->baddr - vma->vm_start); + err = omap_videobuf_dma_init_user(vb, physp, vb->bsize); + } else { + err = videobuf_iolock(vbq, vb, NULL); + } + + if (!err) { + /* isp_addr will be stored locally inside isp code */ + err = isp_vbq_prepare(isp, vbq, vb, field); + } + } + + if (!err) + vb->state = VIDEOBUF_PREPARED; + else + omap34xxcam_vbq_release(vbq, vb); + + return err; +} + +/** + * omap34xxcam_vbq_queue - V4L2 video ops buf_queue handler + * @vbq: ptr. to standard V4L2 video buffer queue structure + * @vb: ptr to standard V4L2 video buffer structure + * + * Maps the video buffer to sgdma and through the isp, sets + * the isp buffer done callback and sets the video buffer state + * to active. + */ +static void omap34xxcam_vbq_queue(struct videobuf_queue *vbq, + struct videobuf_buffer *vb) +{ + struct omap34xxcam_fh *fh = vbq->priv_data; + struct omap34xxcam_videodev *vdev = fh->vdev; + struct device *isp = vdev->cam->isp; + + isp_buf_queue(isp, vb, omap34xxcam_vbq_complete, (void *)fh); +} + +static struct videobuf_queue_ops omap34xxcam_vbq_ops = { + .buf_setup = omap34xxcam_vbq_setup, + .buf_prepare = omap34xxcam_vbq_prepare, + .buf_queue = omap34xxcam_vbq_queue, + .buf_release = omap34xxcam_vbq_release, +}; + +/* + * + * IOCTL interface. + * + */ + +/** + * vidioc_querycap - V4L2 query capabilities IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @cap: ptr to standard V4L2 capability structure + * + * Fill in the V4L2 capabliity structure for the camera device + */ +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + + strlcpy(cap->driver, CAM_SHORT_NAME, sizeof(cap->driver)); + strlcpy(cap->card, vdev->vfd->name, sizeof(cap->card)); + cap->version = OMAP34XXCAM_VERSION; + if (vdev->vdev_sensor != v4l2_int_device_dummy()) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +/** + * vidioc_enum_fmt_vid_cap - V4L2 enumerate format capabilities IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @f: ptr to standard V4L2 format description structure + * + * Fills in enumerate format capabilities information for sensor (if SOC + * sensor attached) or ISP (if raw sensor attached). + */ +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + int rval; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + if (vdev->vdev_sensor_mode) + rval = isp_enum_fmt_cap(f); + else if (vdev->vdev_sensor_config.sensor_isp) + rval = vidioc_int_enum_fmt_cap(vdev->vdev_sensor, f); + else + rval = isp_enum_fmt_cap(f); + + return rval; +} + +/** + * vidioc_g_fmt_vid_cap - V4L2 get format capabilities IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @f: ptr to standard V4L2 format structure + * + * Fills in format capabilities for sensor (if SOC sensor attached) or ISP + * (if raw sensor attached). + */ +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + f->fmt.pix = vdev->pix; + mutex_unlock(&vdev->mutex); + + return 0; +} + +static int try_pix_parm(struct omap34xxcam_videodev *vdev, + struct v4l2_pix_format *best_pix_in, + struct v4l2_pix_format *wanted_pix_out, + struct v4l2_fract *best_ival) +{ + int fps; + int fmtd_index; + int rval; + struct v4l2_pix_format best_pix_out; + struct device *isp = vdev->cam->isp; + + if (best_ival->numerator == 0 + || best_ival->denominator == 0) + *best_ival = vdev->vdev_sensor_config.ival_default; + + fps = best_ival->denominator / best_ival->numerator; + + memset(best_pix_in, 0, sizeof(*best_pix_in)); + + best_ival->denominator = 0; + best_pix_out.height = INT_MAX >> 1; + best_pix_out.width = best_pix_out.height; + + for (fmtd_index = 0; ; fmtd_index++) { + int size_index; + struct v4l2_fmtdesc fmtd; + + fmtd.index = fmtd_index; + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rval = vidioc_int_enum_fmt_cap(vdev->vdev_sensor, &fmtd); + if (rval) + break; + dev_dbg(&vdev->vfd->dev, "trying fmt %8.8x (%d)\n", + fmtd.pixelformat, fmtd_index); + /* + * Get supported resolutions. + */ + for (size_index = 0; ; size_index++) { + struct v4l2_frmsizeenum frms; + struct v4l2_pix_format pix_tmp_in, pix_tmp_out; + int ival_index; + + frms.index = size_index; + frms.pixel_format = fmtd.pixelformat; + + rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, + &frms); + if (rval) + break; + + pix_tmp_in.pixelformat = frms.pixel_format; + pix_tmp_in.width = frms.discrete.width; + pix_tmp_in.height = frms.discrete.height; + pix_tmp_out = *wanted_pix_out; + /* Don't do upscaling. */ + if (pix_tmp_out.width > pix_tmp_in.width) + pix_tmp_out.width = pix_tmp_in.width; + if (pix_tmp_out.height > pix_tmp_in.height) + pix_tmp_out.height = pix_tmp_in.height; + rval = isp_try_fmt_cap(isp, &pix_tmp_in, &pix_tmp_out); + if (rval) + return rval; + + dev_dbg(&vdev->vfd->dev, "this w %d\th %d\tfmt %8.8x\t" + "-> w %d\th %d\t fmt %8.8x" + "\twanted w %d\th %d\t fmt %8.8x\n", + pix_tmp_in.width, pix_tmp_in.height, + pix_tmp_in.pixelformat, + pix_tmp_out.width, pix_tmp_out.height, + pix_tmp_out.pixelformat, + wanted_pix_out->width, wanted_pix_out->height, + wanted_pix_out->pixelformat); + +#define IS_SMALLER_OR_EQUAL(pix1, pix2) \ + ((pix1)->width + (pix1)->height \ + < (pix2)->width + (pix2)->height) +#define SIZE_DIFF(pix1, pix2) \ + (abs((pix1)->width - (pix2)->width) \ + + abs((pix1)->height - (pix2)->height)) + + /* + * Don't use modes that are farther from wanted size + * that what we already got. + */ + if (SIZE_DIFF(&pix_tmp_out, wanted_pix_out) + > SIZE_DIFF(&best_pix_out, wanted_pix_out)) { + dev_dbg(&vdev->vfd->dev, "size diff bigger: " + "w %d\th %d\tw %d\th %d\n", + pix_tmp_out.width, pix_tmp_out.height, + best_pix_out.width, + best_pix_out.height); + continue; + } + + /* + * There's an input mode that can provide output + * closer to wanted. + */ + if (SIZE_DIFF(&pix_tmp_out, wanted_pix_out) + < SIZE_DIFF(&best_pix_out, wanted_pix_out)) { + /* Force renegotation of fps etc. */ + best_ival->denominator = 0; + dev_dbg(&vdev->vfd->dev, "renegotiate: " + "w %d\th %d\tw %d\th %d\n", + pix_tmp_out.width, pix_tmp_out.height, + best_pix_out.width, + best_pix_out.height); + } + + for (ival_index = 0; ; ival_index++) { + struct v4l2_frmivalenum frmi; + + frmi.index = ival_index; + frmi.pixel_format = frms.pixel_format; + frmi.width = frms.discrete.width; + frmi.height = frms.discrete.height; + /* FIXME: try to fix standard... */ + frmi.reserved[0] = 0xdeafbeef; + + rval = vidioc_int_enum_frameintervals( + vdev->vdev_sensor, &frmi); + if (rval) + break; + + dev_dbg(&vdev->vfd->dev, "fps %d\n", + frmi.discrete.denominator + / frmi.discrete.numerator); + + if (best_ival->denominator == 0) + goto do_it_now; + + if (best_pix_in->width == 0) + goto do_it_now; + + /* + * We aim to use maximum resolution + * from the sensor, provided that the + * fps is at least as close as on the + * current mode. + */ +#define FPS_ABS_DIFF(fps, ival) abs(fps - (ival).denominator / (ival).numerator) + + /* Select mode with closest fps. */ + if (FPS_ABS_DIFF(fps, frmi.discrete) + < FPS_ABS_DIFF(fps, *best_ival)) { + dev_dbg(&vdev->vfd->dev, "closer fps: " + "fps %ld\t fps %ld\n", + FPS_ABS_DIFF(fps, + frmi.discrete), + FPS_ABS_DIFF(fps, *best_ival)); + goto do_it_now; + } + + /* + * Select bigger resolution if it's available + * at same fps. + */ + if (frmi.width + frmi.height + > best_pix_in->width + best_pix_in->height + && FPS_ABS_DIFF(fps, frmi.discrete) + <= FPS_ABS_DIFF(fps, *best_ival)) { + dev_dbg(&vdev->vfd->dev, "bigger res, " + "same fps: " + "w %d\th %d\tw %d\th %d\n", + frmi.width, frmi.height, + best_pix_in->width, + best_pix_in->height); + goto do_it_now; + } + + dev_dbg(&vdev->vfd->dev, "falling through\n"); + + continue; + +do_it_now: + *best_ival = frmi.discrete; + best_pix_out = pix_tmp_out; + best_pix_in->width = frmi.width; + best_pix_in->height = frmi.height; + best_pix_in->pixelformat = frmi.pixel_format; + + dev_dbg(&vdev->vfd->dev, + "best_pix_in: w %d\th %d\tfmt %8.8x" + "\tival %d/%d\n", + best_pix_in->width, + best_pix_in->height, + best_pix_in->pixelformat, + best_ival->numerator, + best_ival->denominator); + } + } + } + + if (best_ival->denominator == 0) + return -EINVAL; + + *wanted_pix_out = best_pix_out; + + dev_dbg(&vdev->vfd->dev, "w %d, h %d, fmt %8.8x -> w %d, h %d\n", + best_pix_in->width, best_pix_in->height, + best_pix_in->pixelformat, + best_pix_out.width, best_pix_out.height); + + return 0; +} + +static int s_pix_parm(struct omap34xxcam_videodev *vdev, + struct v4l2_pix_format *best_pix, + struct v4l2_pix_format *pix, + struct v4l2_fract *best_ival) +{ + struct device *isp = vdev->cam->isp; + struct v4l2_streamparm a; + struct v4l2_format fmt; + struct v4l2_format old_fmt; + int rval; + + rval = try_pix_parm(vdev, best_pix, pix, best_ival); + if (rval) + return rval; + + rval = isp_s_fmt_cap(isp, best_pix, pix); + if (rval) + return rval; + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix = *best_pix; + vidioc_int_g_fmt_cap(vdev->vdev_sensor, &old_fmt); + rval = vidioc_int_s_fmt_cap(vdev->vdev_sensor, &fmt); + if (rval) + return rval; + + a.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a.parm.capture.timeperframe = *best_ival; + rval = vidioc_int_s_parm(vdev->vdev_sensor, &a); + + return rval; +} + +/** + * vidioc_s_fmt_vid_cap - V4L2 set format capabilities IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @f: ptr to standard V4L2 format structure + * + * Attempts to set input format with the sensor driver (first) and then the + * ISP. Returns the return code from vidioc_g_fmt_vid_cap(). + */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct v4l2_pix_format pix_tmp; + struct v4l2_fract timeperframe; + int rval; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + if (vdev->streaming) { + rval = -EBUSY; + goto out; + } + + if (vdev->vdev_sensor_mode) { + struct v4l2_format input_fmt = *f; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct device *isp = vdev->cam->isp; + + rval = isp_try_fmt_cap(isp, pix, pix); + if (rval) + goto out; + /* Always negotiate with the sensor first */ + rval = vidioc_int_s_fmt_cap(vdev->vdev_sensor, &input_fmt); + if (rval) + goto out; + pix->width = input_fmt.fmt.pix.width; + pix->height = input_fmt.fmt.pix.height; + pix->pixelformat = input_fmt.fmt.pix.pixelformat; + pix->field = input_fmt.fmt.pix.field; + pix->bytesperline = input_fmt.fmt.pix.bytesperline; + pix->colorspace = input_fmt.fmt.pix.colorspace; + pix->sizeimage = input_fmt.fmt.pix.sizeimage; + /* Negotiate with OMAP3 ISP */ + rval = isp_s_fmt_cap(isp, pix, pix); + if (!rval) + vdev->pix = f->fmt.pix; + } else { + vdev->want_pix = f->fmt.pix; + + timeperframe = vdev->want_timeperframe; + + rval = s_pix_parm(vdev, &pix_tmp, &f->fmt.pix, &timeperframe); + if (!rval) + vdev->pix = f->fmt.pix; + } +out: + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_try_fmt_vid_cap - V4L2 try format capabilities IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @f: ptr to standard V4L2 format structure + * + * Checks if the given format is supported by the sensor driver and + * by the ISP. + */ +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct v4l2_pix_format pix_tmp; + struct v4l2_fract timeperframe; + int rval; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + + timeperframe = vdev->want_timeperframe; + + rval = try_pix_parm(vdev, &pix_tmp, &f->fmt.pix, &timeperframe); + + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_reqbufs - V4L2 request buffers IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @b: ptr to standard V4L2 request buffers structure + * + * Attempts to get a buffer from the buffer queue associated with the + * fh through the video buffer library API. + */ +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *b) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + int rval; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + if (vdev->streaming) { + mutex_unlock(&vdev->mutex); + return -EBUSY; + } + + rval = videobuf_reqbufs(&ofh->vbq, b); + + mutex_unlock(&vdev->mutex); + + /* + * Either videobuf_reqbufs failed or the buffers are not + * memory-mapped (which would need special attention). + */ + if (rval < 0 || b->memory != V4L2_MEMORY_MMAP) + goto out; + +out: + return rval; +} + +/** + * vidioc_querybuf - V4L2 query buffer IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @b: ptr to standard V4L2 buffer structure + * + * Attempts to fill in the v4l2_buffer structure for the buffer queue + * associated with the fh through the video buffer library API. + */ +static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap34xxcam_fh *ofh = fh; + + return videobuf_querybuf(&ofh->vbq, b); +} + +/** + * vidioc_qbuf - V4L2 queue buffer IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @b: ptr to standard V4L2 buffer structure + * + * Attempts to queue the v4l2_buffer on the buffer queue + * associated with the fh through the video buffer library API. + */ +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap34xxcam_fh *ofh = fh; + + return videobuf_qbuf(&ofh->vbq, b); +} + +/** + * vidioc_dqbuf - V4L2 dequeue buffer IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @b: ptr to standard V4L2 buffer structure + * + * Attempts to dequeue the v4l2_buffer from the buffer queue + * associated with the fh through the video buffer library API. If the + * buffer is a user space buffer, then this function will also requeue it, + * as user does not expect to do this. + */ +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap34xxcam_fh *ofh = fh; + int rval; + +videobuf_dqbuf_again: + rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); + + /* + * This is a hack. We don't want to show -EIO to the user + * space. Requeue the buffer and try again if we're not doing + * this in non-blocking mode. + */ + if (rval == -EIO) { + videobuf_qbuf(&ofh->vbq, b); + if (!(file->f_flags & O_NONBLOCK)) + goto videobuf_dqbuf_again; + /* + * We don't have a videobuf_buffer now --- maybe next + * time... + */ + rval = -EAGAIN; + } + + return rval; +} + +/** + * vidioc_streamon - V4L2 streamon IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @i: V4L2 buffer type + * + * Attempts to start streaming by enabling the sensor interface and turning + * on video buffer streaming through the video buffer library API. Upon + * success the function returns 0, otherwise an error code is returned. + */ +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + int rval; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + if (vdev->streaming) { + rval = -EBUSY; + goto out; + } + + rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, + OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS); + if (rval) { + dev_dbg(&vdev->vfd->dev, + "omap34xxcam_slave_power_set failed\n"); + goto out; + } + + isp_start(isp); + + rval = videobuf_streamon(&ofh->vbq); + if (rval) { + isp_stop(isp); + omap34xxcam_slave_power_set( + vdev, V4L2_POWER_OFF, + OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS); + } else + vdev->streaming = file; + +out: + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_streamoff - V4L2 streamoff IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @i: V4L2 buffer type + * + * Attempts to stop streaming by flushing all scheduled work, waiting on + * any queued buffers to complete and then stopping the ISP and turning + * off video buffer streaming through the video buffer library API. Upon + * success the function returns 0, otherwise an error code is returned. + */ +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + struct videobuf_queue *q = &ofh->vbq; + int rval; + + mutex_lock(&vdev->mutex); + + if (vdev->streaming == file) + isp_stop(isp); + + rval = videobuf_streamoff(q); + if (!rval) { + vdev->streaming = NULL; + + omap34xxcam_slave_power_set(vdev, V4L2_POWER_STANDBY, + OMAP34XXCAM_SLAVE_POWER_SENSOR); + omap34xxcam_slave_power_suggest(vdev, V4L2_POWER_STANDBY, + OMAP34XXCAM_SLAVE_POWER_LENS); + } + + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_enum_input - V4L2 enumerate input IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @inp: V4L2 input type information structure + * + * Fills in v4l2_input structure. Returns 0. + */ +static int vidioc_enum_input(struct file *file, void *fh, + struct v4l2_input *inp) +{ + struct omap34xxcam_videodev *vdev = ((struct omap34xxcam_fh *)fh)->vdev; + + if (vdev->vdev_sensor_mode) { + if (inp->index == 0) { + strlcpy(inp->name, "COMPOSITE", sizeof(inp->name)); + inp->type = V4L2_INPUT_TYPE_CAMERA; + } else if (inp->index == 1) { + strlcpy(inp->name, "S-VIDEO", sizeof(inp->name)); + inp->type = V4L2_INPUT_TYPE_CAMERA; + } else + return -EINVAL; + } else { + if (inp->index > 0) + return -EINVAL; + strlcpy(inp->name, "camera", sizeof(inp->name)); + inp->type = V4L2_INPUT_TYPE_CAMERA; + } + + return 0; +} + +/** + * vidioc_g_input - V4L2 get input IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @i: address to hold index of input supported + * + * Sets index to 0. + */ +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct omap34xxcam_videodev *vdev = ((struct omap34xxcam_fh *)fh)->vdev; + int rval = 0; + + mutex_lock(&vdev->mutex); + if (vdev->vdev_sensor_mode) { + if (vdev->slave_config[OMAP34XXCAM_SLAVE_SENSOR].cur_input + == INPUT_CVBS_VI4A) + *i = 0; + else if (vdev->slave_config[OMAP34XXCAM_SLAVE_SENSOR].cur_input + == INPUT_SVIDEO_VI2C_VI1C) + *i = 1; + } else { + *i = 0; + } + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_s_input - V4L2 set input IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @i: index of input selected + * + * 0 is only index supported. + */ +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + int rval = 0; + struct v4l2_routing route; + + mutex_lock(&vdev->mutex); + if (vdev->vdev_sensor_mode) { + if (i == 0) + route.input = INPUT_CVBS_VI4A; + else + route.input = INPUT_SVIDEO_VI2C_VI1C; + + route.output = 0; + rval = vidioc_int_s_video_routing(vdev->vdev_sensor, &route); + if (!rval) + vdev->slave_config[OMAP34XXCAM_SLAVE_SENSOR].cur_input + = route.input; + } else { + if (i > 0) + rval = -EINVAL; + } + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_queryctrl - V4L2 query control IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @a: standard V4L2 query control ioctl structure + * + * If the requested control is supported, returns the control information + * in the v4l2_queryctrl structure. Otherwise, returns -EINVAL if the + * control is not supported. If the sensor being used is a "smart sensor", + * this request is passed to the sensor driver, otherwise the ISP is + * queried and if it does not support the requested control, the request + * is forwarded to the "raw" sensor driver to see if it supports it. + */ +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct v4l2_queryctrl a_tmp; + int best_slave = -1; + u32 best_ctrl = (u32)-1; + int i; + + if (vdev->vdev_sensor_config.sensor_isp) + return vidioc_int_queryctrl(vdev->vdev_sensor, a); + + /* No next flags: try slaves directly. */ + if (!(a->id & V4L2_CTRL_FLAG_NEXT_CTRL)) { + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + if (!vidioc_int_queryctrl(vdev->slave[i], a)) + return 0; + } + return isp_queryctrl(a); + } + + /* Find slave with smallest next control id. */ + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + a_tmp = *a; + + if (vidioc_int_queryctrl(vdev->slave[i], &a_tmp)) + continue; + + if (a_tmp.id < best_ctrl) { + best_slave = i; + best_ctrl = a_tmp.id; + } + } + + a_tmp = *a; + if (!isp_queryctrl(&a_tmp)) { + if (a_tmp.id < best_ctrl) { + *a = a_tmp; + + return 0; + } + } + + if (best_slave == -1) + return -EINVAL; + + a->id = best_ctrl; + return vidioc_int_queryctrl(vdev->slave[best_slave], a); +} + +/** + * vidioc_querymenu - V4L2 query menu IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @a: standard V4L2 query menu ioctl structure + * + * If the requested control is supported, returns the menu information + * in the v4l2_querymenu structure. Otherwise, returns -EINVAL if the + * control is not supported or is not a menu. If the sensor being used + * is a "smart sensor", this request is passed to the sensor driver, + * otherwise the ISP is queried and if it does not support the requested + * menu control, the request is forwarded to the "raw" sensor driver to + * see if it supports it. + */ +static int vidioc_querymenu(struct file *file, void *fh, + struct v4l2_querymenu *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + int i; + + if (vdev->vdev_sensor_config.sensor_isp) + return vidioc_int_querymenu(vdev->vdev_sensor, a); + + /* Try slaves directly. */ + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + if (!vidioc_int_querymenu(vdev->slave[i], a)) + return 0; + } + return isp_querymenu(a); +} + +static int vidioc_g_ext_ctrls(struct file *file, void *fh, + struct v4l2_ext_controls *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + int i, ctrl_idx, rval = 0; + + mutex_lock(&vdev->mutex); + + for (ctrl_idx = 0; ctrl_idx < a->count; ctrl_idx++) { + struct v4l2_control ctrl; + + ctrl.id = a->controls[ctrl_idx].id; + + if (vdev->vdev_sensor_config.sensor_isp) { + rval = vidioc_int_g_ctrl(vdev->vdev_sensor, &ctrl); + } else { + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + rval = vidioc_int_g_ctrl(vdev->slave[i], &ctrl); + if (!rval) + break; + } + } + + if (rval) + rval = isp_g_ctrl(isp, &ctrl); + + if (rval) { + a->error_idx = ctrl_idx; + break; + } + + a->controls[ctrl_idx].value = ctrl.value; + } + + mutex_unlock(&vdev->mutex); + + return rval; +} + +static int vidioc_s_ext_ctrls(struct file *file, void *fh, + struct v4l2_ext_controls *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + int i, ctrl_idx, rval = 0; + + mutex_lock(&vdev->mutex); + + for (ctrl_idx = 0; ctrl_idx < a->count; ctrl_idx++) { + struct v4l2_control ctrl; + + ctrl.id = a->controls[ctrl_idx].id; + ctrl.value = a->controls[ctrl_idx].value; + + if (vdev->vdev_sensor_config.sensor_isp) { + rval = vidioc_int_s_ctrl(vdev->vdev_sensor, &ctrl); + } else { + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + rval = vidioc_int_s_ctrl(vdev->slave[i], &ctrl); + if (!rval) + break; + } + } + + if (rval) + rval = isp_s_ctrl(isp, &ctrl); + + if (rval) { + a->error_idx = ctrl_idx; + break; + } + + a->controls[ctrl_idx].value = ctrl.value; + } + + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_g_parm - V4L2 get parameters IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @a: standard V4L2 stream parameters structure + * + * If request is for video capture buffer type, handles request by + * forwarding to sensor driver. + */ +static int vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&vdev->mutex); + rval = vidioc_int_g_parm(vdev->vdev_sensor, a); + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_s_parm - V4L2 set parameters IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @a: standard V4L2 stream parameters structure + * + * If request is for video capture buffer type, handles request by + * first getting current stream parameters from sensor, then forwarding + * request to set new parameters to sensor driver. It then attempts to + * enable the sensor interface with the new parameters. If this fails, it + * reverts back to the previous parameters. + */ +static int vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct v4l2_pix_format pix_tmp_sensor, pix_tmp; + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + if (vdev->streaming) { + rval = -EBUSY; + goto out; + } + + vdev->want_timeperframe = a->parm.capture.timeperframe; + + pix_tmp = vdev->want_pix; + + rval = s_pix_parm(vdev, &pix_tmp_sensor, &pix_tmp, + &a->parm.capture.timeperframe); + +out: + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_cropcap - V4L2 crop capture IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @a: standard V4L2 crop capture structure + * + * If using a "smart" sensor, just forwards request to the sensor driver, + * otherwise fills in the v4l2_cropcap values locally. + */ +static int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct v4l2_cropcap *cropcap = a; + int rval; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + + rval = vidioc_int_cropcap(vdev->vdev_sensor, a); + + if (rval && !vdev->vdev_sensor_config.sensor_isp) { + struct v4l2_format f; + + /* cropcap failed, try to do this via g_fmt_cap */ + rval = vidioc_int_g_fmt_cap(vdev->vdev_sensor, &f); + if (!rval) { + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = f.fmt.pix.width; + cropcap->bounds.height = f.fmt.pix.height; + cropcap->defrect = cropcap->bounds; + cropcap->pixelaspect.numerator = 1; + cropcap->pixelaspect.denominator = 1; + } + } + + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_g_crop - V4L2 get capture crop IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @a: standard V4L2 crop structure + * + * If using a "smart" sensor, just forwards request to the sensor driver, + * otherwise calls the isp functions to fill in current crop values. + */ +static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + int rval = 0; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + + if (vdev->vdev_sensor_config.sensor_isp) + rval = vidioc_int_g_crop(vdev->vdev_sensor, a); + else + rval = isp_g_crop(isp, a); + + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_s_crop - V4L2 set capture crop IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @a: standard V4L2 crop structure + * + * If using a "smart" sensor, just forwards request to the sensor driver, + * otherwise calls the isp functions to set the current crop values. + */ +static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *a) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + int rval = 0; + + if (vdev->vdev_sensor == v4l2_int_device_dummy()) + return -EINVAL; + + mutex_lock(&vdev->mutex); + + if (vdev->vdev_sensor_config.sensor_isp) + rval = vidioc_int_s_crop(vdev->vdev_sensor, a); + else + rval = isp_s_crop(isp, a); + + mutex_unlock(&vdev->mutex); + + return rval; +} + +static int vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *frms) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + u32 pixel_format; + int rval; + + mutex_lock(&vdev->mutex); + + if (vdev->vdev_sensor_config.sensor_isp) { + rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, frms); + } else { + pixel_format = frms->pixel_format; + frms->pixel_format = -1; /* ISP does format conversion */ + rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, frms); + frms->pixel_format = pixel_format; + } + + mutex_unlock(&vdev->mutex); + return rval; +} + +static int vidioc_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *frmi) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + u32 pixel_format; + int rval; + + mutex_lock(&vdev->mutex); + + if (vdev->vdev_sensor_config.sensor_isp) { + rval = vidioc_int_enum_frameintervals(vdev->vdev_sensor, frmi); + } else { + pixel_format = frmi->pixel_format; + frmi->pixel_format = -1; /* ISP does format conversion */ + rval = vidioc_int_enum_frameintervals(vdev->vdev_sensor, frmi); + frmi->pixel_format = pixel_format; + } + + mutex_unlock(&vdev->mutex); + return rval; +} + +/** + * vidioc_querystd - V4L2 query current standard IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @std: standard V4L2 v4l2_std_id enum + * + * If using a "smart" sensor, just forwards request to the sensor driver, + * otherwise returns error + */ +static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *std) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + int rval = 0; + + mutex_lock(&vdev->mutex); + if (vdev->vdev_sensor_mode) { + rval = vidioc_int_querystd(vdev->vdev_sensor, std); + if (rval == 0) + vdev->vfd->current_norm = *std; + } else + rval = -EINVAL; + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_s_std - V4L2 set standard IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @std: standard V4L2 v4l2_std_id enum + * + * If using a "smart" sensor, just forwards request to the sensor driver, + * otherwise returns error + */ +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct omap34xxcam_fh *ofh = fh; + struct omap34xxcam_videodev *vdev = ofh->vdev; + int rval = 0; + + mutex_lock(&vdev->mutex); + if (vdev->vdev_sensor_mode) { + rval = vidioc_int_s_std(vdev->vdev_sensor, std); + if (rval == 0) + vdev->vfd->current_norm = *std; + } else + rval = -EINVAL; + mutex_unlock(&vdev->mutex); + + return rval; +} + +/** + * vidioc_default - private IOCTL handler + * @file: ptr. to system file structure + * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) + * @cmd: ioctl cmd value + * @arg: ioctl arg value + * + * If the sensor being used is a "smart sensor", this request is returned to + * caller with -EINVAL err code. Otherwise if the control id is the private + * VIDIOC_PRIVATE_ISP_AEWB_REQ to update the analog gain or exposure, + * then this request is forwared directly to the sensor to incorporate the + * feedback. The request is then passed on to the ISP private IOCTL handler, + * isp_handle_private() + */ +static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) +{ + struct omap34xxcam_fh *ofh = file->private_data; + struct omap34xxcam_videodev *vdev = ofh->vdev; + struct device *isp = vdev->cam->isp; + int rval; + + if (cmd == VIDIOC_PRIVATE_OMAP34XXCAM_SENSOR_INFO) { + u32 pixclk; + struct v4l2_pix_format active_size, full_size; + struct omap34xxcam_sensor_info *ret_sensor_info; + + ret_sensor_info = (struct omap34xxcam_sensor_info *)arg; + mutex_lock(&vdev->mutex); + rval = vidioc_int_priv_g_pixclk(vdev->vdev_sensor, &pixclk); + mutex_unlock(&vdev->mutex); + if (rval) + goto out; + mutex_lock(&vdev->mutex); + rval = vidioc_int_priv_g_activesize(vdev->vdev_sensor, + &active_size); + mutex_unlock(&vdev->mutex); + if (rval) + goto out; + mutex_lock(&vdev->mutex); + rval = vidioc_int_priv_g_fullsize(vdev->vdev_sensor, + &full_size); + mutex_unlock(&vdev->mutex); + if (rval) + goto out; + ret_sensor_info->current_xclk = pixclk; + memcpy(&ret_sensor_info->active_size, &active_size, + sizeof(struct v4l2_pix_format)); + memcpy(&ret_sensor_info->full_size, &full_size, + sizeof(struct v4l2_pix_format)); + rval = 0; + goto out; + } + + if (vdev->vdev_sensor_config.sensor_isp) { + rval = -EINVAL; + } else { + switch (cmd) { + case VIDIOC_PRIVATE_ISP_AEWB_REQ: + { + /* Need to update sensor first */ + struct isph3a_aewb_data *data; + struct v4l2_control vc; + + data = (struct isph3a_aewb_data *) arg; + if (data->update & SET_EXPOSURE) { + dev_dbg(&vdev->vfd->dev, "using " + "VIDIOC_PRIVATE_ISP_AEWB_REQ to set " + "exposure is deprecated!\n"); + vc.id = V4L2_CID_EXPOSURE; + vc.value = data->shutter; + mutex_lock(&vdev->mutex); + rval = vidioc_int_s_ctrl(vdev->vdev_sensor, + &vc); + mutex_unlock(&vdev->mutex); + if (rval) + goto out; + } + if (data->update & SET_ANALOG_GAIN) { + dev_dbg(&vdev->vfd->dev, "using " + "VIDIOC_PRIVATE_ISP_AEWB_REQ to set " + "gain is deprecated!\n"); + vc.id = V4L2_CID_GAIN; + vc.value = data->gain; + mutex_lock(&vdev->mutex); + rval = vidioc_int_s_ctrl(vdev->vdev_sensor, + &vc); + mutex_unlock(&vdev->mutex); + if (rval) + goto out; + } + } + break; + case VIDIOC_PRIVATE_ISP_AF_REQ: { + /* Need to update lens first */ + struct isp_af_data *data; + struct v4l2_control vc; + + if (!vdev->vdev_lens) { + rval = -EINVAL; + goto out; + } + data = (struct isp_af_data *) arg; + if (data->update & LENS_DESIRED_POSITION) { + dev_dbg(&vdev->vfd->dev, "using " + "VIDIOC_PRIVATE_ISP_AF_REQ to set " + "lens position is deprecated!\n"); + vc.id = V4L2_CID_FOCUS_ABSOLUTE; + vc.value = data->desired_lens_direction; + mutex_lock(&vdev->mutex); + rval = vidioc_int_s_ctrl(vdev->vdev_lens, &vc); + mutex_unlock(&vdev->mutex); + if (rval) + goto out; + } + } + break; + } + + mutex_lock(&vdev->mutex); + rval = isp_handle_private(isp, cmd, arg); + mutex_unlock(&vdev->mutex); + } +out: + return rval; +} + +/* + * + * File operations. + * + */ + +/** + * omap34xxcam_poll - file operations poll handler + * @file: ptr. to system file structure + * @wait: system poll table structure + * + */ +static unsigned int omap34xxcam_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct omap34xxcam_fh *fh = file->private_data; + struct omap34xxcam_videodev *vdev = fh->vdev; + struct videobuf_buffer *vb; + + mutex_lock(&vdev->mutex); + if (vdev->streaming != file) { + mutex_unlock(&vdev->mutex); + return POLLERR; + } + mutex_unlock(&vdev->mutex); + + mutex_lock(&fh->vbq.vb_lock); + if (list_empty(&fh->vbq.stream)) { + mutex_unlock(&fh->vbq.vb_lock); + return POLLERR; + } + vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream); + mutex_unlock(&fh->vbq.vb_lock); + + poll_wait(file, &vb->done, wait); + + if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + + return 0; +} + +/** + * omap34xxcam_mmap - file operations mmap handler + * @file: ptr. to system file structure + * @vma: system virt. mem. area structure + * + * Maps a virtual memory area via the video buffer API + */ +static int omap34xxcam_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct omap34xxcam_fh *fh = file->private_data; + return videobuf_mmap_mapper(&fh->vbq, vma); +} + +/** + * omap34xxcam_open - file operations open handler + * @inode: ptr. to system inode structure + * @file: ptr. to system file structure + * + * Allocates and initializes the per-filehandle data (omap34xxcam_fh), + * enables the sensor, opens/initializes the ISP interface and the + * video buffer queue. Note that this function will allow multiple + * file handles to be open simultaneously, however only the first + * handle opened will initialize the ISP. It is the application + * responsibility to only use one handle for streaming and the others + * for control only. + * This function returns 0 upon success and -ENODEV upon error. + */ +static int omap34xxcam_open(struct file *file) +{ + int rval = 0; + struct omap34xxcam_videodev *vdev = NULL; + struct omap34xxcam_device *cam = omap34xxcam; + struct device *isp; + struct omap34xxcam_fh *fh; + struct v4l2_format sensor_format; + int first_user = 0; + int i; + + for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { + if (cam->vdevs[i].vfd + && cam->vdevs[i].vfd->minor == + iminor(file->f_dentry->d_inode)) { + vdev = &cam->vdevs[i]; + break; + } + } + + if (!vdev || !vdev->vfd) + return -ENODEV; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (fh == NULL) + return -ENOMEM; + + fh->vdev = vdev; + + mutex_lock(&vdev->mutex); + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + if (vdev->slave[i] != v4l2_int_device_dummy() + && !try_module_get(vdev->slave[i]->module)) { + mutex_unlock(&vdev->mutex); + dev_err(&vdev->vfd->dev, "can't try_module_get %s\n", + vdev->slave[i]->name); + rval = -ENODEV; + goto out_try_module_get; + } + } + + if (atomic_inc_return(&vdev->users) == 1) { + first_user = 1; + isp = isp_get(); + if (!isp) { + rval = -EBUSY; + dev_err(&vdev->vfd->dev, "can't get isp\n"); + goto out_isp_get; + } + cam->isp = isp; + if (omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, + OMAP34XXCAM_SLAVE_POWER_ALL)) { + dev_err(&vdev->vfd->dev, "can't power up slaves\n"); + rval = -EBUSY; + goto out_slave_power_set_standby; + } + omap34xxcam_slave_power_set( + vdev, V4L2_POWER_STANDBY, + OMAP34XXCAM_SLAVE_POWER_SENSOR); + omap34xxcam_slave_power_suggest( + vdev, V4L2_POWER_STANDBY, + OMAP34XXCAM_SLAVE_POWER_LENS); + } + + if (vdev->vdev_sensor == v4l2_int_device_dummy() || !first_user) + goto out_no_pix; + + if (vdev->vdev_sensor_config.sensor_isp) { + if ((vdev->slave_config[OMAP34XXCAM_SLAVE_SENSOR].cur_input + != INPUT_CVBS_VI4A) && + (vdev->slave_config[OMAP34XXCAM_SLAVE_SENSOR]. + cur_input != INPUT_SVIDEO_VI2C_VI1C)) { + struct v4l2_routing route; + int rval; + route.input = INPUT_CVBS_VI4A; + route.output = 0; + rval = vidioc_int_s_video_routing(vdev->vdev_sensor, + &route); + if (rval) { + route.input = INPUT_SVIDEO_VI2C_VI1C; + rval = vidioc_int_s_video_routing( + vdev->vdev_sensor, &route); + } + if (!rval) + vdev->slave_config[OMAP34XXCAM_SLAVE_SENSOR] + .cur_input = route.input; + } + sensor_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + + /* Get the format the sensor is using. */ + rval = vidioc_int_g_fmt_cap(vdev->vdev_sensor, &sensor_format); + if (rval) { + dev_err(&vdev->vfd->dev, + "can't get current pix from sensor!\n"); + goto out_vidioc_int_g_fmt_cap; + } + + if (!vdev->pix.width) + vdev->pix = sensor_format.fmt.pix; + + if (!vdev->vdev_sensor_config.sensor_isp) { + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe = + vdev->want_timeperframe; + + rval = s_pix_parm(vdev, &pix, &vdev->pix, &timeperframe); + if (rval) { + dev_err(&vdev->vfd->dev, + "isp doesn't like the sensor!\n"); + goto out_isp_s_fmt_cap; + } + } + +out_no_pix: + mutex_unlock(&vdev->mutex); + + file->private_data = fh; + + spin_lock_init(&fh->vbq_lock); + + videobuf_queue_sg_init(&fh->vbq, &omap34xxcam_vbq_ops, NULL, + &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), fh); + + return 0; + +out_isp_s_fmt_cap: +out_vidioc_int_g_fmt_cap: + omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, + OMAP34XXCAM_SLAVE_POWER_ALL); +out_slave_power_set_standby: + isp_put(); + +out_isp_get: + atomic_dec(&vdev->users); + mutex_unlock(&vdev->mutex); + +out_try_module_get: + for (i--; i >= 0; i--) + if (vdev->slave[i] != v4l2_int_device_dummy()) + module_put(vdev->slave[i]->module); + + kfree(fh); + + return rval; +} + +/** + * omap34xxcam_release - file operations release handler + * @inode: ptr. to system inode structure + * @file: ptr. to system file structure + * + * Complement of omap34xxcam_open. This function will flush any scheduled + * work, disable the sensor, close the ISP interface, stop the + * video buffer queue from streaming and free the per-filehandle data + * (omap34xxcam_fh). Note that because multiple open file handles + * are allowed, this function will only close the ISP and disable the + * sensor when the last open file handle (by count) is closed. + * This function returns 0. + */ +static int omap34xxcam_release(struct file *file) +{ + struct omap34xxcam_fh *fh = file->private_data; + struct omap34xxcam_videodev *vdev = fh->vdev; + struct device *isp = vdev->cam->isp; + int i; + + mutex_lock(&vdev->mutex); + if (vdev->streaming == file) { + isp_stop(isp); + videobuf_streamoff(&fh->vbq); + omap34xxcam_slave_power_set( + vdev, V4L2_POWER_STANDBY, + OMAP34XXCAM_SLAVE_POWER_SENSOR); + omap34xxcam_slave_power_suggest( + vdev, V4L2_POWER_STANDBY, + OMAP34XXCAM_SLAVE_POWER_LENS); + vdev->streaming = NULL; + } + + if (atomic_dec_return(&vdev->users) == 0) { + omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, + OMAP34XXCAM_SLAVE_POWER_ALL); + isp_put(); + } + mutex_unlock(&vdev->mutex); + + file->private_data = NULL; + + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) + if (vdev->slave[i] != v4l2_int_device_dummy()) + module_put(vdev->slave[i]->module); + + kfree(fh); + + return 0; +} + +static struct v4l2_file_operations omap34xxcam_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .poll = omap34xxcam_poll, + .mmap = omap34xxcam_mmap, + .open = omap34xxcam_open, + .release = omap34xxcam_release, +}; + +static void omap34xxcam_vfd_name_update(struct omap34xxcam_videodev *vdev) +{ + struct video_device *vfd = vdev->vfd; + int i; + + strlcpy(vfd->name, CAM_SHORT_NAME, sizeof(vfd->name)); + for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { + strlcat(vfd->name, "/", sizeof(vfd->name)); + if (vdev->slave[i] == v4l2_int_device_dummy()) + continue; + strlcat(vfd->name, vdev->slave[i]->name, sizeof(vfd->name)); + } + dev_dbg(&vdev->vfd->dev, "video%d is now %s\n", vfd->num, vfd->name); +} + +/** + * omap34xxcam_device_unregister - V4L2 detach handler + * @s: ptr. to standard V4L2 device information structure + * + * Detach sensor and unregister and release the video device. + */ +static void omap34xxcam_device_unregister(struct v4l2_int_device *s) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + struct omap34xxcam_hw_config hwc; + + BUG_ON(vidioc_int_g_priv(s, &hwc) < 0); + + mutex_lock(&vdev->mutex); + + if (vdev->slave[hwc.dev_type] != v4l2_int_device_dummy()) { + vdev->slave[hwc.dev_type] = v4l2_int_device_dummy(); + vdev->slaves--; + omap34xxcam_vfd_name_update(vdev); + } + + if (vdev->slaves == 0 && vdev->vfd) { + if (vdev->vfd->minor == -1) { + /* + * The device was never registered, so release the + * video_device struct directly. + */ + video_device_release(vdev->vfd); + } else { + /* + * The unregister function will release the + * video_device struct as well as + * unregistering it. + */ + video_unregister_device(vdev->vfd); + } + vdev->vfd = NULL; + } + + mutex_unlock(&vdev->mutex); +} + +static const struct v4l2_ioctl_ops omap34xxcam_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_querymenu = vidioc_querymenu, + .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, + .vidioc_default = vidioc_default, +}; + +/** + * omap34xxcam_device_register - V4L2 attach handler + * @s: ptr. to standard V4L2 device information structure + * + * Allocates and initializes the V4L2 video_device structure, initializes + * the sensor, and finally + registers the device with V4L2 based on the + * video_device structure. + * + * Returns 0 on success, otherwise an appropriate error code on + * failure. + */ +static int omap34xxcam_device_register(struct v4l2_int_device *s) +{ + struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; + struct omap34xxcam_hw_config hwc; + struct v4l2_ifparm ifparm; + struct device *isp; + int rval; + + /* We need to check rval just once. The place is here. */ + if (vidioc_int_g_priv(s, &hwc)) + return -ENODEV; + + if (vdev->index != hwc.dev_index) + return -ENODEV; + + if (hwc.dev_type < 0 || hwc.dev_type > OMAP34XXCAM_SLAVE_FLASH) + return -EINVAL; + + if (vdev->slave[hwc.dev_type] != v4l2_int_device_dummy()) + return -EBUSY; + + mutex_lock(&vdev->mutex); + if (atomic_read(&vdev->users)) { + printk(KERN_ERR "%s: we're open (%d), can't register\n", + __func__, atomic_read(&vdev->users)); + mutex_unlock(&vdev->mutex); + return -EBUSY; + } + + vdev->slaves++; + + vdev->slave[hwc.dev_type] = s; + vdev->slave_config[hwc.dev_type] = hwc; + + if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) { + isp = isp_get(); + if (!isp) { + rval = -EBUSY; + printk(KERN_ERR "%s: can't get ISP, " + "sensor init failed\n", __func__); + goto err; + } + vdev->cam->isp = isp; + } + rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, + 1 << hwc.dev_type); + if (rval) + goto err_omap34xxcam_slave_power_set; + if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) { + struct v4l2_format format; + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rval = vidioc_int_g_fmt_cap(vdev->vdev_sensor, &format); + if (rval) + rval = -EBUSY; + + vdev->want_pix = format.fmt.pix; + } + omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, 1 << hwc.dev_type); + if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) + isp_put(); + + if (rval) + goto err; + + /* Are we the first slave? */ + if (vdev->slaves == 1) { + /* initialize the video_device struct */ + vdev->vfd = video_device_alloc(); + if (!vdev->vfd) { + printk(KERN_ERR "%s: could not allocate " + "video device struct\n", __func__); + rval = -ENOMEM; + goto err; + } + vdev->vfd->release = video_device_release; + vdev->vfd->minor = -1; + vdev->vfd->fops = &omap34xxcam_fops; + vdev->vfd->ioctl_ops = &omap34xxcam_ioctl_ops; + video_set_drvdata(vdev->vfd, vdev); + + if (video_register_device(vdev->vfd, VFL_TYPE_GRABBER, + hwc.dev_minor) < 0) { + printk(KERN_ERR "%s: could not register V4L device\n", + __func__); + vdev->vfd->minor = -1; + rval = -EBUSY; + goto err; + } + } + /*Determine whether the slave connected is BT656 decoder or a sensor*/ + if (!vidioc_int_g_ifparm(s, &ifparm)) { + if (ifparm.if_type == V4L2_IF_TYPE_BT656) { + vdev->vfd->current_norm = V4L2_STD_NTSC; + vdev->vfd->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL; + if ((ifparm.u.bt656.mode == + V4L2_IF_TYPE_BT656_MODE_BT_8BIT) || + (ifparm.u.bt656.mode == + V4L2_IF_TYPE_BT656_MODE_BT_10BIT)) + vdev->slave_mode[hwc.dev_type] = 1; + } + } + omap34xxcam_vfd_name_update(vdev); + + mutex_unlock(&vdev->mutex); + + return 0; + +err_omap34xxcam_slave_power_set: + if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) + isp_put(); + +err: + if (s == vdev->slave[hwc.dev_type]) { + vdev->slave[hwc.dev_type] = v4l2_int_device_dummy(); + vdev->slaves--; + } + + mutex_unlock(&vdev->mutex); + omap34xxcam_device_unregister(s); + + return rval; +} + +static struct v4l2_int_master omap34xxcam_master = { + .attach = omap34xxcam_device_register, + .detach = omap34xxcam_device_unregister, +}; + +/* + * + * Module initialisation and deinitialisation + * + */ + +static void omap34xxcam_exit(void) +{ + struct omap34xxcam_device *cam = omap34xxcam; + int i; + + if (!cam) + return; + + for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { + if (cam->vdevs[i].cam == NULL) + continue; + + v4l2_int_device_unregister(&cam->vdevs[i].master); + cam->vdevs[i].cam = NULL; + } + + omap34xxcam = NULL; + + kfree(cam); +} + +static int __init omap34xxcam_init(void) +{ + struct omap34xxcam_device *cam; + int i; + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) { + printk(KERN_ERR "%s: could not allocate memory\n", __func__); + return -ENOMEM; + } + + omap34xxcam = cam; + + for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { + struct omap34xxcam_videodev *vdev = &cam->vdevs[i]; + struct v4l2_int_device *m = &vdev->master; + + m->module = THIS_MODULE; + strlcpy(m->name, CAM_NAME, sizeof(m->name)); + m->type = v4l2_int_type_master; + m->u.master = &omap34xxcam_master; + m->priv = vdev; + + mutex_init(&vdev->mutex); + vdev->index = i; + vdev->cam = cam; + vdev->vdev_sensor = + vdev->vdev_lens = + vdev->vdev_flash = v4l2_int_device_dummy(); +#ifdef OMAP34XXCAM_POWEROFF_DELAY + setup_timer(&vdev->poweroff_timer, + omap34xxcam_slave_power_timer, (unsigned long)vdev); + INIT_WORK(&vdev->poweroff_work, omap34xxcam_slave_power_work); +#endif /* OMAP34XXCAM_POWEROFF_DELAY */ + + if (v4l2_int_device_register(m)) + goto err; + } + + return 0; + +err: + omap34xxcam_exit(); + return -ENODEV; +} + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("OMAP34xx Video for Linux camera driver"); +MODULE_LICENSE("GPL"); + +late_initcall(omap34xxcam_init); +module_exit(omap34xxcam_exit); diff --git a/drivers/media/video/omap34xxcam.h b/drivers/media/video/omap34xxcam.h new file mode 100644 index 00000000000..cddc4f2fb36 --- /dev/null +++ b/drivers/media/video/omap34xxcam.h @@ -0,0 +1,190 @@ +/* + * omap34xxcam.h + * + * Copyright (C) 2006--2009 Nokia Corporation + * Copyright (C) 2007--2009 Texas Instruments + * + * Contact: Sakari Ailus + * Tuukka Toivonen + * + * Originally based on the OMAP 2 camera driver. + * + * Written by Sakari Ailus + * Tuukka Toivonen + * Sergio Aguirre + * Mohit Jalori + * Sameer Venkatraman + * Leonides Martinez + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef OMAP34XXCAM_H +#define OMAP34XXCAM_H + +#include +#include "isp/isp.h" + +#define CAM_NAME "omap34xxcam" +#define CAM_SHORT_NAME "omap3" + +#define OMAP34XXCAM_XCLK_NONE -1 +#define OMAP34XXCAM_XCLK_A 0 +#define OMAP34XXCAM_XCLK_B 1 + +#define OMAP34XXCAM_SLAVE_SENSOR 0 +#define OMAP34XXCAM_SLAVE_LENS 1 +#define OMAP34XXCAM_SLAVE_FLASH 2 /* This is the last slave! */ + +/* mask for omap34xxcam_slave_power_set */ +#define OMAP34XXCAM_SLAVE_POWER_SENSOR (1 << OMAP34XXCAM_SLAVE_SENSOR) +#define OMAP34XXCAM_SLAVE_POWER_LENS (1 << OMAP34XXCAM_SLAVE_LENS) +#define OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS \ + (OMAP34XXCAM_SLAVE_POWER_SENSOR | OMAP34XXCAM_SLAVE_POWER_LENS) +#define OMAP34XXCAM_SLAVE_POWER_FLASH (1 << OMAP34XXCAM_SLAVE_FLASH) +#define OMAP34XXCAM_SLAVE_POWER_ALL -1 + +#define OMAP34XXCAM_VIDEODEVS 4 + +/* #define OMAP34XXCAM_POWEROFF_DELAY (2 * HZ) */ + +struct omap34xxcam_device; +struct omap34xxcam_videodev; + +/** + * struct omap34xxcam_sensor_config - struct for vidioc_int_g_priv ioctl + * @sensor_isp: Is sensor smart/SOC or raw + * @capture_mem: Size limit to mmap buffers. + * @ival_default: Default frame interval for sensor. + */ +struct omap34xxcam_sensor_config { + int sensor_isp; + u32 capture_mem; + struct v4l2_fract ival_default; +}; + +struct omap34xxcam_lens_config { +}; + +struct omap34xxcam_flash_config { +}; + +struct omap34xxcam_hw_config { + int dev_index; /* Index in omap34xxcam_sensors */ + int dev_minor; /* Video device minor number */ + int dev_type; /* OMAP34XXCAM_SLAVE_* */ + union { + struct omap34xxcam_sensor_config sensor; + struct omap34xxcam_lens_config lens; + struct omap34xxcam_flash_config flash; + } u; + int cur_input; +}; + +/** + * struct omap34xxcam_videodev - per /dev/video* structure + * @mutex: serialises access to this structure + * @cam: pointer to cam hw structure + * @master: we are v4l2_int_device master + * @sensor: sensor device + * @lens: lens device + * @flash: flash device + * @slaves: how many slaves we have at the moment + * @vfd: our video device + * @index: index of this structure in cam->vdevs + * @users: how many users we have + * @power_state: Current power state + * @power_state_wish: New power state when poweroff_timer expires + * @power_state_mask: Bitmask of devices to set the new power state + * @poweroff_timer: Timer for dispatching poweroff_work + * @poweroff_work: Work for slave power state change + * @sensor_config: ISP-speicific sensor configuration + * @lens_config: ISP-speicific lens configuration + * @flash_config: ISP-speicific flash configuration + * @streaming: streaming file handle, if streaming is enabled + * @want_timeperframe: Desired timeperframe + * @want_pix: Desired pix + * @pix: Current pix + */ +struct omap34xxcam_videodev { + struct mutex mutex; /* serialises access to this structure */ + + struct omap34xxcam_device *cam; + struct v4l2_int_device master; + +#define vdev_sensor slave[OMAP34XXCAM_SLAVE_SENSOR] +#define vdev_lens slave[OMAP34XXCAM_SLAVE_LENS] +#define vdev_flash slave[OMAP34XXCAM_SLAVE_FLASH] + struct v4l2_int_device *slave[OMAP34XXCAM_SLAVE_FLASH + 1]; + + /* number of slaves attached */ + int slaves; + + /*** video device parameters ***/ + struct video_device *vfd; + + /*** general driver state information ***/ + int index; + atomic_t users; + enum v4l2_power power_state[OMAP34XXCAM_SLAVE_FLASH + 1]; +#ifdef OMAP34XXCAM_POWEROFF_DELAY + enum v4l2_power power_state_wish; + int power_state_mask; + struct timer_list poweroff_timer; + struct work_struct poweroff_work; +#endif /* OMAP34XXCAM_POWEROFF_DELAY */ + +#define vdev_sensor_config slave_config[OMAP34XXCAM_SLAVE_SENSOR].u.sensor +#define vdev_lens_config slave_config[OMAP34XXCAM_SLAVE_LENS].u.lens +#define vdev_flash_config slave_config[OMAP34XXCAM_SLAVE_FLASH].u.flash + struct omap34xxcam_hw_config slave_config[OMAP34XXCAM_SLAVE_FLASH + 1]; + +#define vdev_sensor_mode slave_mode[OMAP34XXCAM_SLAVE_SENSOR] +#define vdev_lens_mode slave_mode[OMAP34XXCAM_SLAVE_LENS] +#define vdev_flash_mode slave_mode[OMAP34XXCAM_SLAVE_FLASH] + int slave_mode[OMAP34XXCAM_SLAVE_FLASH + 1]; + + /*** capture data ***/ + struct file *streaming; + struct v4l2_fract want_timeperframe; + struct v4l2_pix_format want_pix; + struct v4l2_pix_format pix; +}; + +/** + * struct omap34xxcam_device - per-device data structure + * @vdevs: /dev/video specific structures + */ +struct omap34xxcam_device { + struct omap34xxcam_videodev vdevs[OMAP34XXCAM_VIDEODEVS]; + struct device *isp; +}; + +/** + * struct omap34xxcam_fh - per-filehandle data structure + * @vbq_lock: spinlock for the videobuf queue + * @vbq: V4L2 video buffer queue structure + * @field_count: field counter for videobuf_buffer + * @vdev: our /dev/video specific structure + */ +struct omap34xxcam_fh { + spinlock_t vbq_lock; /* spinlock for the videobuf queue */ + struct videobuf_queue vbq; + atomic_t field_count; + struct omap34xxcam_videodev *vdev; +}; + +#endif /* ifndef OMAP34XXCAM_H */ diff --git a/drivers/media/video/ov3640.c b/drivers/media/video/ov3640.c new file mode 100644 index 00000000000..21d0b72e54d --- /dev/null +++ b/drivers/media/video/ov3640.c @@ -0,0 +1,2212 @@ +/* + * ov3640.c - OV3640 sensor driver + * + * Copyright (C) 2009 Texas Instruments. + * + * Leverage ov3640.c + * + * 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. + */ + +#include +#include +#include +#include +#include "ov3640_regs.h" +#include "omap34xxcam.h" +#include "isp/ispcsi2.h" + +#define OV3640_DRIVER_NAME "ov3640" + +#define I2C_M_WR 0 + +/* Register initialization tables for ov3640 */ +/* Terminating list entry for reg */ +#define OV3640_REG_TERM 0xFFFF +/* Terminating list entry for val */ +#define OV3640_VAL_TERM 0xFF + +#define OV3640_CSI2_VIRTUAL_ID 0x1 + +/* FPS Capabilities */ +#define OV3640_MIN_FPS 5 +#define OV3640_DEF_FPS 15 +#define OV3640_MAX_FPS 30 + +#define OV3640_MIN_BRIGHT 0 +#define OV3640_MAX_BRIGHT 6 +#define OV3640_DEF_BRIGHT 0 +#define OV3640_BRIGHT_STEP 1 + +#define OV3640_DEF_CONTRAST 0 +#define OV3640_MIN_CONTRAST 0 +#define OV3640_MAX_CONTRAST 6 +#define OV3640_CONTRAST_STEP 1 + +/* NOTE: Set this as 0 for enabling SoC mode */ +#define OV3640_RAW_MODE 1 + +/* XCLK Frequency in Hz*/ +#define OV3640_XCLK 24000000 + +/* High byte of product ID */ +#define OV3640_PIDH_MAGIC 0x36 +/* Low byte of product ID */ +#define OV3640_PIDL_MAGIC1 0x41 +#define OV3640_PIDL_MAGIC2 0x4C + +/* define a structure for ov3640 register initialization values */ +struct ov3640_reg { + unsigned int reg; + unsigned char val; +}; + +enum ov3640_image_size { + XGA, + QXGA +}; +enum ov3640_pixel_format { + YUV, + RGB565, + RGB555, + RAW10 +}; + +#define OV3640_NUM_IMAGE_SIZES 2 +#define OV3640_NUM_PIXEL_FORMATS 4 +#define OV3640_NUM_FPS 3 + +struct ov3640_capture_size { + unsigned long width; + unsigned long height; +}; + +const static struct ov3640_reg ov3640_common[2][100] = { + /* XGA_Default settings */ + { + {OV3640_AEC_H, 0x03}, + {OV3640_AEC_L, 0x0F}, + {OV3640_AGC_L, 0x07}, + {0x304d, 0x45}, + {0x30aa, 0x45}, + {OV3640_IO_CTRL1, 0xff}, + {OV3640_IO_CTRL2, 0x10}, + {OV3640_WPT_HISH, 0x38}, + {OV3640_BPT_HISL, 0x30}, + {OV3640_VPT, 0x61}, + {0x3082, 0x20}, + {OV3640_AUTO_3, OV3640_AUTO_3_DUMMYFC_1FRAME | + OV3640_AUTO_3_AGCGAINCEIL_32X}, + {OV3640_AUTO_1, OV3640_AUTO_1_FASTAEC | + OV3640_AUTO_1_AECBIGSTEPS | + OV3640_AUTO_1_BANDINGFILTEREN | + OV3640_AUTO_1_AUTOBANDINGFILTER | + OV3640_AUTO_1_EXTRBRIGHTEXPEN +#if (OV3640_RAW_MODE == 0) + | OV3640_AUTO_1_AGCEN + | OV3640_AUTO_1_AECEN +#endif + }, + {OV3640_AHW_H, 0x08}, + {OV3640_AHW_L, 0x18}, + {OV3640_AVH_H, 0x06}, + {OV3640_AVH_L, 0x0c}, + {OV3640_WEIGHT0, 0x62}, + {OV3640_WEIGHT1, 0x26}, + {OV3640_WEIGHT2, 0xe6}, + {OV3640_WEIGHT3, 0x6e}, + {OV3640_WEIGHT4, 0xea}, + {OV3640_WEIGHT5, 0xae}, + {OV3640_WEIGHT6, 0xa6}, + {OV3640_WEIGHT7, 0x6a}, + {OV3640_SC_SYN_CTRL0, 0x02}, + {OV3640_SC_SYN_CTRL1, 0xfd}, + {OV3640_SC_SYN_CTRL2, 0x00}, + {OV3640_SC_SYN_CTRL3, 0xff}, + {OV3640_DSP_CTRL_0, 0x13}, + {OV3640_DSP_CTRL_1, 0xde}, + {OV3640_DSP_CTRL_2, 0xef}, + {0x3316, 0xff}, + {0x3317, 0x00}, + {0x3312, 0x26}, + {0x3314, 0x42}, + {0x3313, 0x2b}, + {0x3315, 0x42}, + {0x3310, 0xd0}, + {0x3311, 0xbd}, + {0x330c, 0x18}, + {0x330d, 0x18}, + {0x330e, 0x56}, + {0x330f, 0x5c}, + {0x330b, 0x1c}, + {0x3306, 0x5c}, + {0x3307, 0x11}, + {OV3640_R_A1, 0x52}, + {OV3640_G_A1, 0x46}, + {OV3640_B_A1, 0x38}, + {OV3640_DSPC0, 0x20}, + {OV3640_DSPC1, 0x17}, + {OV3640_DSPC2, 0x04}, + {OV3640_DSPC3, 0x08}, + {0x3507, 0x06}, + {0x350a, 0x4f}, + {OV3640_SC_CTRL0, 0x02}, + {OV3640_DSP_CTRL_1, 0xde}, + {OV3640_DSP_CTRL_4, 0xfc}, + {OV3640_SYS, OV3640_SYS_BASERES_XGA}, + {OV3640_VS_L, 0x06 + 1}, + {OV3640_VH_H, 0x03}, + {OV3640_VH_L, 0x04}, + {OV3640_VSYNCOPT, 0x24}, + {OV3640_PCLK, OV3640_PCLK_DIVBY2}, + {0x30d7, 0x90}, + {OV3640_SIZE_IN_MISC, 0x34}, + {OV3640_HSIZE_IN_L, 0x0c}, + {OV3640_VSIZE_IN_L, 0x04}, + {OV3640_SIZE_OUT_MISC, 0x34}, + {OV3640_HSIZE_OUT_L, 0x08}, + {OV3640_VSIZE_OUT_L, 0x04}, + {OV3640_ISP_PAD_CTR2, 0x42}, + {OV3640_ISP_XOUT_H, 0x04}, + {OV3640_ISP_XOUT_L, 0x00}, + {OV3640_ISP_YOUT_H, 0x03}, + {OV3640_ISP_YOUT_L, 0x00}, + {OV3640_TMC13, 0x04}, + {OV3640_OUT_CTRL00, OV3640_OUT_CTRL00_VSYNCSEL2 | + OV3640_OUT_CTRL00_VSYNCGATE | + OV3640_OUT_CTRL00_VSYNCPOL_NEG}, + {OV3640_MISC_CTRL, 0x00}, + {OV3640_Y_EDGE_MT, 0x60}, + {OV3640_BASE1, 0x03}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* QXGA Default settings */ + { + {OV3640_AEC_H, 0x06}, + {OV3640_AEC_L, 0x1F}, + {OV3640_AGC_L, 0x12}, + {0x304d, 0x45}, + {0x30aa, 0x45}, + {OV3640_IO_CTRL0, 0xff}, + {OV3640_IO_CTRL1, 0xff}, + {OV3640_IO_CTRL2, 0x10}, + {0x30d7, 0x10}, + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x60}, + {OV3640_BPT_HISL, 0x58}, + {OV3640_VPT, 0xa1}, + {OV3640_TMC11, 0x02}, + {0x3082, 0x20}, + {OV3640_AHW_H, 0x08}, + {OV3640_AHW_L, 0x18}, + {OV3640_AVH_H, 0x06}, + {OV3640_AVH_L, 0x0c}, + {OV3640_WEIGHT0, 0x62}, + {OV3640_WEIGHT1, 0x26}, + {OV3640_WEIGHT2, 0xe6}, + {OV3640_WEIGHT3, 0x6e}, + {OV3640_WEIGHT4, 0xea}, + {OV3640_WEIGHT5, 0xae}, + {OV3640_WEIGHT6, 0xa6}, + {OV3640_WEIGHT7, 0x6a}, + {OV3640_AUTO_3, OV3640_AUTO_3_DUMMYFC_1FRAME | + OV3640_AUTO_3_AGCGAINCEIL_8X}, + {OV3640_AUTO_1, OV3640_AUTO_1_FASTAEC | + OV3640_AUTO_1_AECBIGSTEPS | + OV3640_AUTO_1_BANDINGFILTEREN | + OV3640_AUTO_1_AUTOBANDINGFILTER | + OV3640_AUTO_1_EXTRBRIGHTEXPEN +#if (OV3640_RAW_MODE == 0) + | OV3640_AUTO_1_AGCEN + | OV3640_AUTO_1_AECEN +#endif + }, + {OV3640_SC_SYN_CTRL0, 0x02}, + {OV3640_SC_SYN_CTRL1, 0xfd}, + {OV3640_SC_SYN_CTRL2, 0x00}, + {OV3640_SC_SYN_CTRL3, 0xff}, + {OV3640_AWB_CTRL_3, 0xa5}, + {0x3316, 0xff}, + {0x3317, 0x00}, + {OV3640_TMC11, 0x02}, + {0x3082, 0x20}, + {OV3640_DSP_CTRL_0, 0x13}, + {OV3640_DSP_CTRL_1, 0xd6}, + {OV3640_DSP_CTRL_2, 0xef}, + {OV3640_DSPC0, 0x20}, + {OV3640_DSPC1, 0x17}, + {OV3640_DSPC2, 0x04}, + {OV3640_DSPC3, 0x08}, + {OV3640_HS_H, 0x01}, + {OV3640_HS_L, 0x1d}, + {OV3640_VS_H, 0x00}, + {OV3640_VS_L, 0x0a + 1}, + {OV3640_HW_H, 0x08}, + {OV3640_HW_L, 0x18}, + {OV3640_VH_H, 0x06}, + {OV3640_VH_L, 0x0c}, + {OV3640_SIZE_IN_MISC, 0x68}, + {OV3640_HSIZE_IN_L, 0x18}, + {OV3640_VSIZE_IN_L, 0x0c}, + {OV3640_SIZE_OUT_MISC, 0x68}, + {OV3640_HSIZE_OUT_L, 0x08}, + {OV3640_VSIZE_OUT_L, 0x04}, + {OV3640_ISP_PAD_CTR2, 0x42}, + {OV3640_ISP_XOUT_H, 0x08}, + {OV3640_ISP_XOUT_L, 0x00}, + {OV3640_ISP_YOUT_H, 0x06}, + {OV3640_ISP_YOUT_L, 0x00}, + {0x3507, 0x06}, + {0x350a, 0x4f}, + {OV3640_OUT_CTRL00, 0xc4}, + /* Light Mode - Auto */ + {OV3640_MISC_CTRL, 0x00}, + /* Sharpness - Level 5 */ + {OV3640_Y_EDGE_MT, 0x45}, + /* Sharpness - Auto */ + {OV3640_Y_EDGE_MT, 0x60}, + {OV3640_BASE1, 0x03}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, +}; + +const static struct ov3640_reg ov3640_common_csi2[] = { + /* NM OUT_CONTROL2 SOL/EOL off */ + {OV3640_MIPI_CTRL02, 0x22}, + /* NM OUT_CONTROL1E h_sel? */ + {OV3640_OUT_CTRL1E, 0x00}, + /* min_hs_zero: 6UI + 105ns */ + {OV3640_MIPI_CTRL22, ((6 & 0x3F) << 2) | ((105 & 0x300) >> 8)}, + {OV3640_MIPI_CTRL23, (105 & 0xFF)}, + /* min_clk_zero: 240ns */ + {OV3640_MIPI_CTRL26, ((0 & 0x3F) << 2) | ((240 & 0x300) >> 8)}, + {OV3640_MIPI_CTRL27, (240 & 0xFF)}, + /* min_clk_prepare: 38ns */ + {OV3640_MIPI_CTRL28, ((0 & 0x3F) << 2) | ((38 & 0x300) >> 8)}, + {OV3640_MIPI_CTRL29, (38 & 0xFF)}, + /* max_clk_prepare: 95ns */ + {OV3640_MIPI_CTRL2A, ((0 & 0x3F) << 2) | ((95 & 0x300) >> 8)}, + {OV3640_MIPI_CTRL2B, (95 & 0xFF)}, + /* min_clk_post: 52UI + 60ns */ + {OV3640_MIPI_CTRL2C, ((52 & 0x3F) << 2) | ((60 & 0x300) >> 8)}, + {OV3640_MIPI_CTRL2D, (60 & 0xFF)}, + /* min_hs_prepare: 4UI + 40ns */ + {OV3640_MIPI_CTRL32, ((4 & 0x3F) << 2) | ((40 & 0x300) >> 8)}, + {OV3640_MIPI_CTRL33, (40 & 0xFF)}, + /* ph_byte_order {DI,WC_h,WC_l} */ + {OV3640_MIPI_CTRL03, 0x49 | OV3640_MIPI_CTRL03_ECC_PHBYTEORDER}, + /* ph_byte_order2 ph={WC,DI} */ + {OV3640_MIPI_CTRL4C, OV3640_MIPI_CTRL4C_ECC_PHBYTEORDER2}, + {0x309e, 0x00}, + {OV3640_REG_TERM, OV3640_VAL_TERM}, +}; + +/* Array of image sizes supported by OV3640. These must be ordered from + * smallest image size to largest. + */ +const static struct ov3640_capture_size ov3640_sizes[] = { + /* XGA */ + { 1024, 768 }, + /* QXGA */ + { 2048, 1536 }, +}; + +/** + * struct ov3640_sensor - main structure for storage of sensor information + * @pdata: access functions and data for platform level information + * @v4l2_int_device: V4L2 device structure structure + * @i2c_client: iic client device structure + * @pix: V4L2 pixel format information structure + * @timeperframe: time per frame expressed as V4L fraction + * @isize: base image size + * @ver: ov3640 chip version + * @width: configured width + * @height: configuredheight + * @vsize: vertical size for the image + * @hsize: horizontal size for the image + * @crop_rect: crop rectangle specifying the left,top and width and height + */ +struct ov3640_sensor { + struct device *dev; + struct ov3640_platform_data *pdata; + struct v4l2_int_device *v4l2_int_device; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; + int isize; + int ver; + int fps; + unsigned long width; + unsigned long height; + unsigned long vsize; + unsigned long hsize; + struct v4l2_rect crop_rect; + int detected; +}; + +/* List of image formats supported by OV3640 sensor */ +const static struct v4l2_fmtdesc ov3640_formats[] = { +#if OV3640_RAW_MODE + { + .description = "RAW10", + .pixelformat = V4L2_PIX_FMT_SGRBG10, + }, +#else + { + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + .description = "RGB565, be", + .pixelformat = V4L2_PIX_FMT_RGB565X, + }, + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "UYVY, packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + .description = "RGB555, le", + .pixelformat = V4L2_PIX_FMT_RGB555, + }, + { + .description = "RGB555, be", + .pixelformat = V4L2_PIX_FMT_RGB555X, + }, +#endif +}; + +#define OV3640_NUM_CAPTURE_FORMATS \ + (sizeof(ov3640_formats) / sizeof(ov3640_formats[0])) + +/* register initialization tables for ov3640 */ + +const static struct ov3640_reg ov3640_out_xga[] = { + {OV3640_ISP_XOUT_H, 0x04}, /* ISP_XOUT */ + {OV3640_ISP_XOUT_L, 0x00}, /* ISP_XOUT */ + {OV3640_ISP_YOUT_H, 0x03}, /* ISP_YOUT */ + {OV3640_ISP_YOUT_L, 0x00}, /* ISP_YOUT */ + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg ov3640_out_qxga[] = { + {OV3640_ISP_XOUT_H, 0x08}, /* ISP_XOUT */ + {OV3640_ISP_XOUT_L, 0x00}, /* ISP_XOUT */ + {OV3640_ISP_YOUT_H, 0x06}, /* ISP_YOUT */ + {OV3640_ISP_YOUT_L, 0x00}, /* ISP_YOUT */ + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +/* Brightness Settings - 7 levels */ +const static struct ov3640_reg brightness[7][5] = { + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_SGNSET, 0x09}, + {OV3640_YBRIGHT, 0x30}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_SGNSET, 0x09}, + {OV3640_YBRIGHT, 0x20}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_SGNSET, 0x09}, + {OV3640_YBRIGHT, 0x10}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_SGNSET, 0x01}, + {OV3640_YBRIGHT, 0x00}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_SGNSET, 0x01}, + {OV3640_YBRIGHT, 0x10}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_SGNSET, 0x01}, + {OV3640_YBRIGHT, 0x20}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_SGNSET, 0x01}, + {OV3640_YBRIGHT, 0x30}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, +}; + +/* Contrast Settings - 7 levels */ +const static struct ov3640_reg contrast[7][5] = { + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_YOFFSET, 0x14}, + {OV3640_YGAIN, 0x14}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_YOFFSET, 0x18}, + {OV3640_YGAIN, 0x18}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_YOFFSET, 0x1c}, + {OV3640_YGAIN, 0x1c}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_YOFFSET, 0x20}, + {OV3640_YGAIN, 0x20}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_YOFFSET, 0x24}, + {OV3640_YGAIN, 0x24}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_YOFFSET, 0x28}, + {OV3640_YGAIN, 0x28}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x04}, + {OV3640_YOFFSET, 0x2c}, + {OV3640_YGAIN, 0x2c}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, +}; + +/* Color Settings - 3 colors */ +const static struct ov3640_reg colors[3][5] = { + { + {OV3640_SDE_CTRL, 0x00}, + {OV3640_UREG, 0x80}, + {OV3640_VREG, 0x80}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x18}, + {OV3640_UREG, 0x80}, + {OV3640_VREG, 0x80}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + { + {OV3640_SDE_CTRL, 0x18}, + {OV3640_UREG, 0x40}, + {OV3640_VREG, 0xa6}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, +}; + +/* Average Based Algorithm - Based on target Luminance */ +const static struct ov3640_reg exposure_avg[11][5] = { + /* -1.7EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x10}, + {OV3640_BPT_HISL, 0x08}, + {OV3640_VPT, 0x21}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -1.3EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x18}, + {OV3640_BPT_HISL, 0x10}, + {OV3640_VPT, 0x31}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -1.0EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x20}, + {OV3640_BPT_HISL, 0x18}, + {OV3640_VPT, 0x41}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -0.7EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x28}, + {OV3640_BPT_HISL, 0x20}, + {OV3640_VPT, 0x51}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -0.3EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x30}, + {OV3640_BPT_HISL, 0x28}, + {OV3640_VPT, 0x61}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* default */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x38}, + {OV3640_BPT_HISL, 0x30}, + {OV3640_VPT, 0x61}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 0.3EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x40}, + {OV3640_BPT_HISL, 0x38}, + {OV3640_VPT, 0x71}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 0.7EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x48}, + {OV3640_BPT_HISL, 0x40}, + {OV3640_VPT, 0x81}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 1.0EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x50}, + {OV3640_BPT_HISL, 0x48}, + {OV3640_VPT, 0x91}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 1.3EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x58}, + {OV3640_BPT_HISL, 0x50}, + {OV3640_VPT, 0x91}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 1.7EV */ + { + {OV3640_HISTO7, 0x00}, + {OV3640_WPT_HISH, 0x60}, + {OV3640_BPT_HISL, 0x58}, + {OV3640_VPT, 0xa1}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, +}; + +/* Histogram Based Algorithm - Based on histogram and probability */ +const static struct ov3640_reg exposure_hist[11][5] = { + /* -1.7EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x58}, + {OV3640_BPT_HISL, 0x38}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -1.3EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x60}, + {OV3640_BPT_HISL, 0x40}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -1.0EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x68}, + {OV3640_BPT_HISL, 0x48}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -0.7EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x70}, + {OV3640_BPT_HISL, 0x50}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* -0.3EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x78}, + {OV3640_BPT_HISL, 0x58}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* default */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x80}, + {OV3640_BPT_HISL, 0x60}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 0.3EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x88}, + {OV3640_BPT_HISL, 0x68}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 0.7EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x90}, + {OV3640_BPT_HISL, 0x70}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 1.0EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0x98}, + {OV3640_BPT_HISL, 0x78}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 1.3EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0xa0}, + {OV3640_BPT_HISL, 0x80}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, + /* 1.7EV */ + { + {OV3640_HISTO7, 0x80}, + {OV3640_WPT_HISH, 0xa8}, + {OV3640_BPT_HISL, 0x88}, + {OV3640_REG_TERM, OV3640_VAL_TERM} + }, +}; + +/* ov3640 register configuration for combinations of pixel format and + * image size + */ + +const static struct ov3640_reg qxga_yuv[] = { + {OV3640_SC_CTRL0, 0x02}, + {OV3640_DSP_CTRL_4, 0xFC}, + {OV3640_FMT_MUX_CTRL0, 0x00}, + {OV3640_FMT_CTRL00, 0x00}, + {OV3640_OUT_CTRL01, OV3640_OUT_CTRL01_MIPIBIT8}, + {OV3640_VTS_H, 0x06}, + {OV3640_VTS_L, 0x20}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg qxga_565[] = { + {OV3640_SC_CTRL0, 0x02}, + {OV3640_DSP_CTRL_4, 0xFC}, + {OV3640_FMT_MUX_CTRL0, 0x01}, + {OV3640_FMT_CTRL00, 0x11}, + {OV3640_OUT_CTRL01, OV3640_OUT_CTRL01_MIPIBIT8}, + {OV3640_VTS_H, 0x06}, + {OV3640_VTS_L, 0x20}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg qxga_555[] = { + {OV3640_SC_CTRL0, 0x02}, + {OV3640_DSP_CTRL_4, 0xFC}, + {OV3640_FMT_MUX_CTRL0, 0x01}, + {OV3640_FMT_CTRL00, 0x13}, + {OV3640_OUT_CTRL01, OV3640_OUT_CTRL01_MIPIBIT8}, + {OV3640_VTS_H, 0x06}, + {OV3640_VTS_L, 0x20}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg qxga_raw10[] = { + {OV3640_SC_CTRL0, 0x22}, + {OV3640_DSP_CTRL_4, 0x01}, + {OV3640_FMT_MUX_CTRL0, 0x04}, + {OV3640_FMT_CTRL00, 0x18}, + {OV3640_OUT_CTRL01, 0x00}, + {OV3640_VTS_H, 0x06}, + {OV3640_VTS_L, 0x20}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg xga_yuv[] = { + {OV3640_SC_CTRL0, 0x02}, + {OV3640_DSP_CTRL_4, 0xFC}, + {OV3640_FMT_MUX_CTRL0, 0x00}, + {OV3640_FMT_CTRL00, 0x00}, + {OV3640_OUT_CTRL01, OV3640_OUT_CTRL01_MIPIBIT8}, + {OV3640_VTS_H, 0x03}, + {OV3640_VTS_L, 0x10}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg xga_565[] = { + {OV3640_SC_CTRL0, 0x02}, + {OV3640_DSP_CTRL_4, 0xFC}, + {OV3640_FMT_MUX_CTRL0, 0x01}, + {OV3640_FMT_CTRL00, 0x11}, + {OV3640_OUT_CTRL01, OV3640_OUT_CTRL01_MIPIBIT8}, + {OV3640_VTS_H, 0x03}, + {OV3640_VTS_L, 0x10}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg xga_555[] = { + {OV3640_SC_CTRL0, 0x02}, + {OV3640_DSP_CTRL_4, 0xFC}, + {OV3640_FMT_MUX_CTRL0, 0x01}, + {OV3640_FMT_CTRL00, 0x13}, + {OV3640_OUT_CTRL01, OV3640_OUT_CTRL01_MIPIBIT8}, + {OV3640_VTS_H, 0x03}, + {OV3640_VTS_L, 0x10}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg xga_raw10[] = { + {OV3640_SC_CTRL0, 0x22}, + {OV3640_DSP_CTRL_4, 0x01}, + {OV3640_FMT_MUX_CTRL0, 0x04}, + {OV3640_FMT_CTRL00, 0x18}, + {OV3640_OUT_CTRL01, 0x00}, + {OV3640_VTS_H, 0x03}, + {OV3640_VTS_L, 0x10}, + {OV3640_REG_TERM, OV3640_VAL_TERM} +}; + +const static struct ov3640_reg + *ov3640_reg_init[OV3640_NUM_PIXEL_FORMATS][OV3640_NUM_IMAGE_SIZES] = { + {xga_yuv, qxga_yuv}, + {xga_565, qxga_565}, + {xga_555, qxga_555}, + {xga_raw10, qxga_raw10} +}; + +/* + * struct vcontrol - Video controls + * @v4l2_queryctrl: V4L2 VIDIOC_QUERYCTRL ioctl structure + * @current_value: current value of this control + */ +static struct vcontrol { + struct v4l2_queryctrl qc; + int current_value; +} video_control[] = { +#if (OV3640_RAW_MODE == 0) + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = OV3640_MIN_BRIGHT, + .maximum = OV3640_MAX_BRIGHT, + .step = OV3640_BRIGHT_STEP, + .default_value = OV3640_DEF_BRIGHT, + }, + .current_value = OV3640_DEF_BRIGHT, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = OV3640_MIN_CONTRAST, + .maximum = OV3640_MAX_CONTRAST, + .step = OV3640_CONTRAST_STEP, + .default_value = OV3640_DEF_CONTRAST, + }, + .current_value = OV3640_DEF_CONTRAST, + }, + { + { + .id = V4L2_CID_COLORFX, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Color Effects", + .minimum = V4L2_COLORFX_NONE, + .maximum = V4L2_COLORFX_SEPIA, + .step = 1, + .default_value = V4L2_COLORFX_NONE, + }, + .current_value = V4L2_COLORFX_NONE, + } +#endif +}; + +static struct v4l2_querymenu video_menu[] = { +#if (OV3640_RAW_MODE == 0) + { + .id = V4L2_CID_COLORFX, + .index = 0, + .name = "None", + }, + { + .id = V4L2_CID_COLORFX, + .index = 1, + .name = "B&W", + }, + { + .id = V4L2_CID_COLORFX, + .index = 2, + .name = "Sepia", + }, +#endif +}; + +/* + * find_vctrl - Finds the requested ID in the video control structure array + * @id: ID of control to search the video control array. + * + * Returns the index of the requested ID from the control structure array + */ +static int find_vctrl(int id) +{ + int i = 0; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--) + if (video_control[i].qc.id == id) + break; + if (i < 0) + i = -EINVAL; + return i; +} + +/** + * find_vmenu - Returns index of the menu array of the requested ctrl option. + * @id: Requested control ID. + * @index: Requested menu option index. + * + * Returns 0 if successful, -EINVAL if not found, or -EDOM if its out of + * domain. + **/ +static int find_vmenu(int id, int index) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(video_menu) - 1); i >= 0; i--) { + if ((video_menu[i].id != id) || (video_menu[i].index != index)) + continue; + return i; + } + + return -EINVAL; +} + +/* + * Read a value from a register in ov3640 sensor device. + * The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int ov3640_read_reg(struct i2c_client *client, u16 data_length, u16 reg, + u32 *val) +{ + int err = 0; + struct i2c_msg msg[1]; + unsigned char data[4]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 2; + msg->buf = data; + + /* High byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) { + mdelay(3); + msg->flags = I2C_M_RD; + msg->len = data_length; + err = i2c_transfer(client->adapter, msg, 1); + } + if (err >= 0) { + *val = 0; + /* High byte comes first */ + if (data_length == 1) + *val = data[0]; + else if (data_length == 2) + *val = data[1] + (data[0] << 8); + else + *val = data[3] + (data[2] << 8) + + (data[1] << 16) + (data[0] << 24); + return 0; + } + dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, err); + return err; +} + +/* Write a value to a register in ov3640 sensor device. + * @client: i2c driver client structure. + * @reg: Address of the register to read value from. + * @val: Value to be written to a specific register. + * Returns zero if successful, or non-zero otherwise. + */ +static int ov3640_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + int err = 0; + struct i2c_msg msg[1]; + unsigned char data[3]; + int retries = 0; + + if (!client->adapter) + return -ENODEV; +retry: + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 3; + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + data[2] = val; + + err = i2c_transfer(client->adapter, msg, 1); + udelay(50); + + if (err >= 0) + return 0; + + if (retries <= 5) { + dev_dbg(&client->dev, "Retrying I2C... %d\n", retries); + retries++; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(20)); + goto retry; + } + + return err; +} + +/* + * Initialize a list of ov3640 registers. + * The list of registers is terminated by the pair of values + * {OV3640_REG_TERM, OV3640_VAL_TERM}. + * @client: i2c driver client structure. + * @reglist[]: List of address of the registers to write data. + * Returns zero if successful, or non-zero otherwise. + */ +static int ov3640_write_regs(struct i2c_client *client, + const struct ov3640_reg reglist[]) +{ + int err = 0; + const struct ov3640_reg *next = reglist; + + while (!((next->reg == OV3640_REG_TERM) + && (next->val == OV3640_VAL_TERM))) { + err = ov3640_write_reg(client, next->reg, next->val); + udelay(100); + if (err) + return err; + next++; + } + return 0; +} + +/* Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + */ +static enum ov3640_image_size ov3640_find_size(unsigned int width, + unsigned int height) +{ + if ((width > ov3640_sizes[XGA].width) || + (height > ov3640_sizes[XGA].height)) + return QXGA; + return XGA; +} + +/* + * Set CSI2 Virtual ID. + */ +static int ov3640_set_virtual_id(struct i2c_client *client, u32 id) +{ + return ov3640_write_reg(client, OV3640_MIPI_CTRL0C, (0x3 & id) << 6 | + 0x02); +} + +/* + * Calculates the MIPIClk. + * 1) Calculate fclk + * fclk = (64 - OV3640_PLL_1[5:0]) * N * Bit8Div * MCLK / M + * where N = 1/1.5/2/3 for OV3640_PLL_2[7:6]=0~3 + * M = 1/1.5/2/3 for OV3640_PLL_2[1:0]=0~3 + * Bit8Div = 1/1/4/5 for OV3640_PLL_2[5:4] + * 2) Calculate MIPIClk + * MIPIClk = fclk / ScaleDiv / MIPIDiv + * = fclk * (1/ScaleDiv) / MIPIDiv + * where 1/ScaleDiv = 0x3010[3:0]*2 + * MIPIDiv = 0x3010[5] + 1 + * NOTE: + * - The lookup table 'lut1' has been multiplied by 2 so all its values + * are integers. Since both N & M use the same table, and they are + * used as a ratio then the factor of 2 is already take into account. + * i.e. 2N/2M = N/M + */ +static u32 ov3640_calc_mipiclk(struct v4l2_int_device *s) +{ + struct ov3640_sensor *sensor = s->priv; + struct i2c_client *client = to_i2c_client(sensor->dev); + u32 rxpll, val, n, m, bit8div; + u32 sdiv_inv, mipidiv; + u32 fclk, mipiclk, mclk = 24000000; + u8 lut1[4] = {2, 3, 4, 6}; + u8 lut2[4] = {1, 1, 4, 5}; + + /* Calculate fclk */ + ov3640_read_reg(client, 1, OV3640_PLL_1, &val); + rxpll = val & 0x3F; + + ov3640_read_reg(client, 1, OV3640_PLL_2, &val); + n = lut1[(val >> 6) & 0x3]; + m = lut1[val & 0x3]; + bit8div = lut2[(val >> 4) & 0x3]; + fclk = (64 - rxpll) * n * bit8div * mclk / m; + + ov3640_read_reg(client, 1, OV3640_PLL_3, &val); + mipidiv = ((val >> 5) & 1) + 1; + sdiv_inv = (val & 0xF) * 2; + + if ((val & 0xF) >= 1) + mipiclk = fclk / sdiv_inv / mipidiv; + else + mipiclk = fclk / mipidiv; + dev_dbg(&client->dev, "mipiclk=%u fclk=%u val&0xF=%u sdiv_inv=%u " + "mipidiv=%u\n", + mipiclk, fclk, val&0xF, + sdiv_inv, mipidiv); + return mipiclk; +} + +/** + * ov3640_set_framerate + **/ +static int ov3640_set_framerate(struct i2c_client *client, + struct v4l2_fract *fper, + enum ov3640_image_size isize) +{ + u32 tempfps1, tempfps2; + u8 clkval; +/* + u32 origvts, newvts, templineperiod; + u32 origvts_h, origvts_l, newvts_h, newvts_l; +*/ + int err = 0; + + /* FIXME: QXGA framerate setting forced to 15 FPS */ + if (isize == QXGA) { + err = ov3640_write_reg(client, OV3640_PLL_1, 0x32); + err = ov3640_write_reg(client, OV3640_PLL_2, 0x21); + err = ov3640_write_reg(client, OV3640_PLL_3, 0x21); + err = ov3640_write_reg(client, OV3640_CLK, 0x01); + err = ov3640_write_reg(client, 0x304c, 0x81); + return err; + } + + tempfps1 = fper->denominator * 10000; + tempfps1 /= fper->numerator; + tempfps2 = fper->denominator / fper->numerator; + if ((tempfps1 % 10000) != 0) + tempfps2++; + clkval = (u8)((30 / tempfps2) - 1); + + err = ov3640_write_reg(client, OV3640_CLK, clkval); + /* RxPLL = 50d = 32h */ + err = ov3640_write_reg(client, OV3640_PLL_1, 0x32); + /* RxPLL = 50d = 32h */ + err = ov3640_write_reg(client, OV3640_PLL_2, + OV3640_PLL_2_BIT8DIV_4 | + OV3640_PLL_2_INDIV_1_5); + /* + * NOTE: Sergio's Fix for MIPI CLK timings, not suggested by OV + */ + err = ov3640_write_reg(client, OV3640_PLL_3, 0x21 + + (clkval & 0xF)); + /* Setting DVP divisor value */ + err = ov3640_write_reg(client, 0x304c, 0x82); +/* FIXME: Time adjustment to add granularity to the available fps */ +/* + ov3640_read_reg(client, 1, OV3640_VTS_H, &origvts_h); + ov3640_read_reg(client, 1, OV3640_VTS_L, &origvts_l); + origvts = (u32)((origvts_h << 8) + origvts_l); + templineperiod = 1000000 / (tempfps2 * origvts); + newvts = 1000000 / (tempfps2 * templineperiod); + newvts_h = (u8)((newvts & 0xFF00) >> 8); + newvts_l = (u8)(newvts & 0xFF); + err = ov3640_write_reg(client, OV3640_VTS_H, newvts_h); + err = ov3640_write_reg(client, OV3640_VTS_L, newvts_l); +*/ + return err; +} + +/* + * Configure the ov3640 for a specified image size, pixel format, and frame + * period. xclk is the frequency (in Hz) of the xclk input to the OV3640. + * fper is the frame period (in seconds) expressed as a fraction. + * Returns zero if successful, or non-zero otherwise. + * The actual frame period is returned in fper. + */ +static int ov3640_configure(struct v4l2_int_device *s) +{ + struct ov3640_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &sensor->pix; + struct i2c_client *client = to_i2c_client(sensor->dev); + enum ov3640_image_size isize = XGA; + unsigned char hsize_l = 0, hsize_h = 0; + unsigned char vsize_l = 0, vsize_h = 0; + int vsize = 0, hsize = 0, height_l = 0, height_h = 0, width_l = 0; + int width_h = 0, ratio = 0, err = 0; + u32 mipiclk; + enum ov3640_pixel_format pfmt = YUV; + u32 min_hs_zero_nui, min_hs_zero, min_hs_zero_total; + u32 min_hs_prepare_nui, min_hs_prepare, min_hs_prepare_total; + u32 max_hs_prepare_nui, max_hs_prepare, max_hs_prepare_total; + u32 ubound_hs_settle, lbound_hs_settle; + u32 val; + + switch (pix->pixelformat) { + + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + pfmt = RGB565; + break; + + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + pfmt = RGB555; + break; + + case V4L2_PIX_FMT_SGRBG10: + pfmt = RAW10; + break; + + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pfmt = YUV; + } + + /* Set receivers virtual channel before sensor setup starts. + * Only set the sensors virtual channel after all other setup + * for the sensor is complete. + */ + isp_csi2_ctx_config_virtual_id(0, OV3640_CSI2_VIRTUAL_ID); + isp_csi2_ctx_update(0, false); + + if (ov3640_find_size(pix->width, pix->height) == XGA) + isize = XGA; + else + isize = QXGA; + + /* Reset */ + ov3640_write_reg(client, OV3640_SYS, 0x80); + mdelay(5); + + /* Common registers */ + err = ov3640_write_regs(client, ov3640_common[isize]); + + /* Configure image size and pixel format */ + err = ov3640_write_regs(client, ov3640_reg_init[pfmt][isize]); + + /* Setting of frame rate (OV suggested way) */ + err = ov3640_set_framerate(client, &sensor->timeperframe, isize); +#ifdef CONFIG_VIDEO_OV3640_CSI2 + /* Set CSI2 common register settings */ + err = ov3640_write_regs(client, ov3640_common_csi2); +#endif + sensor->isize = isize; + + /* Scale image if needed*/ + if (((pix->width == ov3640_sizes[QXGA].width) && + (pix->height == ov3640_sizes[QXGA].height) && (isize == QXGA)) || + ((pix->width == ov3640_sizes[XGA].width) && + (pix->height == ov3640_sizes[XGA].height) && (isize == XGA)) || + (pfmt == RAW10)) { + /* if the image size correspond to one of the base image sizes + then we don't need to scale the image */ + sensor->hsize = pix->width; + sensor->vsize = pix->height; + + if (isize == XGA) + ov3640_write_regs(client, ov3640_out_xga); + else + ov3640_write_regs(client, ov3640_out_qxga); + + } else { + /* Default Ver and Hor sizes for QXGA and XGA*/ + if (isize == QXGA) { + vsize = 0x600;/* 0x60c; */ + hsize = 0x800;/* 0x818; */ + } else { + vsize = 0x304; + hsize = 0x40c; + } + /* Scaling */ + /* Adjust V and H sizes for image sizes not derived form VGA*/ + ratio = (pix->width * 1000) / pix->height; + + if (((vsize * ratio + 500) / 1000) > hsize) + vsize = (hsize * 1000) / ratio ; + + else + hsize = (vsize * ratio + 500) / 1000; + + /* We need even numbers */ + if (vsize & 1) + vsize--; + if (hsize & 1) + hsize--; + + /* Adjusting numbers to set registers correctly */ + hsize_l = (0xFF & hsize); + hsize_h = (0xF00 & hsize) >> 8; + vsize_l = (0xFF & vsize); + vsize_h = (0x700 & vsize) >> 4; + + /* According to Software app notes we have to add 0x08 and 0x04 + * in order to scale correctly + */ + width_l = (0xFF & pix->width) + 0x08; + width_h = (0xF00 & pix->width) >> 8; + height_l = (0xFF & pix->height) + 0x04; + height_h = (0x700 & pix->height) >> 4; + + err = ov3640_write_reg(client, OV3640_SIZE_IN_MISC, + (vsize_h | hsize_h)); + err = ov3640_write_reg(client, OV3640_HSIZE_IN_L, hsize_l); + err = ov3640_write_reg(client, OV3640_VSIZE_IN_L, vsize_l); + err = ov3640_write_reg(client, OV3640_SIZE_OUT_MISC, + (height_h | width_h)); + err = ov3640_write_reg(client, OV3640_HSIZE_OUT_L, width_l); + err = ov3640_write_reg(client, OV3640_VSIZE_OUT_L, height_l); + err = ov3640_write_reg(client, OV3640_ISP_PAD_CTR2, 0x42); + err = ov3640_write_reg(client, OV3640_ISP_XOUT_H, width_h); + err = ov3640_write_reg(client, OV3640_ISP_XOUT_L, + (width_l - 0x08)); + err = ov3640_write_reg(client, OV3640_ISP_YOUT_H, + (height_h >> 4)); + err = ov3640_write_reg(client, OV3640_ISP_YOUT_L, + (height_l - 0x04)); + + sensor->hsize = hsize; + sensor->vsize = vsize; + + dev_dbg(&client->dev, "HSIZE_IN =%i VSIZE_IN =%i\n", hsize, + vsize); + dev_dbg(&client->dev, "HSIZE_OUT=%u VSIZE_OUT=%u\n", + (pix->width + 8), + (pix->height + 4)); + dev_dbg(&client->dev, "ISP_XOUT =%u ISP_YOUT =%u\n", + pix->width, + pix->height); + } + + /* Setup the ISP VP based on image format */ + if (pix->pixelformat == V4L2_PIX_FMT_SGRBG10) { + isp_csi2_ctrl_config_vp_out_ctrl(2); + isp_csi2_ctrl_update(false); + } else { + isp_csi2_ctrl_config_vp_out_ctrl(1); + isp_csi2_ctrl_update(false); + } + + /* Store image size */ + sensor->width = pix->width; + sensor->height = pix->height; + + sensor->crop_rect.left = 0; + sensor->crop_rect.width = pix->width; + sensor->crop_rect.top = 0; + sensor->crop_rect.height = pix->height; + +#ifdef CONFIG_VIDEO_OV3640_CSI2 + mipiclk = ov3640_calc_mipiclk(s); + + /* Calculate Valid bounds for High speed settle timing in UIs */ + ov3640_read_reg(client, 1, OV3640_MIPI_CTRL22, &val); + min_hs_zero_nui = ((val & OV3640_MIPI_CTRL22_MIN_HS_ZERO_NUI_MASK) >> + OV3640_MIPI_CTRL22_MIN_HS_ZERO_NUI_SHIFT); + min_hs_zero = ((val & OV3640_MIPI_CTRL22_MIN_HS_ZERO_H_MASK) << 8); + ov3640_read_reg(client, 1, OV3640_MIPI_CTRL23, &val); + min_hs_zero |= (val & OV3640_MIPI_CTRL23_MIN_HS_ZERO_L_MASK); + min_hs_zero_total = ((min_hs_zero_nui * 1000000 * 1000) / mipiclk) + + min_hs_zero; + + ov3640_read_reg(client, 1, OV3640_MIPI_CTRL32, &val); + min_hs_prepare_nui = ((val & + OV3640_MIPI_CTRL32_MIN_HS_PREPARE_NUI_MASK) >> + OV3640_MIPI_CTRL32_MIN_HS_PREPARE_NUI_SHIFT); + min_hs_prepare = ((val & + OV3640_MIPI_CTRL32_MIN_HS_PREPARE_H_MASK) << 8); + ov3640_read_reg(client, 1, OV3640_MIPI_CTRL33, &val); + min_hs_prepare |= (val & OV3640_MIPI_CTRL33_MIN_HS_PREPARE_L_MASK); + min_hs_prepare_total = ((min_hs_prepare_nui * 1000000 * 1000) / + mipiclk) + min_hs_prepare; + + ov3640_read_reg(client, 1, OV3640_MIPI_CTRL34, &val); + max_hs_prepare_nui = ((val & + OV3640_MIPI_CTRL34_MAX_HS_PREPARE_NUI_MASK) >> + OV3640_MIPI_CTRL34_MAX_HS_PREPARE_NUI_SHIFT); + max_hs_prepare = ((val & + OV3640_MIPI_CTRL34_MAX_HS_PREPARE_H_MASK) << 8); + ov3640_read_reg(client, 1, OV3640_MIPI_CTRL35, &val); + max_hs_prepare |= (val & OV3640_MIPI_CTRL35_MAX_HS_PREPARE_L_MASK); + max_hs_prepare_total = ((max_hs_prepare_nui * 1000000 * 1000) / + mipiclk) + max_hs_prepare; + + ubound_hs_settle = ((min_hs_zero_total + min_hs_prepare_total) * + ((mipiclk >> 1) / 1000000)) / 1000; + lbound_hs_settle = (max_hs_prepare_total * ((mipiclk >> 1) / + 1000000)) / 1000; + + /* Send settings to ISP-CSI2 Receiver PHY */ + isp_csi2_calc_phy_cfg0(mipiclk, lbound_hs_settle, ubound_hs_settle); + + /* Set sensors virtual channel*/ + ov3640_set_virtual_id(client, OV3640_CSI2_VIRTUAL_ID); +#endif + return err; +} + + +/* Detect if an ov3640 is present, returns a negative error number if no + * device is detected, or pidl as version number if a device is detected. + */ +static int ov3640_detect(struct i2c_client *client) +{ + u32 pidh, pidl; + + if (!client) + return -ENODEV; + + if (ov3640_read_reg(client, 1, OV3640_PIDH, &pidh)) + return -ENODEV; + + if (ov3640_read_reg(client, 1, OV3640_PIDL, &pidl)) + return -ENODEV; + + if ((pidh == OV3640_PIDH_MAGIC) && ((pidl == OV3640_PIDL_MAGIC1) || + (pidl == OV3640_PIDL_MAGIC2))) { + dev_info(&client->dev, "Detect success (%02X,%02X)\n", pidh, + pidl); + return pidl; + } + + return -ENODEV; +} + +/* To get the cropping capabilities of ov3640 sensor + * Returns zero if successful, or non-zero otherwise. + */ +static int ioctl_cropcap(struct v4l2_int_device *s, + struct v4l2_cropcap *cropcap) +{ + struct ov3640_sensor *sensor = s->priv; + + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = sensor->width; + cropcap->bounds.height = sensor->height; + cropcap->defrect = cropcap->bounds; + cropcap->pixelaspect.numerator = 1; + cropcap->pixelaspect.denominator = 1; + return 0; +} + +/* To get the current crop window for of ov3640 sensor + * Returns zero if successful, or non-zero otherwise. + */ +static int ioctl_g_crop(struct v4l2_int_device *s, struct v4l2_crop *crop) +{ + struct ov3640_sensor *sensor = s->priv; + + crop->c = sensor->crop_rect; + return 0; +} + +/* To set the crop window for of ov3640 sensor + * Returns zero if successful, or non-zero otherwise. + */ +static int ioctl_s_crop(struct v4l2_int_device *s, struct v4l2_crop *crop) +{ + struct ov3640_sensor *sensor = s->priv; + struct v4l2_rect *cur_rect; + unsigned long *cur_width, *cur_height; + int hstart, vstart, hsize, vsize, hsize_l, vsize_l, hsize_h, vsize_h; + int hratio, vratio, zoomfactor, err = 0; + + cur_rect = &sensor->crop_rect; + cur_width = &sensor->width; + cur_height = &sensor->height; + + if ((crop->c.left == cur_rect->left) && + (crop->c.width == cur_rect->width) && + (crop->c.top == cur_rect->top) && + (crop->c.height == cur_rect->height)) + return 0; + + /* out of range? then return the current crop rectangle */ + if ((crop->c.left + crop->c.width) > sensor->width || + (crop->c.top + crop->c.height) > sensor->height) { + crop->c = *cur_rect; + return 0; + } + + if (sensor->isize == QXGA) + zoomfactor = 1; + else + zoomfactor = 2; + + hratio = (sensor->hsize * 1000) / sensor->width; + vratio = (sensor->vsize * 1000) / sensor->height; + hstart = (((crop->c.left * hratio + 500) / 1000) * zoomfactor) + 0x11d; + vstart = (((crop->c.top * vratio + 500) / 1000) + 0x0a); + hsize = (crop->c.width * hratio + 500) / 1000; + vsize = (crop->c.height * vratio + 500) / 1000; + + if (vsize & 1) + vsize--; + if (hsize & 1) + hsize--; + + /* Adjusting numbers to set register correctly */ + hsize_l = (0xFF & hsize); + hsize_h = (0xF00 & hsize) >> 8; + vsize_l = (0xFF & vsize); + vsize_h = (0x700 & vsize) >> 4; + + if ((sensor->height > vsize) || (sensor->width > hsize)) + return -EINVAL; + + hsize = hsize * zoomfactor; +/* + err = ov3640_write_reg(client, OV3640_DSP_CTRL_2, 0xEF); + err = ov3640_write_reg(client, OV3640_SIZE_IN_MISC, (vsize_h | + hsize_h)); + err = ov3640_write_reg(client, OV3640_HSIZE_IN_L, hsize_l); + err = ov3640_write_reg(client, OV3640_VSIZE_IN_L, vsize_l); + err = ov3640_write_reg(client, OV3640_HS_H, (hstart >> 8) & 0xFF); + err = ov3640_write_reg(client, OV3640_HS_L, hstart & 0xFF); + err = ov3640_write_reg(client, OV3640_VS_H, (vstart >> 8) & 0xFF); + err = ov3640_write_reg(client, OV3640_VS_L, vstart & 0xFF); + err = ov3640_write_reg(client, OV3640_HW_H, ((hsize) >> 8) & 0xFF); + err = ov3640_write_reg(client, OV3640_HW_L, hsize & 0xFF); + err = ov3640_write_reg(client, OV3640_VH_H, ((vsize) >> 8) & 0xFF); + err = ov3640_write_reg(client, OV3640_VH_L, vsize & 0xFF); +*/ + if (err) + return err; + + /* save back */ + *cur_rect = crop->c; + + /* Setting crop too fast can cause frame out-of-sync. */ + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(20)); + return 0; +} + + +/* + * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the video_control[] array. Otherwise, returns -EINVAL if the + * control is not supported. + */ +static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc) +{ + int i; + + i = find_vctrl(qc->id); + if (i == -EINVAL) + qc->flags = V4L2_CTRL_FLAG_DISABLED; + + if (i < 0) + return -EINVAL; + + *qc = video_control[i].qc; + return 0; +} + +/** + * ioctl_queryctrl - Query V4L2 control from existing controls in OV3640. + * @a: Pointer to v4l2_queryctrl structure. It only needs the id field filled. + * + * Returns 0 if successful, or -EINVAL if not found in OV3640. + **/ +int ioctl_querymenu(struct v4l2_querymenu *a) +{ + int i; + + i = find_vmenu(a->id, a->index); + + if (i < 0) + return -EINVAL; + + *a = video_menu[i]; + return 0; +} + +/* + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ + +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + struct vcontrol *lvc; + int i; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + vc->value = lvc->current_value; + break; + case V4L2_CID_CONTRAST: + vc->value = lvc->current_value; + break; + case V4L2_CID_PRIVATE_BASE: + vc->value = lvc->current_value; + break; + } + return 0; +} + +/* + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = -EINVAL; + int i; + struct ov3640_sensor *sensor = s->priv; + struct i2c_client *client = to_i2c_client(sensor->dev); + struct vcontrol *lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + + lvc = &video_control[i]; + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + if (vc->value >= 0 && vc->value <= 6) { + retval = ov3640_write_regs(client, + brightness[vc->value]); + } else { + dev_err(&client->dev, + "Brightness level not supported\n"); + return -EINVAL; + } + break; + case V4L2_CID_CONTRAST: + if (vc->value >= 0 && vc->value <= 6) + retval = ov3640_write_regs(client, contrast[vc->value]); + else { + dev_err(&client->dev, "Contrast level not supported\n"); + return -EINVAL; + } + break; + case V4L2_CID_COLORFX: + if (vc->value >= 0 && vc->value <= 2) + retval = ov3640_write_regs(client, colors[vc->value]); + else { + dev_err(&client->dev, "Color effect not supported\n"); + return -EINVAL; + } + break; + } + if (!retval) + lvc->current_value = vc->value; + return retval; +} + +/* + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= OV3640_NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + default: + return -EINVAL; + } + + fmt->flags = ov3640_formats[index].flags; + strlcpy(fmt->description, ov3640_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = ov3640_formats[index].pixelformat; + + return 0; +} + +/* + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ + +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + int ifmt; + enum ov3640_image_size isize; + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->width > ov3640_sizes[QXGA].width) + pix->width = ov3640_sizes[QXGA].width; + if (pix->height > ov3640_sizes[QXGA].height) + pix->height = ov3640_sizes[QXGA].height; + + isize = ov3640_find_size(pix->width, pix->height); + pix->width = ov3640_sizes[isize].width; + pix->height = ov3640_sizes[isize].height; + + for (ifmt = 0; ifmt < OV3640_NUM_CAPTURE_FORMATS; ifmt++) { + if (pix->pixelformat == ov3640_formats[ifmt].pixelformat) + break; + } + if (ifmt == OV3640_NUM_CAPTURE_FORMATS) + ifmt = 0; + pix->pixelformat = ov3640_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width*2; + pix->sizeimage = pix->bytesperline*pix->height; + pix->priv = 0; + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + break; + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + pix->colorspace = V4L2_COLORSPACE_SRGB; + break; + } + return 0; +} + +/* + * ioctl_s_fmt_cap - V4L2 sensor interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct ov3640_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + int rval; + + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + sensor->pix = *pix; + + return 0; +} + +/* + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct ov3640_sensor *sensor = s->priv; + f->fmt.pix = sensor->pix; + + return 0; +} + +/* + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct ov3640_sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = sensor->timeperframe; + + return 0; +} + +/* + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + int rval = 0; + struct ov3640_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + struct v4l2_fract timeperframe_old; + int desired_fps; + + timeperframe_old = sensor->timeperframe; + sensor->timeperframe = *timeperframe; + + desired_fps = timeperframe->denominator / timeperframe->numerator; + if ((desired_fps < OV3640_MIN_FPS) || (desired_fps > OV3640_MAX_FPS)) + rval = -EINVAL; + + if (rval) + sensor->timeperframe = timeperframe_old; + else + *timeperframe = sensor->timeperframe; + + return rval; +} + +/* + * ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold sensor's private data address + * + * Returns device's (sensor's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct ov3640_sensor *sensor = s->priv; + + return sensor->pdata->priv_data_set(s, p); +} + +/* + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the sensor device (call ov3640_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * ov3640 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct ov3640_sensor *sensor = s->priv; + struct i2c_client *client = to_i2c_client(sensor->dev); + int err; + + err = ov3640_detect(client); + if (err < 0) { + dev_err(&client->dev, "Unable to detect sensor, err %d\n", + err); + sensor->detected = 0; + return err; + } + sensor->detected = 1; + sensor->ver = err; + dev_dbg(&client->dev, "Chip version 0x%02x detected\n", sensor->ver); + + return 0; +} + +/** + * ioctl_enum_framesizes - V4L2 sensor if handler for vidioc_int_enum_framesizes + * @s: pointer to standard V4L2 device structure + * @frms: pointer to standard V4L2 framesizes enumeration structure + * + * Returns possible framesizes depending on choosen pixel format + **/ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *frms) +{ + int ifmt; + + for (ifmt = 0; ifmt < OV3640_NUM_CAPTURE_FORMATS; ifmt++) { + if (frms->pixel_format == ov3640_formats[ifmt].pixelformat) + break; + } + /* Is requested pixelformat not found on sensor? */ + if (ifmt == OV3640_NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Do we already reached all discrete framesizes? */ + if (frms->index >= 2) + return -EINVAL; + + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = ov3640_sizes[frms->index].width; + frms->discrete.height = ov3640_sizes[frms->index].height; + + return 0; +} + +const struct v4l2_fract ov3640_frameintervals[] = { + { .numerator = 2, .denominator = 15 }, + { .numerator = 1, .denominator = 15 }, + { .numerator = 1, .denominator = 30 }, +}; + +static int ioctl_enum_frameintervals(struct v4l2_int_device *s, + struct v4l2_frmivalenum *frmi) +{ + int ifmt; + + for (ifmt = 0; ifmt < OV3640_NUM_CAPTURE_FORMATS; ifmt++) { + if (frmi->pixel_format == ov3640_formats[ifmt].pixelformat) + break; + } + /* Is requested pixelformat not found on sensor? */ + if (ifmt == OV3640_NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Do we already reached all discrete framesizes? */ + + if ((frmi->width == ov3640_sizes[1].width) && + (frmi->height == ov3640_sizes[1].height)) { + /* FIXME: The only frameinterval supported by QXGA capture is + * 2/15 fps + */ + if (frmi->index != 0) + return -EINVAL; + } else { + if (frmi->index >= 3) + return -EINVAL; + } + + frmi->type = V4L2_FRMIVAL_TYPE_DISCRETE; + frmi->discrete.numerator = + ov3640_frameintervals[frmi->index].numerator; + frmi->discrete.denominator = + ov3640_frameintervals[frmi->index].denominator; + + return 0; +} + +/* + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power new_power) +{ + struct ov3640_sensor *sensor = s->priv; + int rval; + + switch (new_power) { + case V4L2_POWER_ON: + rval = sensor->pdata->set_xclk(s, OV3640_XCLK); + if (rval == -EINVAL) + break; + rval = sensor->pdata->power_set(s, V4L2_POWER_ON); + if (rval) + break; + + if (sensor->detected) + ov3640_configure(s); + else { + rval = ioctl_dev_init(s); + if (rval) + goto err_on; + } + break; + case V4L2_POWER_OFF: +err_on: + rval = sensor->pdata->power_set(s, V4L2_POWER_OFF); + sensor->pdata->set_xclk(s, 0); + break; + case V4L2_POWER_STANDBY: + rval = sensor->pdata->power_set(s, V4L2_POWER_STANDBY); + sensor->pdata->set_xclk(s, 0); + break; + default: + return -EINVAL; + } + + return rval; +} + +static struct v4l2_int_ioctl_desc ov3640_ioctl_desc[] = { + {vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *)ioctl_enum_framesizes}, + {vidioc_int_enum_frameintervals_num, + (v4l2_int_ioctl_func *)ioctl_enum_frameintervals}, + {vidioc_int_dev_init_num, + (v4l2_int_ioctl_func *)ioctl_dev_init}, + {vidioc_int_dev_exit_num, + (v4l2_int_ioctl_func *)ioctl_dev_exit}, + {vidioc_int_s_power_num, + (v4l2_int_ioctl_func *)ioctl_s_power}, + {vidioc_int_g_priv_num, + (v4l2_int_ioctl_func *)ioctl_g_priv}, + {vidioc_int_init_num, + (v4l2_int_ioctl_func *)ioctl_init}, + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, + {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, + {vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_g_fmt_cap}, + {vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, + {vidioc_int_g_parm_num, + (v4l2_int_ioctl_func *)ioctl_g_parm}, + {vidioc_int_s_parm_num, + (v4l2_int_ioctl_func *)ioctl_s_parm}, + {vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *)ioctl_queryctrl}, + {vidioc_int_querymenu_num, + (v4l2_int_ioctl_func *)ioctl_querymenu}, + {vidioc_int_g_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_s_ctrl}, + {vidioc_int_g_crop_num, + (v4l2_int_ioctl_func *)ioctl_g_crop}, + {vidioc_int_s_crop_num, + (v4l2_int_ioctl_func *)ioctl_s_crop}, + {vidioc_int_cropcap_num, + (v4l2_int_ioctl_func *)ioctl_cropcap}, +}; + +static struct v4l2_int_slave ov3640_slave = { + .ioctls = ov3640_ioctl_desc, + .num_ioctls = ARRAY_SIZE(ov3640_ioctl_desc), +}; + +static struct v4l2_int_device ov3640_int_device = { + .module = THIS_MODULE, + .name = OV3640_DRIVER_NAME, + .type = v4l2_int_type_slave, + .u = { + .slave = &ov3640_slave, + }, +}; + +/* + * ov3640_probe - sensor driver i2c probe handler + * @client: i2c driver client device structure + * + * Register sensor as an i2c client device and V4L2 + * device. + */ +static int ov3640_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ov3640_sensor *sensor; + struct ov3640_platform_data *pdata; + int err; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + /* Don't keep pointer to platform data, copy elements instead */ + sensor->pdata = kzalloc(sizeof(*sensor->pdata), GFP_KERNEL); + if (!sensor->pdata) { + err = -ENOMEM; + goto on_err1; + } + + sensor->pdata->power_set = pdata->power_set; + sensor->pdata->set_xclk = pdata->set_xclk; + sensor->pdata->priv_data_set = pdata->priv_data_set; + + /* Set sensor default values */ + sensor->timeperframe.numerator = 1; + sensor->timeperframe.denominator = 15; + sensor->pix.width = ov3640_sizes[XGA].width; + sensor->pix.height = ov3640_sizes[XGA].height; + sensor->pix.pixelformat = V4L2_PIX_FMT_SGRBG10; + + sensor->v4l2_int_device = &ov3640_int_device; + sensor->v4l2_int_device->priv = sensor; + sensor->dev = &client->dev; + + i2c_set_clientdata(client, sensor); + + err = v4l2_int_device_register(sensor->v4l2_int_device); + if (err) + goto on_err2; + + return 0; +on_err2: + i2c_set_clientdata(client, NULL); + kfree(sensor->pdata); +on_err1: + kfree(sensor); + return err; +} + +/* + * ov3640_remove - sensor driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister sensor as an i2c client device and V4L2 + * device. Complement of ov3640_probe(). + */ +static int ov3640_remove(struct i2c_client *client) +{ + struct ov3640_sensor *sensor = i2c_get_clientdata(client); + + v4l2_int_device_unregister(sensor->v4l2_int_device); + i2c_set_clientdata(client, NULL); + kfree(sensor->pdata); + kfree(sensor); + + return 0; +} + +static const struct i2c_device_id ov3640_id[] = { + { OV3640_DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ov3640_id); + +static struct i2c_driver ov3640sensor_i2c_driver = { + .driver = { + .name = OV3640_DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = ov3640_probe, + .remove = ov3640_remove, + .id_table = ov3640_id, +}; + +/* + * ov3640sensor_init - sensor driver module_init handler + * + * Registers driver as an i2c client driver. Returns 0 on success, + * error code otherwise. + */ +static int __init ov3640sensor_init(void) +{ + int err; + + err = i2c_add_driver(&ov3640sensor_i2c_driver); + if (err) { + printk(KERN_ERR "Failed to register " OV3640_DRIVER_NAME + " sensor\n"); + return err; + } + return 0; +} +module_init(ov3640sensor_init); + +/* + * ov3640sensor_cleanup - sensor driver module_exit handler + * + * Unregisters/deletes driver as an i2c client driver. + * Complement of ov3640sensor_init. + */ +static void __exit ov3640sensor_cleanup(void) +{ + i2c_del_driver(&ov3640sensor_i2c_driver); +} +module_exit(ov3640sensor_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("OV3640 camera sensor driver"); diff --git a/drivers/media/video/ov3640_regs.h b/drivers/media/video/ov3640_regs.h new file mode 100644 index 00000000000..9b35c890c86 --- /dev/null +++ b/drivers/media/video/ov3640_regs.h @@ -0,0 +1,582 @@ +#ifndef OV3640_REGS_H +#define OV3640_REGS_H + +/* + * System Control Registers + */ +#define OV3640_AGC_H 0x3000 +#define OV3640_AGC_L 0x3001 +#define OV3640_AEC_H 0x3002 +#define OV3640_AEC_L 0x3003 +#define OV3640_AECL 0x3004 +#define OV3640_RED 0x3005 +#define OV3640_GREEN 0x3006 +#define OV3640_BLUE 0x3007 +#define OV3640_PIDH 0x300A +#define OV3640_PIDL 0x300B +#define OV3640_SCCB_ID 0x300C +#define OV3640_PCLK 0x300D +#define OV3640_PCLK_HREFQUAL_OUT (1 << 5) +#define OV3640_PCLK_REVERSE (1 << 4) +#define OV3640_PCLK_DIVBY4 (1 << 1) +#define OV3640_PCLK_DIVBY2 1 + +#define OV3640_PLL_1 0x300E +#define OV3640_PLL_1_RXPLL_MASK 0x3F + +#define OV3640_PLL_2 0x300F +#define OV3640_PLL_2_FREQDIV_MASK (0x3 << 6) +#define OV3640_PLL_2_FREQDIV_1 (0x0 << 6) +#define OV3640_PLL_2_FREQDIV_1_5 (0x1 << 6) +#define OV3640_PLL_2_FREQDIV_2 (0x2 << 6) +#define OV3640_PLL_2_FREQDIV_3 (0x3 << 6) + +#define OV3640_PLL_2_BIT8DIV_MASK (0x3 << 4) +#define OV3640_PLL_2_BIT8DIV_1 (0x0 << 4) +#define OV3640_PLL_2_BIT8DIV_1_2 (0x1 << 4) +#define OV3640_PLL_2_BIT8DIV_4 (0x2 << 4) +#define OV3640_PLL_2_BIT8DIV_5 (0x3 << 4) +#define OV3640_PLL_2_BYPASS (1 << 3) + +#define OV3640_PLL_2_INDIV_MASK 0x3 +#define OV3640_PLL_2_INDIV_1 0x0 +#define OV3640_PLL_2_INDIV_1_5 0x1 +#define OV3640_PLL_2_INDIV_2 0x2 +#define OV3640_PLL_2_INDIV_3 0x3 + +#define OV3640_PLL_3 0x3010 +#define OV3640_PLL_3_DVPDIV_MASK (0x3 << 6) +#define OV3640_PLL_3_DVPDIV_1 (0x0 << 6) +#define OV3640_PLL_3_DVPDIV_2 (0x1 << 6) +#define OV3640_PLL_3_DVPDIV_8 (0x2 << 6) +#define OV3640_PLL_3_DVPDIV_16 (0x3 << 6) + +#define OV3640_PLL_3_LANEDIV2LANES (1 << 5) +#define OV3640_PLL_3_SENSORDIV2 (1 << 4) + +#define OV3640_PLL_3_SCALEDIV_MASK 0xF + +#define OV3640_CLK 0x3011 +#define OV3640_CLK_DFREQDBL (1 << 7) +#define OV3640_CLK_SLAVEMODE (1 << 6) +#define OV3640_CLK_DIV_MASK 0x3F + +#define OV3640_SYS 0x3012 +#define OV3640_SYS_SRST (1 << 7) +#define OV3640_SYS_BASERES_MASK (0x7 << 4) +#define OV3640_SYS_BASERES_QXGA (0x0 << 4) +#define OV3640_SYS_BASERES_XGA (0x1 << 4) +#define OV3640_SYS_BASERES_SXGA (0x7 << 4) + +#define OV3640_AUTO_1 0x3013 +#define OV3640_AUTO_1_FASTAEC (1 << 7) +#define OV3640_AUTO_1_AECBIGSTEPS (1 << 6) +#define OV3640_AUTO_1_BANDINGFILTEREN (1 << 5) +#define OV3640_AUTO_1_AUTOBANDINGFILTER (1 << 4) +#define OV3640_AUTO_1_EXTRBRIGHTEXPEN (1 << 3) +#define OV3640_AUTO_1_AGCEN (1 << 2) +#define OV3640_AUTO_1_AECEN 1 + +#define OV3640_AUTO_2 0x3014 +#define OV3640_AUTO_2_MANBANDING50 (1 << 7) +#define OV3640_AUTO_2_AUTOBANDINGDETEN (1 << 6) +#define OV3640_AUTO_2_AGCADDLT1F (1 << 5) +#define OV3640_AUTO_2_FREEZEAECAGC (1 << 4) +#define OV3640_AUTO_2_NIGHTMODEEN (1 << 3) +#define OV3640_AUTO_2_BANDINGSMOOTHSW (1 << 2) +#define OV3640_AUTO_2_MANEXTRBRIGHTEXPEN (1 << 1) +#define OV3640_AUTO_2_BANDINGFILTEREN 1 + +#define OV3640_AUTO_3 0x3015 +#define OV3640_AUTO_3_DUMMYFC_MASK (0x7 << 4) +#define OV3640_AUTO_3_DUMMYFC_NONE (0x0 << 4) +#define OV3640_AUTO_3_DUMMYFC_1FRAME (0x1 << 4) +#define OV3640_AUTO_3_DUMMYFC_2FRAME (0x2 << 4) +#define OV3640_AUTO_3_DUMMYFC_3FRAME (0x3 << 4) +#define OV3640_AUTO_3_DUMMYFC_7FRAME (0x7 << 4) + +#define OV3640_AUTO_3_AGCGAINCEIL_MASK 0x7 +#define OV3640_AUTO_3_AGCGAINCEIL_2X 0x0 +#define OV3640_AUTO_3_AGCGAINCEIL_4X 0x1 +#define OV3640_AUTO_3_AGCGAINCEIL_8X 0x2 +#define OV3640_AUTO_3_AGCGAINCEIL_16X 0x3 +#define OV3640_AUTO_3_AGCGAINCEIL_32X 0x4 +#define OV3640_AUTO_3_AGCGAINCEIL_64X 0x5 +#define OV3640_AUTO_3_AGCGAINCEIL_128X 0x6 +#define OV3640_AUTO_3_AGCGAINCEIL_128X_2 0x7 + +#define OV3640_AUTO_5 0x3017 +#define OV3640_AUTO_5_MANBANDINGCNT_MASK 0x3F + +#define OV3640_WPT_HISH 0x3018 +#define OV3640_BPT_HISL 0x3019 +#define OV3640_VPT 0x301A +#define OV3640_YAVG 0x301B +#define OV3640_AECG_MAX50 0x301C +#define OV3640_AECG_MAX60 0x301D +#define OV3640_RZM_H 0x301E +#define OV3640_RZM_L 0x301F + +#define OV3640_HS_H 0x3020 +#define OV3640_HS_L 0x3021 +#define OV3640_VS_H 0x3022 +#define OV3640_VS_L 0x3023 +#define OV3640_HW_H 0x3024 +#define OV3640_HW_L 0x3025 +#define OV3640_VH_H 0x3026 +#define OV3640_VH_L 0x3027 +#define OV3640_HTS_H 0x3028 +#define OV3640_HTS_L 0x3029 +#define OV3640_VTS_H 0x302A +#define OV3640_VTS_L 0x302B +#define OV3640_EXHTS 0x302C +#define OV3640_EXVTS_H 0x302D +#define OV3640_EXVTS_L 0x302E + +#define OV3640_WEIGHT0 0x3030 +#define OV3640_WEIGHT1 0x3031 +#define OV3640_WEIGHT2 0x3032 +#define OV3640_WEIGHT3 0x3033 +#define OV3640_WEIGHT4 0x3034 +#define OV3640_WEIGHT5 0x3035 +#define OV3640_WEIGHT6 0x3036 +#define OV3640_WEIGHT7 0x3037 +#define OV3640_AHS_H 0x3038 +#define OV3640_AHS_L 0x3039 +#define OV3640_AVS_H 0x303A +#define OV3640_AVS_L 0x303B +#define OV3640_AHW_H 0x303C +#define OV3640_AHW_L 0x303D +#define OV3640_AVH_H 0x303E +#define OV3640_AVH_L 0x303F + +#define OV3640_HISTO0 0x3040 +#define OV3640_HISTO1 0x3041 +#define OV3640_HISTO2 0x3042 +#define OV3640_HISTO3 0x3043 +#define OV3640_HISTO4 0x3044 +#define OV3640_HISTO5 0x3045 +#define OV3640_HISTO6 0x3046 +#define OV3640_HISTO7 0x3047 +#define OV3640_D56C1 0x3048 + +#define OV3640_BLC9 0x3069 + +#define OV3640_BD50_H 0x3070 +#define OV3640_BD50_L 0x3071 +#define OV3640_BD60_H 0x3072 +#define OV3640_BD60_L 0x3073 +#define OV3640_VSYNCOPT 0x3075 +#define OV3640_TMC1 0x3077 +#define OV3640_TMC1_CHSYNCSWAP (1 << 7) +#define OV3640_TMC1_HREFSWAP (1 << 6) +#define OV3640_TMC1_HREFPOL_NEG (1 << 3) +#define OV3640_TMC1_VSYNCPOL_NEG (1 << 1) +#define OV3640_TMC1_HSYNCPOL_NEG 1 + +#define OV3640_TMC2 0x3078 +#define OV3640_TMC2_VSYNCDROP (1 << 1) +#define OV3640_TMC2_FRAMEDATADROP 1 + +#define OV3640_TMC3 0x3079 +#define OV3640_TMC3_VSLATCH (1 << 7) + +#define OV3640_TMC4 0x307A +#define OV3640_TMC5 0x307B +#define OV3640_TMC5_AWB_GAINWRITE_DIS (1 << 7) +#define OV3640_TMC5_DCOLORBAREN (1 << 3) +#define OV3640_TMC5_DCOLORBARPAT_MASK 0x7 + +#define OV3640_TMC6 0x307C +#define OV3640_TMC6_DGAINEN (1 << 5) +#define OV3640_TMC6_HMIRROR (1 << 1) +#define OV3640_TMC6_VFLIP (1 << 0) + +#define OV3640_TMC7 0x307D +#define OV3640_TMC7_COLORBARTESTPATEN (1 << 7) +#define OV3640_TMC7_AVGHIST_SENSOR (1 << 5) + +#define OV3640_TMC8 0x307E + +#define OV3640_TMCA 0x3080 +#define OV3640_TMCB 0x3081 +#define OV3640_TMCB_MIRROROPTEN (1 << 7) +#define OV3640_TMCB_OTPFASTMEMCLK (1 << 6) +#define OV3640_TMCB_SWAPBYTESOUT 1 + +#define OV3640_TMCF 0x3085 +#define OV3640_TMC10 0x3086 +#define OV3640_TMC10_SYSRST (1 << 3) +#define OV3640_TMC10_REGSLEEPOPT (1 << 2) +#define OV3640_TMC10_SLEEPOPT (1 << 1) +#define OV3640_TMC10_SLEEPEN 1 + +#define OV3640_TMC11 0x3087 +#define OV3640_ISP_XOUT_H 0x3088 +#define OV3640_ISP_XOUT_L 0x3089 +#define OV3640_ISP_YOUT_H 0x308A +#define OV3640_ISP_YOUT_L 0x308B +#define OV3640_TMC13 0x308D +#define OV3640_5060 0x308E +#define OV3640_OTP 0x308F + +#define OV3640_IO_CTRL0 0x30B0 +#define OV3640_IO_CTRL1 0x30B1 +#define OV3640_IO_CTRL2 0x30B2 +#define OV3640_DVP0 0x30B4 +#define OV3640_DVP1 0x30B5 +#define OV3640_DVP2 0x30B6 +#define OV3640_DVP3 0x30B7 +#define OV3640_DSPC0 0x30B8 +#define OV3640_DSPC1 0x30B9 +#define OV3640_DSPC2 0x30BA +#define OV3640_DSPC3 0x30BB +#define OV3640_DSPC7 0x30BF +/* + * END - System Control Registers + */ + +/* + * SC Registers + */ +#define OV3640_SC_CTRL0 0x3100 +#define OV3640_SC_CTRL2 0x3102 +#define OV3640_SC_SYN_CTRL0 0x3104 +#define OV3640_SC_SYN_CTRL1 0x3105 +#define OV3640_SC_SYN_CTRL2 0x3106 +#define OV3640_SC_SYN_CTRL3 0x3107 +/* + * END - SC Registers + */ + +/* + * CIF Registers + */ +#define OV3640_CIF_CTRL0 0x3200 +#define OV3640_CIF_CTRL4 0x3204 +/* + * END - CIF Registers + */ + +/* + * DSP Registers + */ +#define OV3640_DSP_CTRL_0 0x3300 +#define OV3640_DSP_CTRL_1 0x3301 +#define OV3640_DSP_CTRL_2 0x3302 +#define OV3640_DSP_CTRL_4 0x3304 +#define OV3640_AWB_CTRL_3 0x3308 + +#define OV3640_YST1 0x331B +#define OV3640_YST2 0x331C +#define OV3640_YST3 0x331D +#define OV3640_YST4 0x331E +#define OV3640_YST5 0x331F + +#define OV3640_YST6 0x3320 +#define OV3640_YST7 0x3321 +#define OV3640_YST8 0x3322 +#define OV3640_YST9 0x3323 +#define OV3640_YST10 0x3324 +#define OV3640_YST11 0x3325 +#define OV3640_YST12 0x3326 +#define OV3640_YST13 0x3327 +#define OV3640_YST14 0x3328 +#define OV3640_YST15 0x3329 +#define OV3640_YSLP15 0x332A +#define OV3640_MISC_CTRL 0x332B +#define OV3640_DNS_TH 0x332C +#define OV3640_Y_EDGE_MT 0x332D +#define OV3640_Y_EDGE_TH_TM 0x332E +#define OV3640_BASE1 0x332F + +#define OV3640_BASE2 0x3330 +#define OV3640_OFFSET 0x3331 +#define OV3640_CMXSIGN_MISC 0x333F + +#define OV3640_CMX_1 0x3340 +#define OV3640_CMX_2 0x3341 +#define OV3640_CMX_3 0x3342 +#define OV3640_CMX_4 0x3343 +#define OV3640_CMX_5 0x3344 +#define OV3640_CMX_6 0x3345 +#define OV3640_CMX_7 0x3346 +#define OV3640_CMX_8 0x3347 +#define OV3640_CMX_9 0x3348 +#define OV3640_CMXSIGN 0x3349 + +#define OV3640_SGNSET 0x3354 +#define OV3640_SDE_CTRL 0x3355 +#define OV3640_HUE_COS 0x3356 +#define OV3640_HUE_SIN 0x3357 +#define OV3640_SAT_U 0x3358 +#define OV3640_SAT_V 0x3359 +#define OV3640_UREG 0x335A +#define OV3640_VREG 0x335B +#define OV3640_YOFFSET 0x335C +#define OV3640_YGAIN 0x335D +#define OV3640_YBRIGHT 0x335E +#define OV3640_SIZE_IN_MISC 0x335F + +#define OV3640_HSIZE_IN_L 0x3360 +#define OV3640_VSIZE_IN_L 0x3361 +#define OV3640_SIZE_OUT_MISC 0x3362 +#define OV3640_HSIZE_OUT_L 0x3363 +#define OV3640_VSIZE_OUT_L 0x3364 + +#define OV3640_R_XY0 0x3367 +#define OV3640_R_X0 0x3368 +#define OV3640_R_Y0 0x3369 +#define OV3640_R_A1 0x336A +#define OV3640_R_A2_B2 0x336B +#define OV3640_R_B1 0x336C +#define OV3640_G_XY0 0x336D +#define OV3640_G_X0 0x336E +#define OV3640_G_Y0 0x336F + +#define OV3640_G_A1 0x3370 +#define OV3640_G_A2_B2 0x3371 +#define OV3640_G_B1 0x3372 +#define OV3640_B_XY0 0x3373 +#define OV3640_B_X0 0x3374 +#define OV3640_B_Y0 0x3375 +#define OV3640_B_A1 0x3376 +#define OV3640_B_A2_B2 0x3377 +#define OV3640_B_B1 0x3378 + +#define OV3640_MISC_DCW_SIZE 0x33A4 +#define OV3640_DCW_OH 0x33A5 +#define OV3640_DCW_OV 0x33A6 +#define OV3640_R_GAIN_M 0x33A7 +#define OV3640_G_GAIN_M 0x33A8 +#define OV3640_B_GAIN_M 0x33A9 +#define OV3640_OVLY_MISC1 0x33AA +#define OV3640_OVLY_LEFT 0x33AB +#define OV3640_OVLY_TOP 0x33AC +#define OV3640_OVLY_MISC2 0x33AD +#define OV3640_OVLY_RIGHT 0x33AE +#define OV3640_OVLY_BOTTEM 0x33AF + +#define OV3640_OVLY_MISC3 0x33B0 +#define OV3640_OVLY_EXT_WIDTH_H 0x33B1 +#define OV3640_OVLY_EXT_WIDTH_L 0x33B2 +#define OV3640_OVLY_Y 0x33B3 +#define OV3640_OVLY_U 0x33B4 +#define OV3640_OVLY_V 0x33B5 +/* + * END - DSP Registers + */ + +/* + * FMT MUX Registers + */ +#define OV3640_FMT_MUX_CTRL0 0x3400 +#define OV3640_ISP_PAD_CTR2 0x3403 +#define OV3640_FMT_CTRL00 0x3404 +#define OV3640_FMT_CTRL00_UV_sel_SHIFT 7 +#define OV3640_FMT_CTRL00_UV_sel_MASK (0x1 << \ + OV3640_FMT_CTRL00_UV_sel_SHIFT) +#define OV3640_FMT_CTRL00_UV_sel_USE_UV_avg_Y (0x0 << \ + OV3640_FMT_CTRL00_UV_sel_SHIFT) +#define OV3640_FMT_CTRL00_UV_sel_USE_U0Y0_V0Y1 (0x1 << \ + OV3640_FMT_CTRL00_UV_sel_SHIFT) + +#define OV3640_FMT_CTRL00_YUV422_in_SHIFT 6 +#define OV3640_FMT_CTRL00_YUV422_in_MASK (0x1 << \ + OV3640_FMT_CTRL00_YUV422_in_SHIFT) +#define OV3640_FMT_CTRL00_YUV422_in_DISABLE (0x0 << \ + OV3640_FMT_CTRL00_YUV422_in_SHIFT) +#define OV3640_FMT_CTRL00_YUV422_in_ENABLE (0x1 << \ + OV3640_FMT_CTRL00_YUV422_in_SHIFT) + +#define OV3640_FMT_CTRL00_FMT_SHIFT 0 +#define OV3640_FMT_CTRL00_FMT_MASK (0x3F << \ + OV3640_FMT_CTRL00_FMT_SHIFT) +#define OV3640_DITHER_CTRL0 0x3405 +/* + * END - FMT MUX Registers + */ + +/* + * OUT_TOP Registers + */ +#define OV3640_OUT_CTRL00 0x3600 +#define OV3640_OUT_CTRL00_VSYNCSEL2 (1 << 7) +#define OV3640_OUT_CTRL00_VSYNCGATE (1 << 6) +#define OV3640_OUT_CTRL00_PCLKPOL_NEG (1 << 4) +#define OV3640_OUT_CTRL00_HREFPOL_NEG (1 << 3) +#define OV3640_OUT_CTRL00_VSYNCPOL_NEG (1 << 2) +#define OV3640_OUT_CTRL00_VSYNCSEL (1 << 1) +#define OV3640_OUT_CTRL00_DVPOUTDATAORDERINV 1 + +#define OV3640_OUT_CTRL01 0x3601 +#define OV3640_OUT_CTRL01_PCLKGATEEN (1 << 7) +#define OV3640_OUT_CTRL01_CCIR656EN (1 << 4) +#define OV3640_OUT_CTRL01_MIPIBIT8 1 + +#define OV3640_MIPI_CTRL02 0x3602 +#define OV3640_MIPI_CTRL02_DVPDISABLE (1 << 4) +#define OV3640_MIPI_CTRL02_MIPILINESYNCEN (1 << 2) +#define OV3640_MIPI_CTRL02_MIPIGATESCEN (1 << 1) + +#define OV3640_MIPI_CTRL03 0x3603 +#define OV3640_MIPI_CTRL03_ECC_PHBYTEORDER (1 << 2) +#define OV3640_MIPI_CTRL03_ECC_PHBITORDER (1 << 1) + +#define OV3640_OUT_CTRL08 0x3608 +#define OV3640_OUT_CTRL08_HREF_DLY_SHIFT 4 +#define OV3640_OUT_CTRL08_HREF_DLY_MASK (0xF << \ + OV3640_OUT_CTRL08_HREF_DLY_SHIFT) + + +#define OV3640_OUT_CTRL09 0x3609 +#define OV3640_OUT_CTRL0A 0x360A +#define OV3640_OUT_CTRL0B 0x360B +#define OV3640_MIPI_CTRL0C 0x360C +#define OV3640_MIPI_CTRL0C_VIRTUALCH_ID_MASK (0x3 << 6) + +#define OV3640_OUT_CTRL0D 0x360D +#define OV3640_MIPI_CTRL0E 0x360E +#define OV3640_MIPI_CTRL0E_WKUPDELAY_MASK 0x3F + + +#define OV3640_MIPI_CTRL10 0x3610 +#define OV3640_MIPI_CTRL10_WIDTH_MAN_L_MASK 0xFF + +#define OV3640_MIPI_CTRL11 0x3611 +#define OV3640_MIPI_CTRL11_WIDTH_MAN_H_MASK (0x7 << 5) + +#define OV3640_CLIP_MIN 0x3614 +#define OV3640_CLIP_MAX 0x3615 +#define OV3640_OUT_CTRL16 0x3616 +#define OV3640_OUT_CTRL1D 0x361D +#define OV3640_OUT_CTRL1E 0x361E +#define OV3640_MIPI_CTRL1F 0x361F +#define OV3640_MIPI_CTRL1F_PCLK_PERIOD_MASK 0xFF + +#define OV3640_MIPI_CTRL22 0x3622 +#define OV3640_MIPI_CTRL22_MIN_HS_ZERO_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL22_MIN_HS_ZERO_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL22_MIN_HS_ZERO_NUI_SHIFT) +#define OV3640_MIPI_CTRL22_MIN_HS_ZERO_H_MASK 0x3 + +#define OV3640_MIPI_CTRL23 0x3623 +#define OV3640_MIPI_CTRL23_MIN_HS_ZERO_L_MASK 0xFF + +#define OV3640_MIPI_CTRL24 0x3624 +#define OV3640_MIPI_CTRL24_MIN_HS_TRAIL_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL24_MIN_HS_TRAIL_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL24_MIN_HS_TRAIL_NUI_SHIFT) +#define OV3640_MIPI_CTRL24_MIN_HS_TRAIL_H_MASK 0x3 + +#define OV3640_MIPI_CTRL25 0x3625 +#define OV3640_MIPI_CTRL25_MIN_HS_TRAIL_L_MASK 0xFF + +#define OV3640_MIPI_CTRL26 0x3626 +#define OV3640_MIPI_CTRL26_MIN_CLK_ZERO_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL26_MIN_CLK_ZERO_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL26_MIN_CLK_ZERO_NUI_SHIFT) +#define OV3640_MIPI_CTRL26_MIN_CLK_ZERO_H_MASK 0x3 + +#define OV3640_MIPI_CTRL27 0x3627 +#define OV3640_MIPI_CTRL27_MIN_CLK_ZERO_L_MASK 0xFF + +#define OV3640_MIPI_CTRL28 0x3628 +#define OV3640_MIPI_CTRL28_MIN_CLK_PREPARE_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL28_MIN_CLK_PREPARE_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL28_MIN_CLK_PREPARE_NUI_SHIFT) +#define OV3640_MIPI_CTRL28_MIN_CLK_PREPARE_H_MASK 0x3 + +#define OV3640_MIPI_CTRL29 0x3629 +#define OV3640_MIPI_CTRL29_MIN_CLK_PREPARE_L_MASK 0xFF + +#define OV3640_MIPI_CTRL2A 0x362A +#define OV3640_MIPI_CTRL2A_MAX_CLK_PREPARE_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL2A_MAX_CLK_PREPARE_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL2A_MAX_CLK_PREPARE_NUI_SHIFT) +#define OV3640_MIPI_CTRL2A_MAX_CLK_PREPARE_H_MASK 0x3 + +#define OV3640_MIPI_CTRL2B 0x362B +#define OV3640_MIPI_CTRL2B_MAX_CLK_PREPARE_L_MASK 0xFF + +#define OV3640_MIPI_CTRL2C 0x362C +#define OV3640_MIPI_CTRL2C_MIN_CLK_POST_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL2C_MIN_CLK_POST_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL2C_MIN_CLK_POST_NUI_SHIFT) +#define OV3640_MIPI_CTRL2C_MIN_CLK_POST_H_MASK 0x3 + +#define OV3640_MIPI_CTRL2D 0x362D +#define OV3640_MIPI_CTRL2D_MIN_CLK_POST_L_MASK 0xFF + +#define OV3640_MIPI_CTRL2E 0x362E +#define OV3640_MIPI_CTRL2E_MIN_CLK_TRAIL_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL2E_MIN_CLK_TRAIL_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL2E_MIN_CLK_TRAIL_NUI_SHIFT) +#define OV3640_MIPI_CTRL2E_MIN_CLK_TRAIL_H_MASK 0x3 + +#define OV3640_MIPI_CTRL2F 0x362F +#define OV3640_MIPI_CTRL2F_MIN_CLK_TRAIL_L_MASK 0xFF + +#define OV3640_MIPI_CTRL30 0x3630 +#define OV3640_MIPI_CTRL30_MIN_LPX_P_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL30_MIN_LPX_P_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL30_MIN_LPX_P_NUI_SHIFT) +#define OV3640_MIPI_CTRL30_MIN_LPX_P_H_MASK 0x3 + +#define OV3640_MIPI_CTRL31 0x3631 +#define OV3640_MIPI_CTRL31_MIN_LPX_P_L_MASK 0xFF + +#define OV3640_MIPI_CTRL32 0x3632 +#define OV3640_MIPI_CTRL32_MIN_HS_PREPARE_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL32_MIN_HS_PREPARE_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL32_MIN_HS_PREPARE_NUI_SHIFT) +#define OV3640_MIPI_CTRL32_MIN_HS_PREPARE_H_MASK 0x3 + +#define OV3640_MIPI_CTRL33 0x3633 +#define OV3640_MIPI_CTRL33_MIN_HS_PREPARE_L_MASK 0xFF + +#define OV3640_MIPI_CTRL34 0x3634 +#define OV3640_MIPI_CTRL34_MAX_HS_PREPARE_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL34_MAX_HS_PREPARE_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL34_MAX_HS_PREPARE_NUI_SHIFT) +#define OV3640_MIPI_CTRL34_MAX_HS_PREPARE_H_MASK 0x3 + +#define OV3640_MIPI_CTRL35 0x3635 +#define OV3640_MIPI_CTRL35_MAX_HS_PREPARE_L_MASK 0xFF + +#define OV3640_MIPI_CTRL36 0x3636 +#define OV3640_MIPI_CTRL36_MIN_HS_EXIT_NUI_SHIFT 2 +#define OV3640_MIPI_CTRL36_MIN_HS_EXIT_NUI_MASK (0x3F << \ + OV3640_MIPI_CTRL36_MIN_HS_EXIT_NUI_SHIFT) +#define OV3640_MIPI_CTRL36_MIN_HS_EXIT_H_MASK 0x3 + +#define OV3640_MIPI_CTRL37 0x3637 +#define OV3640_MIPI_CTRL37_MIN_HS_EXIT_L_MASK 0xFF + +#define OV3640_OUT_CTRL3C 0x363C +#define OV3640_MIPI_CTRL3D 0x363D +#define OV3640_MIPI_CTRL3D_JPGPADEN (1 << 6) +#define OV3640_OUT_CTRL3E 0x363E +#define OV3640_OUT_CTRL3F 0x363F + +#define OV3640_OUT_CTRL40 0x3640 +#define OV3640_OUT_CTRL43 0x3643 +#define OV3640_OUT_CTRL44 0x3644 +#define OV3640_OUT_CTRL46 0x3646 +#define OV3640_MIPI_CTRL4C 0x364C +#define OV3640_MIPI_CTRL4C_ECC_PHBYTEORDER2 (1 << 2) +/* + * END - OUT_TOP Registers + */ +/* + * MC Registers + */ +#define OV3640_INTR_MASK0 0x3700 +#define OV3640_INTR_MASK1 0x3701 +#define OV3640_INTR0 0x3708 +#define OV3640_INTR1 0x3709 +/* + * END - MC Registers + */ + +#endif /* ifndef OV3640_REGS_H */ diff --git a/drivers/media/video/ti-media/Kconfig b/drivers/media/video/ti-media/Kconfig new file mode 100644 index 00000000000..bc5237a863b --- /dev/null +++ b/drivers/media/video/ti-media/Kconfig @@ -0,0 +1,89 @@ +config VIDEO_VPSS_SYSTEM + tristate "VPSS System module driver" + depends on TI_MEDIA + help + Support for vpss system module for video driver + default n + +config VIDEO_VPFE_CAPTURE + tristate "VPFE Video Capture Driver" + depends on TI_MEDIA && (ARCH_DAVINCI || MACH_OMAP3517EVM) + select VIDEOBUF_DMA_CONTIG + help + Support for DMXXXX VPFE based frame grabber. This is the + common V4L2 module for following SoCs from Texas + Instruments:- DM6446, DM355 & AM3517. + + To compile this driver as a module, choose M here: the + module will be called vpfe-capture. + +config VIDEO_DM6446_CCDC + tristate "DM6446 CCDC HW module" + depends on VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_DM355_CCDC + tristate "DM355 CCDC HW module" + depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config DISPLAY_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Display" + depends on TI_MEDIA && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + select VIDEO_ADV7343 + select VIDEO_THS7303 + help + Support for DM6467 based display device. + + To compile this driver as a module, choose M here: the + module will be called vpif_display. + +config CAPTURE_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Capture" + depends on TI_MEDIA && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + help + Support for DM6467 based capture device. + + To compile this driver as a module, choose M here: the + module will be called vpif_capture. + +config VIDEO_DAVINCI_VPIF + tristate "DaVinci VPIF Driver" + depends on DISPLAY_DAVINCI_DM646X_EVM + help + Support for DaVinci VPIF Driver. + + To compile this driver as a module, choose M here: the + module will be called vpif. + +config VIDEO_OMAP3_OUT + tristate "OMAP2/OMAP3 V4L2-DSS driver" + select VIDEOBUF_GEN + select VIDEOBUF_DMA_SG + select OMAP2_DSS + depends on TI_MEDIA && (ARCH_OMAP24XX || ARCH_OMAP34XX) + default y + ---help--- + V4L2 DSS support for OMAP2/3 based boards. diff --git a/drivers/media/video/ti-media/Makefile b/drivers/media/video/ti-media/Makefile new file mode 100644 index 00000000000..4a5060a483a --- /dev/null +++ b/drivers/media/video/ti-media/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for the TI video device drivers. +# + +# VPIF +obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o + +#DM646x EVM Display driver +obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o +#DM646x EVM Capture driver +obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o + +# Capture: DM6446 and DM355 +obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o +obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o +obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o + +omap-vout-mod-objs := omap_vout.o omap_voutlib.o +obj-$(CONFIG_VIDEO_OMAP3_OUT) += omap-vout-mod.o diff --git a/drivers/media/video/ti-media/ccdc_hw_device.h b/drivers/media/video/ti-media/ccdc_hw_device.h new file mode 100644 index 00000000000..adf89b61438 --- /dev/null +++ b/drivers/media/video/ti-media/ccdc_hw_device.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + * + * ccdc device API + */ +#ifndef _CCDC_HW_DEVICE_H +#define _CCDC_HW_DEVICE_H + +#ifdef __KERNEL__ +#include +#include +#include +#include + +/* + * ccdc hw operations + */ +struct ccdc_hw_ops { + /* Pointer to initialize function to initialize ccdc device */ + int (*open) (struct device *dev); + /* Pointer to deinitialize function */ + int (*close) (struct device *dev); + /* set ccdc base address */ + void (*set_ccdc_base)(void *base, int size); + /* Pointer to function to enable or disable ccdc */ + void (*enable) (int en); + /* reset sbl. only for 6446 */ + void (*reset) (void); + /* enable output to sdram */ + void (*enable_out_to_sdram) (int en); + /* Pointer to function to set hw parameters */ + int (*set_hw_if_params) (struct vpfe_hw_if_param *param); + /* get interface parameters */ + int (*get_hw_if_params) (struct vpfe_hw_if_param *param); + /* + * Pointer to function to set parameters. Used + * for implementing VPFE_S_CCDC_PARAMS + */ + int (*set_params) (void *params); + /* + * Pointer to function to get parameter. Used + * for implementing VPFE_G_CCDC_PARAMS + */ + int (*get_params) (void *params); + /* Pointer to function to configure ccdc */ + int (*configure) (void); + + /* Pointer to function to set buffer type */ + int (*set_buftype) (enum ccdc_buftype buf_type); + /* Pointer to function to get buffer type */ + enum ccdc_buftype (*get_buftype) (void); + /* Pointer to function to set frame format */ + int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); + /* Pointer to function to get frame format */ + enum ccdc_frmfmt (*get_frame_format) (void); + /* enumerate hw pix formats */ + int (*enum_pix)(u32 *hw_pix, int i); + /* Pointer to function to set buffer type */ + u32 (*get_pixel_format) (void); + /* Pointer to function to get pixel format. */ + int (*set_pixel_format) (u32 pixfmt); + /* Pointer to function to set image window */ + int (*set_image_window) (struct v4l2_rect *win); + /* Pointer to function to set image window */ + void (*get_image_window) (struct v4l2_rect *win); + /* Pointer to function to get line length */ + unsigned int (*get_line_length) (void); + + /* Query CCDC control IDs */ + int (*queryctrl)(struct v4l2_queryctrl *qctrl); + /* Set CCDC control */ + int (*set_control)(struct v4l2_control *ctrl); + /* Get CCDC control */ + int (*get_control)(struct v4l2_control *ctrl); + + /* Pointer to function to set frame buffer address */ + void (*setfbaddr) (unsigned long addr); + /* Pointer to function to get field id */ + int (*getfid) (void); + + /* suspend/resume support */ + void (*save_context)(void); + void (*restore_context)(void); +}; + +struct ccdc_hw_device { + /* ccdc device name */ + char name[32]; + /* module owner */ + struct module *owner; + /* hw ops */ + struct ccdc_hw_ops hw_ops; +}; + +/* Used by CCDC module to register & unregister with vpfe capture driver */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev); +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev); + +#endif +#endif diff --git a/drivers/media/video/ti-media/dm355_ccdc.c b/drivers/media/video/ti-media/dm355_ccdc.c new file mode 100644 index 00000000000..a09f0a48819 --- /dev/null +++ b/drivers/media/video/ti-media/dm355_ccdc.c @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + * + * CCDC hardware module for DM355 + * ------------------------------ + * + * This module is for configuring DM355 CCD controller of VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application include dm355_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Split module parameter structure to module specific ioctl structs + * 3) add support for lense shading correction + * 4) investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include +#include +#include +#include +#include +#include "dm355_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM355"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = 256, + .gb_g = 256, + .gr_cy = 256, + .b_mg = 256 + }, + .config_params = { + .datasft = 2, + .data_sz = CCDC_DATA_10BITS, + .mfilt1 = CCDC_NO_MEDIAN_FILTER1, + .mfilt2 = CCDC_NO_MEDIAN_FILTER2, + .alaw = { + .gama_wd = 2, + }, + .blk_clamp = { + .sample_pixel = 1, + .dc_sub = 25 + }, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + }, +}; + + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .win = CCDC_WIN_PAL, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +static enum vpfe_hw_if_type ccdc_if_type; +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~CCDC_SYNCEN_VDHDEN_MASK); + temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~(CCDC_SYNCEN_WEN_MASK)); + temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_config_gain_offset(void) +{ + /* configure gain */ + regw(ccdc_hw_params_raw.gain.r_ye, RYEGAIN); + regw(ccdc_hw_params_raw.gain.gr_cy, GRCYGAIN); + regw(ccdc_hw_params_raw.gain.gb_g, GBGGAIN); + regw(ccdc_hw_params_raw.gain.b_mg, BMGGAIN); + /* configure offset */ + regw(ccdc_hw_params_raw.ccdc_offset, OFFSET); +} + +/* + * ccdc_restore_defaults() + * This function restore power on defaults in the ccdc registers + */ +static int ccdc_restore_defaults(void) +{ + int i; + + dev_dbg(dev, "\nstarting ccdc_restore_defaults..."); + /* set all registers to zero */ + for (i = 0; i <= CCDC_REG_LAST; i += 4) + regw(0, i); + + /* now override the values with power on defaults in registers */ + regw(MODESET_DEFAULT, MODESET); + /* no culling support */ + regw(CULH_DEFAULT, CULH); + regw(CULV_DEFAULT, CULV); + /* Set default Gain and Offset */ + ccdc_hw_params_raw.gain.r_ye = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gb_g = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gr_cy = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.b_mg = GAIN_DEFAULT; + ccdc_config_gain_offset(); + regw(OUTCLIP_DEFAULT, OUTCLIP); + regw(LSCCFG2_DEFAULT, LSCCFG2); + /* select ccdc input */ + if (vpss_select_ccdc_source(VPSS_CCDCIN)) { + dev_dbg(dev, "\ncouldn't select ccdc input source"); + return -EFAULT; + } + /* select ccdc clock */ + if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { + dev_dbg(dev, "\ncouldn't enable ccdc clock"); + return -EFAULT; + } + dev_dbg(dev, "\nEnd of ccdc_restore_defaults..."); + return 0; +} + +static int ccdc_open(struct device *device) +{ + dev = device; + return ccdc_restore_defaults(); +} + +static int ccdc_close(struct device *device) +{ + /* disable clock */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 0); + /* do nothing for now */ + return 0; +} +/* + * ccdc_setwin() + * This function will configure the window size to + * be capture in CCDC reg. + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start, SPH); + regw(horz_nr_pixels, NPH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 and VDINT1 */ + regw(vert_start, VDINT0); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(vert_start, VDINT0); + regw(mid_img, VDINT1); + } + regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); + regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || + ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { + dev_dbg(dev, "Invalid value of data shift\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || + ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { + dev_dbg(dev, "Invalid value of median filter1\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || + ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { + dev_dbg(dev, "Invalid value of median filter2\n"); + return -EINVAL; + } + + if ((ccdcparam->med_filt_thres < 0) || + (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { + dev_dbg(dev, "Invalid value of median filter thresold\n"); + return -EINVAL; + } + + if (ccdcparam->data_sz < CCDC_DATA_16BITS || + ccdcparam->data_sz > CCDC_DATA_8BITS) { + dev_dbg(dev, "Invalid value of data size\n"); + return -EINVAL; + } + + if (ccdcparam->alaw.enable) { + if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || + ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { + dev_dbg(dev, "Invalid value of ALAW\n"); + return -EINVAL; + } + } + + if (ccdcparam->blk_clamp.b_clamp_enable) { + if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || + ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { + dev_dbg(dev, "Invalid value of sample pixel\n"); + return -EINVAL; + } + if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || + ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { + dev_dbg(dev, "Invalid value of sample lines\n"); + return -EINVAL; + } + } + return 0; +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying ccdc" + "params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + memcpy(&ccdc_hw_params_raw.config_params, + &ccdc_raw_params, + sizeof(ccdc_raw_params)); + return 0; + } + return -EINVAL; +} + +/* This function will configure CCDC for YCbCr video capture */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 temp; + + /* first set the CCDC power on defaults values in all registers */ + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + ccdc_restore_defaults(); + + /* configure pixel format & video frame format */ + temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << + CCDC_INPUT_MODE_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT)); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, REC656IF); + /* + * configure the FID, VD, HD pin polarity fld,hd pol positive, + * vd negative, 8-bit pack mode + */ + temp |= CCDC_VD_POL_NEGATIVE; + } else { /* y/c external sync mode */ + temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + + /* pack the data to 8-bit */ + temp |= CCDC_DATA_PACK_ENABLE; + + regw(temp, MODESET); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* configure the order of y cb cr in SD-RAM */ + temp = (params->pix_order << CCDC_Y8POS_SHIFT); + temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; + regw(temp, CCDCFG); + + /* + * configure the horizontal line offset. This is done by rounding up + * width to a multiple of 16 pixels and multiply by two to account for + * y:cb:cr 4:2:2 data + */ + regw(((params->win.width * 2 + 31) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); + } + + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +/* + * ccdc_config_black_clamp() + * configure parameters for Optical Black Clamp + */ +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->b_clamp_enable) { + /* configure DCSub */ + regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); + regw(0x0000, CLAMP); + return; + } + /* Enable the Black clamping, set sample lines and pixels */ + val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; + regw(val, CLAMP); + + /* If Black clamping is enable then make dcsub 0 */ + val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) + << CCDC_NUM_LINE_CALC_SHIFT; + regw(val, DCSUB); +} + +/* + * ccdc_config_black_compense() + * configure parameters for Black Compensation + */ +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = (bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT); + regw(val, BLKCMP1); + + val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT); + regw(val, BLKCMP0); +} + +/* + * ccdc_write_dfc_entry() + * write an entry in the dfc table. + */ +int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) +{ +/* TODO This is to be re-visited and adjusted */ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 val, count = DFC_WRITE_WAIT_COUNT; + + regw(dfc->dft_corr_vert[index], DFCMEM0); + regw(dfc->dft_corr_horz[index], DFCMEM1); + regw(dfc->dft_corr_sub1[index], DFCMEM2); + regw(dfc->dft_corr_sub2[index], DFCMEM3); + regw(dfc->dft_corr_sub3[index], DFCMEM4); + /* set WR bit to write */ + val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; + regw(val, DFCMEMCTL); + + /* + * Assume, it is very short. If we get an error, we need to + * adjust this value + */ + while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) + count--; + /* + * TODO We expect the count to be non-zero to be successful. Adjust + * the count if write requires more time + */ + + if (count) { + dev_err(dev, "defect table write timeout !!!\n"); + return -1; + } + return 0; +} + +/* + * ccdc_config_vdfc() + * configure parameters for Vertical Defect Correction + */ +static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) +{ + u32 val; + int i; + + /* Configure General Defect Correction. The table used is from IPIPE */ + val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; + + /* Configure Vertical Defect Correction if needed */ + if (!dfc->ver_dft_en) { + /* Enable only General Defect Correction */ + regw(val, DFCCTL); + return 0; + } + + if (dfc->table_size > CCDC_DFT_TABLE_SIZE) + return -EINVAL; + + val |= CCDC_DFCCTL_VDFC_DISABLE; + val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << + CCDC_DFCCTL_VDFCSL_SHIFT; + val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << + CCDC_DFCCTL_VDFCUDA_SHIFT; + val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << + CCDC_DFCCTL_VDFLSFT_SHIFT; + regw(val , DFCCTL); + + /* clear address ptr to offset 0 */ + val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + + /* write defect table entries */ + for (i = 0; i < dfc->table_size; i++) { + /* increment address for non zero index */ + if (i != 0) + val = CCDC_DFCMEMCTL_INC_ADDR; + regw(val, DFCMEMCTL); + if (ccdc_write_dfc_entry(i, dfc) < 0) + return -EFAULT; + } + + /* update saturation level and enable dfc */ + regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); + val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << + CCDC_DFCCTL_VDFCEN_SHIFT); + regw(val, DFCCTL); + return 0; +} + +/* + * ccdc_config_csc() + * configure parameters for color space conversion + * Each register CSCM0-7 has two values in S8Q5 format. + */ +static void ccdc_config_csc(struct ccdc_csc *csc) +{ + u32 val1, val2; + int i; + + if (!csc->enable) + return; + + /* Enable the CSC sub-module */ + regw(CCDC_CSC_ENABLE, CSCCTL); + + /* Converting the co-eff as per the format of the register */ + for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + /* + * convert decimal part to binary. Use 2 decimal + * precision, user values range from .00 - 0.99 + */ + val1 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + } else { + + /* CSCM - MSB */ + val2 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + val2 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } +} + +/* + * ccdc_config_color_patterns() + * configure parameters for color patterns + */ +static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, + struct ccdc_col_pat *pat1) +{ + u32 val; + + val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | + (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | + (pat1->elop << 12) | (pat1->elep << 14)); + regw(val, COLPTN); +} + +/* This function will configure CCDC for Raw mode image capture */ +static int ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* restore power on defaults to register */ + ccdc_restore_defaults(); + + /* CCDCFG register: + * set CCD Not to swap input since input is RAW data + * set FID detection function to Latch at V-Sync + * set WENLOG - ccdc valid area to AND + * set TRGSEL to WENBIT + * set EXTRG to DISABLE + * disable latching function on VSYNC - shadowed registers + */ + regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); + + /* + * Set VDHD direction to input, input type to raw input + * normal data polarity, do not use external WEN + */ + val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | + CCDC_EXWEN_DISABLE); + + /* + * Configure the vertical sync polarity (MODESET.VDPOL), horizontal + * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), + * frame format(progressive or interlace), & pixel format (Input mode) + */ + val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); + + /* set pack for alaw compression */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + val |= CCDC_DATA_PACK_ENABLE; + + /* Configure for LPF */ + if (config_params->lpf_enable) + val |= (config_params->lpf_enable & CCDC_LPF_MASK) << + CCDC_LPF_SHIFT; + + /* Configure the data shift */ + val |= (config_params->datasft & CCDC_DATASFT_MASK) << + CCDC_DATASFT_SHIFT; + regw(val , MODESET); + dev_dbg(dev, "\nWriting 0x%x to MODESET...\n", val); + + /* Configure the Median Filter threshold */ + regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); + + /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ + val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | + CCDC_CFA_MOSAIC; + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val |= (CCDC_ALAW_ENABLE | + ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) << + CCDC_GAMMAWD_INPUT_SHIFT)); + } + + /* Configure Median filter1 & filter2 */ + val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | + (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); + + regw(val, GAMMAWD); + dev_dbg(dev, "\nWriting 0x%x to GAMMAWD...\n", val); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1); + + /* Optical Clamp Averaging */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Vertical Defect Correction if needed */ + if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) + return -EFAULT; + + /* color space conversion */ + ccdc_config_csc(&config_params->csc); + + /* color pattern */ + ccdc_config_color_patterns(&config_params->col_pat_field0, + &config_params->col_pat_field1); + + /* Configure the Gain & offset control */ + ccdc_config_gain_offset(); + + dev_dbg(dev, "\nWriting %x to COLPTN...\n", val); + + /* Configure DATAOFST register */ + val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_H_SHIFT; + val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_V_SHIFT; + regw(val, DATAOFST); + + /* configuring HSIZE register */ + val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* If pack 8 is enable then 1 pixel will take 1 byte */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) { + val |= (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + /* adjust to multiple of 32 */ + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } else { + /* else one pixel will take 2 byte */ + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_INVERSE); + } else { + /* For interlace non inverse mode */ + regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_NORMAL); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_enable) { + /* For progessive inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_INVERSE); + } else { + /* For progessive non inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_NORMAL); + } + } + dev_dbg(dev, "\nend of ccdc_config_raw..."); + return 0; +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + alaw->enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x007f, STADRH); + regw((addr >> 5) & 0x0ffff, STADRL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM355 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm355_ccdc_init(void) +{ + printk(KERN_NOTICE "dm355_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm355_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm355_ccdc_init); +module_exit(dm355_ccdc_exit); diff --git a/drivers/media/video/ti-media/dm355_ccdc_regs.h b/drivers/media/video/ti-media/dm355_ccdc_regs.h new file mode 100644 index 00000000000..d6d2ef0533b --- /dev/null +++ b/drivers/media/video/ti-media/dm355_ccdc_regs.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM355_CCDC_REGS_H +#define _DM355_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDWIDTH 0x08 +#define VDWIDTH 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define NPH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define NLV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define STADRH 0x3c +#define STADRL 0x40 +#define CLAMP 0x44 +#define DCSUB 0x48 +#define COLPTN 0x4c +#define BLKCMP0 0x50 +#define BLKCMP1 0x54 +#define MEDFILT 0x58 +#define RYEGAIN 0x5c +#define GRCYGAIN 0x60 +#define GBGGAIN 0x64 +#define BMGGAIN 0x68 +#define OFFSET 0x6c +#define OUTCLIP 0x70 +#define VDINT0 0x74 +#define VDINT1 0x78 +#define RSV0 0x7c +#define GAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +#define FMTCFG 0x8c +#define FMTPLEN 0x90 +#define FMTSPH 0x94 +#define FMTLNH 0x98 +#define FMTSLV 0x9c +#define FMTLNV 0xa0 +#define FMTRLEN 0xa4 +#define FMTHCNT 0xa8 +#define FMT_ADDR_PTR_B 0xac +#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) +#define FMTPGM_VF0 0xcc +#define FMTPGM_VF1 0xd0 +#define FMTPGM_AP0 0xd4 +#define FMTPGM_AP1 0xd8 +#define FMTPGM_AP2 0xdc +#define FMTPGM_AP3 0xe0 +#define FMTPGM_AP4 0xe4 +#define FMTPGM_AP5 0xe8 +#define FMTPGM_AP6 0xec +#define FMTPGM_AP7 0xf0 +#define LSCCFG1 0xf4 +#define LSCCFG2 0xf8 +#define LSCH0 0xfc +#define LSCV0 0x100 +#define LSCKH 0x104 +#define LSCKV 0x108 +#define LSCMEMCTL 0x10c +#define LSCMEMD 0x110 +#define LSCMEMQ 0x114 +#define DFCCTL 0x118 +#define DFCVSAT 0x11c +#define DFCMEMCTL 0x120 +#define DFCMEM0 0x124 +#define DFCMEM1 0x128 +#define DFCMEM2 0x12c +#define DFCMEM3 0x130 +#define DFCMEM4 0x134 +#define CSCCTL 0x138 +#define CSCM0 0x13c +#define CSCM1 0x140 +#define CSCM2 0x144 +#define CSCM3 0x148 +#define CSCM4 0x14c +#define CSCM5 0x150 +#define CSCM6 0x154 +#define CSCM7 0x158 +#define DATAOFST 0x15c +#define CCDC_REG_LAST DATAOFST +/************************************************************** +* Define for various register bit mask and shifts for CCDC +* +**************************************************************/ +#define CCDC_RAW_IP_MODE 0 +#define CCDC_VDHDOUT_INPUT 0 +#define CCDC_YCINSWP_RAW (0 << 4) +#define CCDC_EXWEN_DISABLE 0 +#define CCDC_DATAPOL_NORMAL 0 +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 +#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) +#define CCDC_CCDCFG_WENLOG_AND 0 +#define CCDC_CCDCFG_TRGSEL_WEN 0 +#define CCDC_CCDCFG_EXTRG_DISABLE 0 +#define CCDC_CFA_MOSAIC 0 +#define CCDC_Y8POS_SHIFT 11 + +#define CCDC_VDC_DFCVSAT_MASK 0x3fff +#define CCDC_DATAOFST_MASK 0x0ff +#define CCDC_DATAOFST_H_SHIFT 0 +#define CCDC_DATAOFST_V_SHIFT 8 +#define CCDC_GAMMAWD_CFA_MASK 1 +#define CCDC_GAMMAWD_CFA_SHIFT 5 +#define CCDC_GAMMAWD_INPUT_SHIFT 2 +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_VD_POL_NEGATIVE (1 << 2) +#define CCDC_FRM_FMT_MASK 1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_VDHDOUT_MASK 1 +#define CCDC_VDHDOUT_SHIFT 0 +#define CCDC_EXWEN_MASK 1 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_INPUT_MODE_MASK 3 +#define CCDC_INPUT_MODE_SHIFT 12 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_DATAPOL_MASK 1 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_WEN_ENABLE (1 << 1) +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE 1 +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_REC656IF_BT656_EN 3 + +#define CCDC_FMTCFG_FMTMODE_MASK 3 +#define CCDC_FMTCFG_FMTMODE_SHIFT 1 +#define CCDC_FMTCFG_LNUM_MASK 3 +#define CCDC_FMTCFG_LNUM_SHIFT 4 +#define CCDC_FMTCFG_ADDRINC_MASK 7 +#define CCDC_FMTCFG_ADDRINC_SHIFT 8 + +#define CCDC_CCDCFG_FIDMD_SHIFT 6 +#define CCDC_CCDCFG_WENLOG_SHIFT 8 +#define CCDC_CCDCFG_TRGSEL_SHIFT 9 +#define CCDC_CCDCFG_EXTRG_SHIFT 10 +#define CCDC_CCDCFG_MSBINVI_SHIFT 13 + +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_HSIZE_FLIP_MASK 1 +#define CCDC_HSIZE_VAL_MASK 0xFFF +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D +#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D +#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 +#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 +#define CCDC_START_PX_HOR_MASK 0x7FFF +#define CCDC_NUM_PX_HOR_MASK 0x7FFF +#define CCDC_START_VER_ONE_MASK 0x7FFF +#define CCDC_START_VER_TWO_MASK 0x7FFF +#define CCDC_NUM_LINES_VER 0x7FFF + +#define CCDC_BLK_CLAMP_ENABLE (1 << 15) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x1FFF +#define CCDC_BLK_SAMPLE_LN_MASK 3 +#define CCDC_BLK_SAMPLE_LN_SHIFT 13 + +#define CCDC_NUM_LINE_CALC_MASK 3 +#define CCDC_NUM_LINE_CALC_SHIFT 14 + +#define CCDC_BLK_DC_SUB_MASK 0x3FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 +#define CCDC_BLK_COMP_R_COMP_SHIFT 8 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF + +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_CSC_ENABLE 1 +#define CCDC_CSC_DEC_MAX 32 + +#define CCDC_MFILT1_SHIFT 10 +#define CCDC_MFILT2_SHIFT 8 +#define CCDC_MED_FILT_THRESH 0x3FFF +#define CCDC_LPF_MASK 1 +#define CCDC_LPF_SHIFT 14 +#define CCDC_OFFSET_MASK 0x3FF +#define CCDC_DATASFT_MASK 7 +#define CCDC_DATASFT_SHIFT 8 + +#define CCDC_DF_ENABLE 1 + +#define CCDC_FMTPLEN_P0_MASK 0xF +#define CCDC_FMTPLEN_P1_MASK 0xF +#define CCDC_FMTPLEN_P2_MASK 7 +#define CCDC_FMTPLEN_P3_MASK 7 +#define CCDC_FMTPLEN_P0_SHIFT 0 +#define CCDC_FMTPLEN_P1_SHIFT 4 +#define CCDC_FMTPLEN_P2_SHIFT 8 +#define CCDC_FMTPLEN_P3_SHIFT 12 + +#define CCDC_FMTSPH_MASK 0x1FFF +#define CCDC_FMTLNH_MASK 0x1FFF +#define CCDC_FMTSLV_MASK 0x1FFF +#define CCDC_FMTLNV_MASK 0x7FFF +#define CCDC_FMTRLEN_MASK 0x1FFF +#define CCDC_FMTHCNT_MASK 0x1FFF + +#define CCDC_ADP_INIT_MASK 0x1FFF +#define CCDC_ADP_LINE_SHIFT 13 +#define CCDC_ADP_LINE_MASK 3 +#define CCDC_FMTPGN_APTR_MASK 7 + +#define CCDC_DFCCTL_GDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) +#define CCDC_DFCCTL_VDFCEN_SHIFT 4 +#define CCDC_DFCCTL_VDFCSL_MASK 3 +#define CCDC_DFCCTL_VDFCSL_SHIFT 5 +#define CCDC_DFCCTL_VDFCUDA_MASK 1 +#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 +#define CCDC_DFCCTL_VDFLSFT_MASK 3 +#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 +#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 +#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 +#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 +#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) + +#define CCDC_LSCCFG_GFTSF_MASK 7 +#define CCDC_LSCCFG_GFTSF_SHIFT 1 +#define CCDC_LSCCFG_GFTINV_MASK 0xf +#define CCDC_LSCCFG_GFTINV_SHIFT 4 +#define CCDC_LSC_GFTABLE_SEL_MASK 3 +#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 +#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 +#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 +#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 +#define CCDC_LSC_GFMODE_MASK 3 +#define CCDC_LSC_GFMODE_SHIFT 4 +#define CCDC_LSC_DISABLE 0 +#define CCDC_LSC_ENABLE 1 +#define CCDC_LSC_TABLE1_SLC 0 +#define CCDC_LSC_TABLE2_SLC 1 +#define CCDC_LSC_TABLE3_SLC 2 +#define CCDC_LSC_MEMADDR_RESET (1 << 2) +#define CCDC_LSC_MEMADDR_INCR (0 << 2) +#define CCDC_LSC_FRAC_MASK_T1 0xFF +#define CCDC_LSC_INT_MASK 3 +#define CCDC_LSC_FRAC_MASK 0x3FFF +#define CCDC_LSC_CENTRE_MASK 0x3FFF +#define CCDC_LSC_COEF_MASK 0xff +#define CCDC_LSC_COEFL_SHIFT 0 +#define CCDC_LSC_COEFU_SHIFT 8 +#define CCDC_GAIN_MASK 0x7FF +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +/* Power on Defaults in hardware */ +#define MODESET_DEFAULT 0x200 +#define CULH_DEFAULT 0xFFFF +#define CULV_DEFAULT 0xFF +#define GAIN_DEFAULT 256 +#define OUTCLIP_DEFAULT 0x3FFF +#define LSCCFG2_DEFAULT 0xE + +#endif diff --git a/drivers/media/video/ti-media/dm644x_ccdc.c b/drivers/media/video/ti-media/dm644x_ccdc.c new file mode 100644 index 00000000000..bc7a9ce5638 --- /dev/null +++ b/drivers/media/video/ti-media/dm644x_ccdc.c @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + * + * CCDC hardware module for DM6446 + * ------------------------------ + * + * This module is for configuring CCD controller of DM6446 VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters. This file is named DM644x so that other + * variants such DM6443 may be supported using the same module. + * + * TODO: Test Raw bayer parameter settings and bayer capture + * Split module parameter structure to module specific ioctl structs + * investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include +#include +#include +#include +#include +#include "dm644x_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM6446"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .config_params = { + .data_sz = CCDC_DATA_10BITS, + }, +}; + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_PAL, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +#define CCDC_MAX_RAW_YUV_FORMATS 2 + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; +static enum vpfe_hw_if_type ccdc_if_type; + +#define CCDC_SZ_REGS SZ_1K + +static u32 ccdc_ctx[CCDC_SZ_REGS / sizeof(u32)]; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int flag) +{ + regw(flag, CCDC_PCR); +} + +static void ccdc_enable_vport(int flag) +{ + if (flag) + /* enable video port */ + regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); + else + regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); +} + +/* + * ccdc_setwin() + * This function will configure the window size + * to be capture in CCDC reg + */ +void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val = 0, mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, + CCDC_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); + regw(val, CCDC_VDINT); + + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | + (mid_img & CCDC_VDINT_VDINT1_MASK); + regw(val, CCDC_VDINT); + + } + regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, + CCDC_VERT_START); + regw(vert_nr_lines, CCDC_VERT_LINES); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static void ccdc_readregs(void) +{ + unsigned int val = 0; + + val = regr(CCDC_ALAW); + dev_notice(dev, "\nReading 0x%x to ALAW...\n", val); + val = regr(CCDC_CLAMP); + dev_notice(dev, "\nReading 0x%x to CLAMP...\n", val); + val = regr(CCDC_DCSUB); + dev_notice(dev, "\nReading 0x%x to DCSUB...\n", val); + val = regr(CCDC_BLKCMP); + dev_notice(dev, "\nReading 0x%x to BLKCMP...\n", val); + val = regr(CCDC_FPC_ADDR); + dev_notice(dev, "\nReading 0x%x to FPC_ADDR...\n", val); + val = regr(CCDC_FPC); + dev_notice(dev, "\nReading 0x%x to FPC...\n", val); + val = regr(CCDC_FMTCFG); + dev_notice(dev, "\nReading 0x%x to FMTCFG...\n", val); + val = regr(CCDC_COLPTN); + dev_notice(dev, "\nReading 0x%x to COLPTN...\n", val); + val = regr(CCDC_FMT_HORZ); + dev_notice(dev, "\nReading 0x%x to FMT_HORZ...\n", val); + val = regr(CCDC_FMT_VERT); + dev_notice(dev, "\nReading 0x%x to FMT_VERT...\n", val); + val = regr(CCDC_HSIZE_OFF); + dev_notice(dev, "\nReading 0x%x to HSIZE_OFF...\n", val); + val = regr(CCDC_SDOFST); + dev_notice(dev, "\nReading 0x%x to SDOFST...\n", val); + val = regr(CCDC_VP_OUT); + dev_notice(dev, "\nReading 0x%x to VP_OUT...\n", val); + val = regr(CCDC_SYN_MODE); + dev_notice(dev, "\nReading 0x%x to SYN_MODE...\n", val); + val = regr(CCDC_HORZ_INFO); + dev_notice(dev, "\nReading 0x%x to HORZ_INFO...\n", val); + val = regr(CCDC_VERT_START); + dev_notice(dev, "\nReading 0x%x to VERT_START...\n", val); + val = regr(CCDC_VERT_LINES); + dev_notice(dev, "\nReading 0x%x to VERT_LINES...\n", val); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->alaw.enable) { + if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || + (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || + (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { + dev_dbg(dev, "\nInvalid data line select"); + return -1; + } + } + return 0; +} + +static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_virtaddr = NULL; + unsigned int *fpc_physaddr = NULL; + + memcpy(config_params, raw_params, sizeof(*raw_params)); + /* + * allocate memory for fault pixel table and copy the user + * values to the table + */ + if (!config_params->fault_pxl.enable) + return 0; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + fpc_virtaddr = (unsigned int *)phys_to_virt( + (unsigned long)fpc_physaddr); + /* + * Allocate memory for FPC table if current + * FPC table buffer is not big enough to + * accomodate FPC Number requested + */ + if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) { + if (fpc_physaddr != NULL) { + free_pages((unsigned long)fpc_physaddr, + get_order + (config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + + /* Allocate memory for FPC table */ + fpc_virtaddr = + (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, + get_order(raw_params-> + fault_pxl.fp_num * + FP_NUM_BYTES)); + + if (fpc_virtaddr == NULL) { + dev_dbg(dev, + "\nUnable to allocate memory for FPC"); + return -EFAULT; + } + fpc_physaddr = + (unsigned int *)virt_to_phys((void *)fpc_virtaddr); + } + + /* Copy number of fault pixels and FPC table */ + config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num; + if (copy_from_user(fpc_virtaddr, + (void __user *)raw_params->fault_pxl.fpc_table_addr, + config_params->fault_pxl.fp_num * FP_NUM_BYTES)) { + dev_dbg(dev, "\n copy_from_user failed"); + return -EFAULT; + } + config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr; + return 0; +} + +static int ccdc_close(struct device *dev) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + + if (fpc_physaddr != NULL) { + fpc_virtaddr = (unsigned int *) + phys_to_virt((unsigned long)fpc_physaddr); + free_pages((unsigned long)fpc_virtaddr, + get_order(config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + return 0; +} + +/* + * ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void ccdc_restore_defaults(void) +{ + int i; + + /* disable CCDC */ + ccdc_enable(0); + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + regw(0, i); + regw(CCDC_NO_CULLING, CCDC_CULLING); + regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); +} + +static int ccdc_open(struct device *device) +{ + dev = device; + ccdc_restore_defaults(); + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_enable_vport(1); + return 0; +} + +static void ccdc_sbl_reset(void) +{ + vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying" + "ccdc params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + if (!ccdc_update_raw_params(&ccdc_raw_params)) + return 0; + } + return -EINVAL; +} + +/* + * ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 syn_mode; + + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + ccdc_restore_defaults(); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << + CCDC_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << + CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | + CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE; + if (ccdc_if_type == VPFE_BT656_10BIT) + syn_mode |= CCDC_SYN_MODE_10BITS; + else + syn_mode |= CCDC_SYN_MODE_8BITS; + /* + * Enable A-Law + */ + regw(regr(CCDC_ALAW) | CCDC_ALAW_ENABLE, CCDC_ALAW); + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + regw(syn_mode, CCDC_SYN_MODE); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + if (ccdc_if_type == VPFE_BT656_10BIT) + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT, + CCDC_CCDCFG); + else + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte bondary. So clear LSB 5 bits + */ + regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; + regw(val, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x%x to DCSUB...\n", val); + regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x0000 to CLAMP...\n"); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << + CCDC_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << + CCDC_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); + regw(val, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x%x to CLAMP...\n", val); + /* If Black clamping is enable then make dcsub 0 */ + regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x00000000 to DCSUB...\n"); +} + +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT)); + regw(val, CCDC_BLKCMP); +} + +static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) +{ + u32 val; + + /* Initially disable FPC */ + val = CCDC_FPC_DISABLE; + regw(val, CCDC_FPC); + + if (!fpc->enable) + return; + + /* Configure Fault pixel if needed */ + regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); + dev_dbg(dev, "\nWriting 0x%x to FPC_ADDR...\n", + (fpc->fpc_table_addr)); + /* Write the FPC params with FPC disable */ + val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; + regw(val, CCDC_FPC); + + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); + /* read the FPC register */ + val = regr(CCDC_FPC) | CCDC_FPC_ENABLE; + regw(val, CCDC_FPC); + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); +} + +/* + * ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +void ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int syn_mode = 0; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* Reset CCDC */ + ccdc_restore_defaults(); + + /* Disable latching function registers on VSYNC */ + regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((config_params->data_sz & CCDC_DATA_SZ_MASK) << + CCDC_DATA_SZ_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | + CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); + regw(val, CCDC_ALAW); + dev_dbg(dev, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); + + /* Configure Black Clamp */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Configure Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Configure Fault Pixel Correction */ + ccdc_config_fpc(&config_params->fault_pxl); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= CCDC_DATA_PACK_ENABLE; + +#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE + /* enable video port */ + val = CCDC_ENABLE_VIDEO_PORT; +#else + /* disable video port */ + val = CCDC_DISABLE_VIDEO_PORT; +#endif + + if (config_params->data_sz == CCDC_DATA_8BITS) + val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + else + val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + /* Write value in FMTCFG */ + regw(val, CCDC_FMTCFG); + + dev_dbg(dev, "\nWriting 0x%x to FMTCFG...\n", val); + /* Configure the color pattern according to mt9t001 sensor */ + regw(CCDC_COLPTN_VAL, CCDC_COLPTN); + + dev_dbg(dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); + /* + * Configure Data formatter(Video port) pixel selection + * (FMT_HORZ, FMT_VERT) + */ + val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << + CCDC_FMT_HORZ_FMTSPH_SHIFT) | + (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); + regw(val, CCDC_FMT_HORZ); + + dev_dbg(dev, "\nWriting 0x%x to FMT_HORZ...\n", val); + val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) + << CCDC_FMT_VERT_FMTSLV_SHIFT; + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; + else + val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; + + dev_dbg(dev, "\nparams->win.height 0x%x ...\n", + params->win.height); + regw(val, CCDC_FMT_VERT); + + dev_dbg(dev, "\nWriting 0x%x to FMT_VERT...\n", val); + + dev_dbg(dev, "\nbelow regw(val, FMT_VERT)..."); + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & + CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); + else + /* else one pixel will take 2 byte */ + regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + + CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, + CCDC_HSIZE_OFF); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For intelace inverse mode */ + regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x4B6D to SDOFST...\n"); + } + + else { + /* For intelace non inverse mode */ + regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0249 to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0000 to SDOFST...\n"); + } + + /* + * Configure video port pixel selection (VPOUT) + * Here -1 is to make the height value less than FMT_VERT.FMTLNV + */ + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) + << CCDC_VP_OUT_VERT_NUM_SHIFT; + else + val = + ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - + 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << + CCDC_VP_OUT_VERT_NUM_SHIFT; + + val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) + << CCDC_VP_OUT_HORZ_NUM_SHIFT; + val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; + regw(val, CCDC_VP_OUT); + + dev_dbg(dev, "\nWriting 0x%x to VP_OUT...\n", val); + regw(syn_mode, CCDC_SYN_MODE); + dev_dbg(dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nend of ccdc_config_raw..."); + ccdc_readregs(); +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + ccdc_hw_params_raw.config_params.alaw.enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(CCDC_SYN_MODE) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw(addr & 0xffffffe0, CCDC_SDR_ADDR); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + case VPFE_BT656_10BIT: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static void ccdc_save_context(void) +{ + ccdc_ctx[CCDC_PCR] = regr(CCDC_PCR); + ccdc_ctx[CCDC_SYN_MODE] = regr(CCDC_SYN_MODE); + ccdc_ctx[CCDC_HD_VD_WID] = regr(CCDC_HD_VD_WID); + ccdc_ctx[CCDC_PIX_LINES] = regr(CCDC_PIX_LINES); + ccdc_ctx[CCDC_HORZ_INFO] = regr(CCDC_HORZ_INFO); + ccdc_ctx[CCDC_VERT_START] = regr(CCDC_VERT_START); + ccdc_ctx[CCDC_VERT_LINES] = regr(CCDC_VERT_LINES); + ccdc_ctx[CCDC_CULLING] = regr(CCDC_CULLING); + ccdc_ctx[CCDC_HSIZE_OFF] = regr(CCDC_HSIZE_OFF); + ccdc_ctx[CCDC_SDOFST] = regr(CCDC_SDOFST); + ccdc_ctx[CCDC_SDR_ADDR] = regr(CCDC_SDR_ADDR); + ccdc_ctx[CCDC_CLAMP] = regr(CCDC_CLAMP); + ccdc_ctx[CCDC_DCSUB] = regr(CCDC_DCSUB); + ccdc_ctx[CCDC_COLPTN] = regr(CCDC_COLPTN); + ccdc_ctx[CCDC_BLKCMP] = regr(CCDC_BLKCMP); + ccdc_ctx[CCDC_FPC] = regr(CCDC_FPC); + ccdc_ctx[CCDC_FPC_ADDR] = regr(CCDC_FPC_ADDR); + ccdc_ctx[CCDC_VDINT] = regr(CCDC_VDINT); + ccdc_ctx[CCDC_ALAW] = regr(CCDC_ALAW); + ccdc_ctx[CCDC_REC656IF] = regr(CCDC_REC656IF); + ccdc_ctx[CCDC_CCDCFG] = regr(CCDC_CCDCFG); + ccdc_ctx[CCDC_FMTCFG] = regr(CCDC_FMTCFG); + ccdc_ctx[CCDC_FMT_HORZ] = regr(CCDC_FMT_HORZ); + ccdc_ctx[CCDC_FMT_VERT] = regr(CCDC_FMT_VERT); + ccdc_ctx[CCDC_FMT_ADDR0] = regr(CCDC_FMT_ADDR0); + ccdc_ctx[CCDC_FMT_ADDR1] = regr(CCDC_FMT_ADDR1); + ccdc_ctx[CCDC_FMT_ADDR2] = regr(CCDC_FMT_ADDR2); + ccdc_ctx[CCDC_FMT_ADDR3] = regr(CCDC_FMT_ADDR3); + ccdc_ctx[CCDC_FMT_ADDR4] = regr(CCDC_FMT_ADDR4); + ccdc_ctx[CCDC_FMT_ADDR5] = regr(CCDC_FMT_ADDR5); + ccdc_ctx[CCDC_FMT_ADDR6] = regr(CCDC_FMT_ADDR6); + ccdc_ctx[CCDC_FMT_ADDR7] = regr(CCDC_FMT_ADDR7); + ccdc_ctx[CCDC_PRGEVEN_0] = regr(CCDC_PRGEVEN_0); + ccdc_ctx[CCDC_PRGEVEN_1] = regr(CCDC_PRGEVEN_1); + ccdc_ctx[CCDC_PRGODD_0] = regr(CCDC_PRGODD_0); + ccdc_ctx[CCDC_PRGODD_1] = regr(CCDC_PRGODD_1); + ccdc_ctx[CCDC_VP_OUT] = regr(CCDC_VP_OUT); +} + +static void ccdc_restore_context(void) +{ + regw(ccdc_ctx[CCDC_SYN_MODE], CCDC_SYN_MODE); + regw(ccdc_ctx[CCDC_HD_VD_WID], CCDC_HD_VD_WID); + regw(ccdc_ctx[CCDC_PIX_LINES], CCDC_PIX_LINES); + regw(ccdc_ctx[CCDC_HORZ_INFO], CCDC_HORZ_INFO); + regw(ccdc_ctx[CCDC_VERT_START], CCDC_VERT_START); + regw(ccdc_ctx[CCDC_VERT_LINES], CCDC_VERT_LINES); + regw(ccdc_ctx[CCDC_CULLING], CCDC_CULLING); + regw(ccdc_ctx[CCDC_HSIZE_OFF], CCDC_HSIZE_OFF); + regw(ccdc_ctx[CCDC_SDOFST], CCDC_SDOFST); + regw(ccdc_ctx[CCDC_SDR_ADDR], CCDC_SDR_ADDR); + regw(ccdc_ctx[CCDC_CLAMP], CCDC_CLAMP); + regw(ccdc_ctx[CCDC_DCSUB], CCDC_DCSUB); + regw(ccdc_ctx[CCDC_COLPTN], CCDC_COLPTN); + regw(ccdc_ctx[CCDC_BLKCMP], CCDC_BLKCMP); + regw(ccdc_ctx[CCDC_FPC], CCDC_FPC); + regw(ccdc_ctx[CCDC_FPC_ADDR], CCDC_FPC_ADDR); + regw(ccdc_ctx[CCDC_VDINT], CCDC_VDINT); + regw(ccdc_ctx[CCDC_ALAW], CCDC_ALAW); + regw(ccdc_ctx[CCDC_REC656IF], CCDC_REC656IF); + regw(ccdc_ctx[CCDC_CCDCFG], CCDC_CCDCFG); + regw(ccdc_ctx[CCDC_FMTCFG], CCDC_FMTCFG); + regw(ccdc_ctx[CCDC_FMT_HORZ], CCDC_FMT_HORZ); + regw(ccdc_ctx[CCDC_FMT_VERT], CCDC_FMT_VERT); + regw(ccdc_ctx[CCDC_FMT_ADDR0], CCDC_FMT_ADDR0); + regw(ccdc_ctx[CCDC_FMT_ADDR1], CCDC_FMT_ADDR1); + regw(ccdc_ctx[CCDC_FMT_ADDR2], CCDC_FMT_ADDR2); + regw(ccdc_ctx[CCDC_FMT_ADDR3], CCDC_FMT_ADDR3); + regw(ccdc_ctx[CCDC_FMT_ADDR4], CCDC_FMT_ADDR4); + regw(ccdc_ctx[CCDC_FMT_ADDR5], CCDC_FMT_ADDR5); + regw(ccdc_ctx[CCDC_FMT_ADDR6], CCDC_FMT_ADDR6); + regw(ccdc_ctx[CCDC_FMT_ADDR7], CCDC_FMT_ADDR7); + regw(ccdc_ctx[CCDC_PRGEVEN_0], CCDC_PRGEVEN_0); + regw(ccdc_ctx[CCDC_PRGEVEN_1], CCDC_PRGEVEN_1); + regw(ccdc_ctx[CCDC_PRGODD_0], CCDC_PRGODD_0); + regw(ccdc_ctx[CCDC_PRGODD_1], CCDC_PRGODD_1); + regw(ccdc_ctx[CCDC_VP_OUT], CCDC_VP_OUT); + regw(ccdc_ctx[CCDC_PCR], CCDC_PCR); +} +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM6446 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .reset = ccdc_sbl_reset, + .enable = ccdc_enable, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + .save_context = ccdc_save_context, + .restore_context = ccdc_restore_context, + }, +}; + +static int dm644x_ccdc_init(void) +{ + printk(KERN_NOTICE "dm644x_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm644x_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm644x_ccdc_init); +module_exit(dm644x_ccdc_exit); diff --git a/drivers/media/video/ti-media/dm644x_ccdc_regs.h b/drivers/media/video/ti-media/dm644x_ccdc_regs.h new file mode 100644 index 00000000000..b18d166f181 --- /dev/null +++ b/drivers/media/video/ti-media/dm644x_ccdc_regs.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM644X_CCDC_REGS_H +#define _DM644X_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define CCDC_PID 0x0 +#define CCDC_PCR 0x4 +#define CCDC_SYN_MODE 0x8 +#define CCDC_HD_VD_WID 0xc +#define CCDC_PIX_LINES 0x10 +#define CCDC_HORZ_INFO 0x14 +#define CCDC_VERT_START 0x18 +#define CCDC_VERT_LINES 0x1c +#define CCDC_CULLING 0x20 +#define CCDC_HSIZE_OFF 0x24 +#define CCDC_SDOFST 0x28 +#define CCDC_SDR_ADDR 0x2c +#define CCDC_CLAMP 0x30 +#define CCDC_DCSUB 0x34 +#define CCDC_COLPTN 0x38 +#define CCDC_BLKCMP 0x3c +#define CCDC_FPC 0x40 +#define CCDC_FPC_ADDR 0x44 +#define CCDC_VDINT 0x48 +#define CCDC_ALAW 0x4c +#define CCDC_REC656IF 0x50 +#define CCDC_CCDCFG 0x54 +#define CCDC_FMTCFG 0x58 +#define CCDC_FMT_HORZ 0x5c +#define CCDC_FMT_VERT 0x60 +#define CCDC_FMT_ADDR0 0x64 +#define CCDC_FMT_ADDR1 0x68 +#define CCDC_FMT_ADDR2 0x6c +#define CCDC_FMT_ADDR3 0x70 +#define CCDC_FMT_ADDR4 0x74 +#define CCDC_FMT_ADDR5 0x78 +#define CCDC_FMT_ADDR6 0x7c +#define CCDC_FMT_ADDR7 0x80 +#define CCDC_PRGEVEN_0 0x84 +#define CCDC_PRGEVEN_1 0x88 +#define CCDC_PRGODD_0 0x8c +#define CCDC_PRGODD_1 0x90 +#define CCDC_VP_OUT 0x94 + + +/*************************************************************** +* Define for various register bit mask and shifts for CCDC +****************************************************************/ +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_HSIZE_OFF_MASK 0xffffffe0 +#define CCDC_32BYTE_ALIGN_VAL 31 +#define CCDC_FRM_FMT_MASK 0x1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF +#define CCDC_WEN_ENABLE (1 << 17) +#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE (1 << 3) +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_BLK_CLAMP_ENABLE (1 << 31) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x7FFF +#define CCDC_BLK_ST_PXL_SHIFT 10 +#define CCDC_BLK_SAMPLE_LN_MASK 7 +#define CCDC_BLK_SAMPLE_LN_SHIFT 28 +#define CCDC_BLK_SAMPLE_LINE_MASK 7 +#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 +#define CCDC_BLK_DC_SUB_MASK 0x03FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 +#define CCDC_BLK_COMP_R_COMP_SHIFT 24 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_DISABLE 0 +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMTCFG_VPIN_MASK 7 +#define CCDC_FMTCFG_VPIN_SHIFT 12 +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF +#define CCDC_HORZ_INFO_SPH_SHIFT 16 +#define CCDC_VERT_START_SLV0_SHIFT 16 +#define CCDC_VDINT_VDINT0_SHIFT 16 +#define CCDC_VDINT_VDINT1_MASK 0xFFFF +#define CCDC_PPC_RAW 1 +#define CCDC_DCSUB_DEFAULT_VAL 0 +#define CCDC_CLAMP_DEFAULT_VAL 0 +#define CCDC_ENABLE_VIDEO_PORT 0x8000 +#define CCDC_DISABLE_VIDEO_PORT 0 +#define CCDC_COLPTN_VAL 0xBB11BB11 +#define CCDC_TWO_BYTES_PER_PIXEL 2 +#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D +#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define CCDC_INTERLACED_HEIGHT_SHIFT 1 +#define CCDC_SYN_MODE_INPMOD_SHIFT 12 +#define CCDC_SYN_MODE_INPMOD_MASK 3 +#define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_MODE_10BITS (6 << 8) +#define CCDC_SYN_MODE_11BITS (5 << 8) +#define CCDC_SYN_MODE_12BITS (4 << 8) +#define CCDC_SYN_MODE_13BITS (3 << 8) +#define CCDC_SYN_MODE_14BITS (2 << 8) +#define CCDC_SYN_MODE_15BITS (1 << 8) +#define CCDC_SYN_MODE_16BITS (0 << 8) +#define CCDC_SYN_FLDMODE_MASK 1 +#define CCDC_SYN_FLDMODE_SHIFT 7 +#define CCDC_REC656IF_BT656_EN 3 +#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2) +#define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_CCDCFG_BW656_10BIT (1 << 5) +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_NO_CULLING 0xffff00ff +#endif diff --git a/drivers/media/video/ti-media/omap_vout.c b/drivers/media/video/ti-media/omap_vout.c new file mode 100644 index 00000000000..a13f65e7b14 --- /dev/null +++ b/drivers/media/video/ti-media/omap_vout.c @@ -0,0 +1,2625 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "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 + +/* configuration macros */ +#define VOUT_NAME "omap_vout" + +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 + +#define NUM_OF_VIDEO_CHANNELS 2 + +#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; +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"); + +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 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, + }, +}; + +#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, + unsigned 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; + } + 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 + "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) +{ + int i; + + for (i = 0; i < 4; i++) + omap_vrfb_release_ctx(&vout->vrfb_context[i]); + + 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(const 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(const struct omap_vout_device *vout) +{ + return vout->rotation || vout->mirror; +} + +/* Reverse the rotation degree if mirroring is enabled */ +static inline int calc_rotation(const struct omap_vout_device *vout) +{ + 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; + } +} + +/* Free the V4L2 buffers */ +static void omap_vout_free_buffers(struct omap_vout_device *vout) +{ + int i, numbuffers; + + /* Allocate memory for the buffers */ + 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; + bool yuv_mode; + + /* 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; + + if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 || + vout->dss_mode == OMAP_DSS_COLOR_UYVY) + yuv_mode = true; + else + yuv_mode = false; + + 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->bpp, yuv_mode); + } + return 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; + 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; + } + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "%s Offset:%x\n", __func__, *cropped_offset); + return 0; +} + +/* convert V4L2 pixel format to DSS pixel format */ +static enum omap_color_mode video_mode_to_dss_mode(struct omap_vout_device + *vout) +{ + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct v4l2_pix_format *pix = &vout->pix; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + switch (pix->pixelformat) { + 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: + return (ovl->id == OMAP_DSS_VIDEO1) ? + OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32; + case V4L2_PIX_FMT_BGR32: + return OMAP_DSS_COLOR_RGBX32; + + default: + return -EINVAL; + } + return -EINVAL; +} + +/* 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) +{ + int r = 0; + enum omap_color_mode mode = 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 (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; + 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; + info.rotation_type = OMAP_DSS_ROT_VRFB; + info.screen_width = 2048; + } + + 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); + + 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; +} + +/* Initialize the overlay structure */ +int omapvid_init(struct omap_vout_device *vout, u32 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 width 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); + 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; + +} + +/* 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; + + startindex = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex) + *count = startindex; + + 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; + } + + if (V4L2_MEMORY_MMAP != vout->memory) + return 0; + + /* 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) { + 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; + 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; + + 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; + 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; + struct videobuf_dmabuf *dmabuf = NULL; + enum dss_rotation rotation; + struct vid_vrfb_dma *tx; + + 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; + /* 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; + } + 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]; + 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_QUEUED; +} + +/* 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; + + 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; + 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; + } + 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; + + /* 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); + 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); + 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; + } + /* Video1 plane does not support global alpha */ + 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; + + 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; + } + 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; + 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]); + dmabuf->vmalloc = (void *) vout->buf_virt_addr[i]; + 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; + + 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; + } + } + + 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; + } + + 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; + + 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; + 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; + + if (omap_vout_calculate_offset(vout)) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i] + + vout->cropped_offset; + + 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; + if (ovl->set_overlay_info(ovl, &info)) + return -EINVAL; + } + } + + /* First save the configuration in ovelray structure */ + r = omapvid_init(vout, 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; + return ovl->set_overlay_info(ovl, &info); + } + } + + /* Turn of the pipeline */ + r = omapvid_apply_changes(vout); + if (r) { + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + return r; + } + INIT_LIST_HEAD(&vout->dma_queue); + 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 omap_overlay_manager_info info; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + 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; + + 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); + + /*Initialize the control variables for + rotation, flipping and background color. */ + control = vout->control; + control[0].id = V4L2_CID_ROTATE; + control[0].value = 0; + vout->rotation = 0; + vout->mirror = 0; + vout->control[2].id = V4L2_CID_HFLIP; + vout->control[2].value = 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, j, r = 0; + int image_width, image_height; + unsigned numbuffers; + struct video_device *vfd; + int static_vrfb_allocation = 0, vrfb_num_bufs = 4; + + vout = vid_dev->vouts[vid_num]; + vfd = vout->vfd; + + 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; + } + 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; + /* Select video2 if only 1 overlay is controlled by V4L2 */ + 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); + + if (r) + goto error2; + else + goto success; +error2: + omap_vout_release_vrfb(vout); + 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 overlay and video2 overlay. + * Setup the Display attached to that overlays + */ + for (i = 1; i < 3; 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->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 /* MANUAL_UPDATE */ + 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); + } + } + } + + if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) { + printk(KERN_ERR VOUT_NAME "v4l2_device_register failed\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); + } + 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; + 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; + + /* First save the configuration in ovelray structure */ + r = omapvid_init(vout, 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; + /* First save the configuration in ovelray structure */ + r = omapvid_init(vout, 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); + } + } + + omap_vout_release_vrfb(vout); + + omap_vout_free_buffers(vout); + /* Free the VRFB buffer if allocated + * init time + */ + if (vout->vrfb_static_allocation) + omap_vout_free_vrfb_buffers(vout); + + 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/ti-media/omap_voutdef.h b/drivers/media/video/ti-media/omap_voutdef.h new file mode 100644 index 00000000000..3b2f7456c21 --- /dev/null +++ b/drivers/media/video/ti-media/omap_voutdef.h @@ -0,0 +1,148 @@ +/* + * 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 + +#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 +#define MAX_VOUT_DEV 2 +#define MAX_OVLS 3 +#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 previously allocated buffer which is big enough */ + int buffer_size; + /* keep buffer info across opens */ + unsigned long buf_virt_addr[VIDEO_MAX_FRAME]; + unsigned long buf_phy_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 variables */ + 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/ti-media/omap_voutlib.c b/drivers/media/video/ti-media/omap_voutlib.c new file mode 100644 index 00000000000..ff4f7f7e1c1 --- /dev/null +++ b/drivers/media/video/ti-media/omap_voutlib.c @@ -0,0 +1,319 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; + + if (cpu_is_omap24xx()) { + /* + * omap24xx supports 8x to 1/2x scaling. + */ + if ((crop->height/win->w.height) >= 2) { + /* The maximum vertical downsizing ratio is 2:1 */ + crop->height = win->w.height * 2; + } + if ((crop->width/win->w.width) >= 2) { + /* The maximum horizontal downsizing ratio is 2:1 */ + crop->width = win->w.width * 2; + } + if (crop->width > 768) { + /* The OMAP2420 vertical resizing line buffer is 768 + * pixels wide. + */ + if (crop->height != win->w.height) + crop->width = 768; + } + } else if (cpu_is_omap34xx()) { + /* + * omap34xx supports 8x to 1/4x scaling. + */ + 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 (cpu_is_omap24xx()) { + 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 (cpu_is_omap24xx()) { + if (vresize > 2048) + vresize = 2048; + else if (vresize == 0) + vresize = 1; + } else if (cpu_is_omap34xx()) { + 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 (cpu_is_omap24xx()) { + if (hresize > 2048) + hresize = 2048; + else if (hresize == 0) + hresize = 1; + } else if (cpu_is_omap34xx()) { + 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; + } + if (cpu_is_omap24xx()) { + /* + * omap24xx supports 8x to 1/2x scaling. + */ + if ((try_crop.height/win->w.height) >= 2) { + /* The maximum vertical downsizing ratio is 2:1 */ + try_crop.height = win->w.height * 2; + } + if ((try_crop.width/win->w.width) >= 2) { + /* The maximum horizontal downsizing ratio is 2:1 */ + try_crop.width = win->w.width * 2; + } + if (try_crop.width > 768) { + /* The OMAP2420 vertical resizing line buffer is + * 768 pixels wide. + */ + if (try_crop.height != win->w.height) + try_crop.width = 768; + } + } else if (cpu_is_omap34xx()) { + /* Check for resizing constraints + * 34xx allow 8x to 1/4x scaling. + */ + 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/ti-media/omap_voutlib.h b/drivers/media/video/ti-media/omap_voutlib.h new file mode 100644 index 00000000000..8ef6e25b9e6 --- /dev/null +++ b/drivers/media/video/ti-media/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/ti-media/vpfe_capture.c b/drivers/media/video/ti-media/vpfe_capture.c new file mode 100644 index 00000000000..cd82947ea15 --- /dev/null +++ b/drivers/media/video/ti-media/vpfe_capture.c @@ -0,0 +1,2482 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355 only). + * + * Features supported + * - MMAP IO + * - Capture using TVP5146 over BT.656 + * - support for interfacing decoders using sub device model + * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV + * data capture to SDRAM. + * TODO list + * - Support multiple REQBUF after open + * - Support for de-allocating buffers through REQBUF + * - Support for chaining Image Processor + * - Support for static allocation of buffers + * - Support for STREAMON before QBUF + * - Support for control ioctls + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdc_hw_device.h" + +static int debug; +static u32 numbuffers = 2; +static u32 bufsize = (720 * 576 * 2); +static int interface; + +module_param(interface, bool, S_IRUGO); +module_param(numbuffers, uint, S_IRUGO); +module_param(bufsize, uint, S_IRUGO); +module_param(debug, bool, 0644); + +/** + * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002 + * and for capture raw bayer data from camera sensors such as MT9T031. At this + * point there is problem in co-existence of mt9t031 and tvp5146 due to i2c + * address collision. So set the variable below from bootargs to do either video + * capture or camera capture. + * interface = 0 - video capture (from TVP514x or such), + * interface = 1 - Camera capture (from MT9T031 or such) + * Re-visit this when we fix the co-existence issue + */ +MODULE_PARM_DESC(interface, "interface 0-1 (default:0)"); +MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); +MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + /* 0 - progressive, 1 - interlaced */ + int frame_format; +}; + +/* ccdc configuration */ +struct ccdc_config { + /* This make sure vpfe is probed and ready to go */ + int vpfe_probed; + /* name of ccdc device */ + char name[32]; + /* for storing mem maps for CCDC */ + int ccdc_addr_size; + void *__iomem ccdc_addr; +}; + +/* data structures */ +static struct vpfe_config_params config_params = { + .min_numbuffers = 2, + .numbuffers = 2, + .min_bufsize = 720 * 480 * 2, + .device_bufsize = 720 * 576 * 2, +}; + +/* ccdc device registered */ +static struct ccdc_hw_device *ccdc_dev; +/* lock for accessing ccdc information */ +static DEFINE_MUTEX(ccdc_lock); +/* ccdc configuration */ +static struct ccdc_config *ccdc_cfg; + +const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ +static const struct vpfe_pixel_format vpfe_pix_fmts[] = { + { + .fmtdesc = { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit A-Law compr.", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + }, + .bpp = 1, + .subdev_pix_fmt = V4L2_PIX_FMT_SGRBG10, + }, + { + .fmtdesc = { + .index = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb - 16bit", + .pixelformat = V4L2_PIX_FMT_SBGGR16, + }, + .bpp = 2, + .subdev_pix_fmt = V4L2_PIX_FMT_SGRBG10, + }, + { + .fmtdesc = { + .index = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit DPCM compr.", + .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, + }, + .bpp = 1, + .subdev_pix_fmt = V4L2_PIX_FMT_SGRBG10, + }, + { + .fmtdesc = { + .index = 3, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + .bpp = 2, + .subdev_pix_fmt = V4L2_PIX_FMT_UYVY, + }, + { + .fmtdesc = { + .index = 4, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + .bpp = 2, + .subdev_pix_fmt = V4L2_PIX_FMT_UYVY, + }, + { + .fmtdesc = { + .index = 5, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Y/CbCr 4:2:0 - Semi planar", + .pixelformat = V4L2_PIX_FMT_NV12, + }, + .bpp = 1, + .subdev_pix_fmt = V4L2_PIX_FMT_UYVY, + }, +}; + +/** + * vpfe_lookup_pix_format() - lookup an entry in the vpfe pix format table + * @pix_format: v4l pix format + * This function lookup an entry in the vpfe pix format table based on + * pix_format + */ +static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { + if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat) + return &vpfe_pix_fmts[i]; + } + return NULL; +} + +/* + * vpfe_register_ccdc_device. CCDC module calls this to + * register with vpfe capture + */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev) +{ + int ret = 0; + printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); + + BUG_ON(!dev->hw_ops.open); + BUG_ON(!dev->hw_ops.enable); + BUG_ON(!dev->hw_ops.set_hw_if_params); + BUG_ON(!dev->hw_ops.configure); + BUG_ON(!dev->hw_ops.set_buftype); + BUG_ON(!dev->hw_ops.get_buftype); + BUG_ON(!dev->hw_ops.enum_pix); + BUG_ON(!dev->hw_ops.set_frame_format); + BUG_ON(!dev->hw_ops.get_frame_format); + BUG_ON(!dev->hw_ops.get_pixel_format); + BUG_ON(!dev->hw_ops.set_pixel_format); + BUG_ON(!dev->hw_ops.set_params); + BUG_ON(!dev->hw_ops.set_image_window); + BUG_ON(!dev->hw_ops.get_image_window); + BUG_ON(!dev->hw_ops.get_line_length); + BUG_ON(!dev->hw_ops.setfbaddr); + BUG_ON(!dev->hw_ops.getfid); + + mutex_lock(&ccdc_lock); + if (NULL == ccdc_cfg) { + /* + * TODO. Will this ever happen? if so, we need to fix it. + * Proabably we need to add the request to a linked list and + * walk through it during vpfe probe + */ + printk(KERN_ERR "vpfe capture not initialized\n"); + ret = -EFAULT; + goto unlock; + } + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + ret = -EINVAL; + goto unlock; + } + + if (ccdc_dev) { + printk(KERN_ERR "ccdc already registered\n"); + ret = -EINVAL; + goto unlock; + } + + ccdc_dev = dev; + dev->hw_ops.set_ccdc_base(ccdc_cfg->ccdc_addr, + ccdc_cfg->ccdc_addr_size); +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} +EXPORT_SYMBOL(vpfe_register_ccdc_device); + +/* + * vpfe_unregister_ccdc_device. CCDC module calls this to + * unregister with vpfe capture + */ +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev) +{ + if (NULL == dev) { + printk(KERN_ERR "invalid ccdc device ptr\n"); + return; + } + + printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", + dev->name); + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + return; + } + + mutex_lock(&ccdc_lock); + ccdc_dev = NULL; + mutex_unlock(&ccdc_lock); + return; +} +EXPORT_SYMBOL(vpfe_unregister_ccdc_device); + +/* + * vpfe_get_camera_frame_params() + * Get the image parameters such as max height and width, frame format + * etc and update the stdinfo accordingly. This is a work around to get + * the maximum width, height and frame format since camera driver doesn't + * support s_std. + */ +static int vpfe_get_camera_frame_params(struct vpfe_device *vpfe_dev) +{ + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + struct v4l2_format sd_fmt; + int ret; + + /* TODO: Currently there is no support for setting timings + * in sensor similar to S_STD. So get the limits of width and height + * using try format. In future we should be able to set + * timings for a specific resolution and fps. In that case + * we know the limits for the specific timing and this code + * would require change. + */ + memset(&sd_fmt, 0, sizeof(sd_fmt)); + sd_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + /* hard code it to match that of mt9t031 sensor */ + sd_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGRBG10; + /* use a value big enough */ + sd_fmt.fmt.pix.width = 1 << 31; + sd_fmt.fmt.pix.height = 1 << 31; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, try_fmt, &sd_fmt); + + if (!ret) { + vpfe_dev->std_info.active_pixels = sd_fmt.fmt.pix.width; + vpfe_dev->std_info.active_lines = sd_fmt.fmt.pix.height; + /* hard code the frame format to be progressive always. */ + vpfe_dev->std_info.frame_format = 0; + } + return ret; +} + +/* + * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings + */ +static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev, + struct v4l2_format *f) +{ + struct v4l2_rect image_win; + enum ccdc_buftype buf_type; + enum ccdc_frmfmt frm_fmt; + + vpfe_dev->crop.top = 0; + vpfe_dev->crop.left = 0; + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ccdc_dev->hw_ops.get_image_window(&image_win); + vpfe_dev->crop.width = image_win.width; + vpfe_dev->crop.height = image_win.height; + f->fmt.pix.width = image_win.width; + f->fmt.pix.height = image_win.height; + f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length(); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + buf_type = ccdc_dev->hw_ops.get_buftype(); + f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format(); + frm_fmt = ccdc_dev->hw_ops.get_frame_format(); + if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + f->fmt.pix.field = V4L2_FIELD_NONE; + else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) + f->fmt.pix.field = V4L2_FIELD_SEQ_TB; + else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n"); + return -EINVAL; + } + } else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n"); + return -EINVAL; + } + return 0; +} + +/* + * vpfe_config_ccdc_image_format() + * For a pix format, configure ccdc to setup the capture + */ +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret = 0; + + if (ccdc_dev->hw_ops.set_pixel_format( + vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); + + switch (vpfe_dev->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + return ret; +} +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device support g_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, + const v4l2_std_id *std_id) +{ + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + struct v4l2_format sd_fmt; + int i, ret = 0; + + /* configure the ccdc based on standard */ + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & *std_id) { + vpfe_dev->std_info.active_pixels = + vpfe_standards[i].width; + vpfe_dev->std_info.active_lines = + vpfe_standards[i].height; + vpfe_dev->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe_dev->std_index = i; + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); + return -EINVAL; + } + + vpfe_dev->crop.top = 0; + vpfe_dev->crop.left = 0; + vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; + vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; + vpfe_dev->fmt.fmt.pix.width = vpfe_dev->crop.width; + vpfe_dev->fmt.fmt.pix.height = vpfe_dev->crop.height; + + /* first field and frame format based on standard frame format */ + if (vpfe_dev->std_info.frame_format) { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + /* assume V4L2_PIX_FMT_UYVY as default */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + } else { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_NONE; + /* assume V4L2_PIX_FMT_SBGGR8 */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + } + + sd_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + /* if sub device supports g_fmt, override the defaults */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, g_fmt, &sd_fmt); + + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&vpfe_dev->v4l2_dev, + "error in getting g_fmt from sub device\n"); + return ret; + } + vpfe_dev->fmt = sd_fmt; + /* Sets the values in CCDC */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + if (ret) + return ret; + + /* Update the values of sizeimage and bytesperline */ + if (!ret) { + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + } + return ret; +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + /* set first input of current subdevice as the current input */ + vpfe_dev->current_input = 0; + /* + * set default standard. For camera device, we cannot set standard. + * So we set it to -1. Otherwise, first entry in the standard is the + * is the default + */ + if (vpfe_dev->current_subdev->is_camera) { + vpfe_dev->std_index = -1; + /* + * Configure the vpfe default format information based on ccdc + * defaults + */ + ret = vpfe_get_ccdc_image_format(vpfe_dev, &vpfe_dev->fmt); + /* Get max width and height available for capture from camera */ + if (!ret) + ret = vpfe_get_camera_frame_params(vpfe_dev); + + } else { + vpfe_dev->std_index = 0; + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); + } + + if (ret) + return ret; + + /* now open the ccdc device to initialize it */ + mutex_lock(&ccdc_lock); + if (NULL == ccdc_dev) { + v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); + ret = -ENODEV; + goto unlock; + } + + if (!try_module_get(ccdc_dev->owner)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); + ret = -ENODEV; + goto unlock; + } + ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); + if (!ret) + vpfe_dev->initialized = 1; + + /* Clear all VPFE/CCDC interrupts */ + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(-1); + +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} + +/* + * vpfe_open : It creates object of file handle structure and + * stores it in private_data member of filepointer + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); + + if (!vpfe_dev->cfg->num_subdevs) { + v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + if (NULL == fh) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->vpfe_dev = vpfe_dev; + mutex_lock(&vpfe_dev->lock); + /* If decoder is not initialized. initialize it */ + if (!vpfe_dev->initialized) { + if (vpfe_initialize_device(vpfe_dev)) { + mutex_unlock(&vpfe_dev->lock); + return -ENODEV; + } + } + /* Increment device usrs counter */ + vpfe_dev->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&vpfe_dev->prio, &fh->prio); + mutex_unlock(&vpfe_dev->lock); + return 0; +} + +static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vpfe_dev->next_frm->queue); + vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; + if (V4L2_MEMORY_USERPTR == vpfe_dev->memory) + addr = vpfe_dev->cur_frm->boff; + else + addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + if (V4L2_MEMORY_USERPTR == vpfe_dev->memory) + addr = vpfe_dev->cur_frm->boff; + else + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) +{ + struct timeval timevalue; + + do_gettimeofday(&timevalue); + vpfe_dev->cur_frm->ts = timevalue; + vpfe_dev->cur_frm->state = VIDEOBUF_DONE; + vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; + wake_up_interruptible(&vpfe_dev->cur_frm->done); + vpfe_dev->cur_frm = vpfe_dev->next_frm; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + enum v4l2_field field; + int fid; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); + field = vpfe_dev->fmt.fmt.pix.field; + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) { + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; + } + + /* only for 6446 this will be applicable */ + if (NULL != ccdc_dev->hw_ops.reset) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "frame format is progressive...\n"); + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; + } + + /* interlaced or TB capture check which field we are in hardware */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + vpfe_dev->field_id ^= 1; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", + fid, vpfe_dev->field_id); + if (fid == vpfe_dev->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the next frame + * is available, release the current frame and move on + */ + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, reconfigure + * the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(vpfe_dev); + + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe_dev->dma_queue_lock); + if (!list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe_dev->field_id = fid; + } + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + + return IRQ_HANDLED; +} + +/* vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) { + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; + } + + spin_lock(&vpfe_dev->dma_queue_lock); + if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && + !list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + + return IRQ_HANDLED; +} + +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); +} + +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { + return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, + IRQF_DISABLED, "vpfe_capture1", + vpfe_dev); + } + return 0; +} + +/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ +static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + vpfe_dev->started = 0; + ccdc_dev->hw_ops.enable(0); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); +} + +/* + * vpfe_release : This function deletes buffer queue, frees the + * buffers and the vpfe file handle + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&vpfe_dev->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (vpfe_dev->started) { + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, + "stream off failed in subdev\n"); + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + videobuf_streamoff(&vpfe_dev->buffer_queue); + } + vpfe_dev->io_usrs = 0; + vpfe_dev->numbuffers = config_params.numbuffers; + } + + /* Decrement device usrs counter */ + vpfe_dev->usrs--; + /* Close the priority */ + v4l2_prio_close(&vpfe_dev->prio, &fh->prio); + /* If this is the last file handle */ + if (!vpfe_dev->usrs) { + vpfe_dev->initialized = 0; + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(vpfe_dev->pdev); + module_put(ccdc_dev->owner); + } + mutex_unlock(&vpfe_dev->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kfree(fh); + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Get the device object and file handle object */ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (vpfe_dev->started) + return videobuf_poll_stream(file, + &vpfe_dev->buffer_queue, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_check_format() + * This function adjust the input pixel format as per hardware + * capabilities and update the same in pixfmt. + * Following algorithm used :- + * + * If given pixformat is not in the vpfe list of pix formats or not + * supported by the hardware, current value of pixformat in the device + * is used + * If given field is not supported, then current field is used. If field + * is different from current, then it is matched with that from sub device. + * Minimum height is 2 lines for interlaced or tb field and 1 line for + * progressive. Maximum height is clamped to active active lines of scan + * Minimum width is 32 bytes in memory and width is clamped to active + * pixels of scan. + * bytesperline is a multiple of 32. + */ +static const struct vpfe_pixel_format * + vpfe_check_format(struct vpfe_device *vpfe_dev, + struct v4l2_pix_format *pixfmt) +{ + u32 min_height = 1, min_width = 32, max_width, max_height; + const struct vpfe_pixel_format *vpfe_pix_fmt; + u32 pix; + int temp, found; + + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + if (NULL == vpfe_pix_fmt) { + /* + * use current pixel format in the vpfe device. We + * will find this pix format in the table + */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check if hw supports it */ + temp = 0; + found = 0; + while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { + if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) { + found = 1; + break; + } + temp++; + } + + if (!found) { + /* use current pixel format */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + /* + * Since this is currently used in the vpfe device, we + * will find this pix format in the table + */ + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check what field format is supported */ + if (pixfmt->field == V4L2_FIELD_ANY) { + /* if field is any, use current value as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + } + + /* + * if field is not same as current field in the vpfe device + * try matching the field with the sub device field + */ + if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { + /* + * If field value is not in the supported fields, use current + * field used in the device as default + */ + switch (pixfmt->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + /* if sub device is supporting progressive, use that */ + if (!vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_NONE: + if (vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_INTERLACED; + break; + + default: + /* use current field as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + break; + } + } + + /* Now adjust image resolutions supported */ + if (pixfmt->field == V4L2_FIELD_INTERLACED || + pixfmt->field == V4L2_FIELD_SEQ_TB) + min_height = 2; + + max_width = vpfe_dev->std_info.active_pixels; + max_height = vpfe_dev->std_info.active_lines; + min_width /= vpfe_pix_fmt->bpp; + + v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); + + pixfmt->width = clamp((pixfmt->width), min_width, max_width); + pixfmt->height = clamp((pixfmt->height), min_height, max_height); + + /* If interlaced, adjust height to be a multiple of 2 */ + if (pixfmt->field == V4L2_FIELD_INTERLACED) + pixfmt->height &= (~1); + /* + * recalculate bytesperline and sizeimage since width + * and height might have changed + */ + pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) + & ~31); + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = + pixfmt->bytesperline * pixfmt->height + + ((pixfmt->bytesperline * pixfmt->height) >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height =" + " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, + pixfmt->bytesperline, pixfmt->sizeimage); + return vpfe_pix_fmt; +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + cap->version = VPFE_CAPTURE_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); + /* Fill in the information about format */ + *fmt = vpfe_dev->fmt; + return ret; +} + +static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmt; + int temp_index; + u32 pix; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); + + if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) + return -EINVAL; + + /* Fill in the information about format */ + pix_fmt = vpfe_lookup_pix_format(pix); + if (NULL != pix_fmt) { + temp_index = fmt->index; + *fmt = pix_fmt->fmtdesc; + fmt->index = temp_index; + return 0; + } + return -EINVAL; +} + +static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + struct vpfe_subdev_info *sdinfo; + struct v4l2_format sd_fmt; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); + + /* If streaming is started, return error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Check for valid frame format */ + pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); + + if (NULL == pix_fmts) + return -EINVAL; + + /* store the pixel format in the device object */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + sdinfo = vpfe_dev->current_subdev; + if (sdinfo->is_camera) { + /* + * Current implementation of camera sub device calculates + * sensor timing values based on S_FMT. So we need to + * explicitely call S_FMT first and make sure it succeeds before + * setting capture parameters in ccdc + */ + sd_fmt = *fmt; + sd_fmt.fmt.pix.pixelformat = pix_fmts->subdev_pix_fmt; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_fmt, &sd_fmt); + + if (!ret) { + vpfe_dev->crop.width = fmt->fmt.pix.width; + vpfe_dev->crop.height = fmt->fmt.pix.height; + } + } + + if (!ret) { + vpfe_dev->fmt = *fmt; + /* set image capture parameters in the ccdc */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + } + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); + + pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); + if (NULL == pix_fmts) + return -EINVAL; + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + sdinfo->num_inputs)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->module_name, + vpfe_dev->current_subdev->module_name)) { + if (vpfe_dev->current_input >= sdinfo->num_inputs) + return -EINVAL; + *app_input_index = j + vpfe_dev->current_input; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index, temp_index; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + temp_index = inp->index; + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev, + &index, + inp->index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "input information not found" + " for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; + memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input)); + inp->index = temp_index; + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe_dev, index); +} + + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev_index, subdev_inp_index; + struct vpfe_route *route; + u32 input = 0, output = 0; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev_index, + &subdev_inp_index, + index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); + goto unlock_out; + } + + sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + + if (!sdinfo->registered) { + ret = -EINVAL; + goto unlock_out; + } + + if (vpfe_dev->cfg->setup_input) { + if (vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "couldn't setup input for %s\n", + sdinfo->module_name); + goto unlock_out; + } + } + + route = &sdinfo->routes[subdev_inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, + s_routing, input, output, 0); + + if (ret) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "vpfe_doioctl:error in setting input in" + " decoder \n"); + ret = -EINVAL; + goto unlock_out; + } + } + + vpfe_dev->current_subdev = sdinfo; + vpfe_dev->current_input = subdev_inp_index; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* set the default image parameters in the device */ + if (vpfe_dev->current_subdev->is_camera) + vpfe_dev->std_index = -1; + /* for camera, use ccdc default parameters */ + ret = vpfe_get_ccdc_image_format(vpfe_dev, &vpfe_dev->fmt); + /* Get max width and height available for capture from camera */ + if (!ret) + ret = vpfe_get_camera_frame_params(vpfe_dev); + else { + vpfe_dev->std_index = 0; + /* + * For non-camera sub device, use standard to configure vpfe + * default + */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); + } +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + sdinfo = vpfe_dev->current_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + sdinfo = vpfe_dev->current_subdev; + /* If streaming is started, return device busy error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + ret = vpfe_config_image_format(vpfe_dev, std_id); + +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + + if (vpfe_dev->std_index < 0 || + vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Standard not supported\n"); + return -EINVAL; + } + *std_id = vpfe_standards[vpfe_dev->std_index].std_id; + return 0; +} + +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + + /* + * if we are using mmap, check the size of the allocated buffer is less + * than or equal to the maximum specified in the driver. Assume here the + * user has called S_FMT and sizeimage has been calculated. + */ + *size = vpfe_dev->fmt.fmt.pix.sizeimage; + if (vpfe_dev->memory == V4L2_MEMORY_MMAP && + vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize) + *size = config_params.device_bufsize; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + return 0; +} + +/* + * vpfe_uservirt_to_phys: This function is used to convert user + * space virtual address to physical address. + */ +static u32 vpfe_uservirt_to_phys(struct vpfe_device *vpfe_dev, u32 virtp) +{ + struct mm_struct *mm = current->mm; + unsigned long physp = 0; + 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 addr */ + 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 { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "get_user_pages failed\n"); + return 0; + } + } + return physp; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vpfe_dev->fmt.fmt.pix.width; + vb->height = vpfe_dev->fmt.fmt.pix.height; + vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; + vb->field = field; + } + + if (V4L2_MEMORY_USERPTR == vpfe_dev->memory) { + if (!vb->baddr) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "buffer address is 0\n"); + return -EINVAL; + } + vb->boff = vpfe_uservirt_to_phys(vpfe_dev, vb->baddr); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(vb->boff, 32)) + return -EINVAL; + } + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + list_add_tail(&vb->queue, &vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + /* + * We need to flush the buffer from the dma queue since + * they are de-allocated + */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. currently support REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (vpfe_dev->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + vpfe_dev->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, + &vpfe_videobuf_qops, + vpfe_dev->pdev, + &vpfe_dev->irqlock, + req_buf->type, + vpfe_dev->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh); + + fh->io_allowed = 1; + vpfe_dev->io_usrs = 1; + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); +} + +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + return videobuf_qbuf(&vpfe_dev->buffer_queue, p); +} + +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + return videobuf_dqbuf(&vpfe_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +static int vpfe_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + sdinfo = vpfe_dev->current_subdev; + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, queryctrl, qctrl); + + if (ret) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + + return ret; +} + +static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, g_ctrl, ctrl); +} + +static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_ctrl, ctrl); +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) +{ + struct v4l2_rect image_win; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); + + ccdc_dev->hw_ops.get_image_window(&image_win); + vpfe_dev->field_off = image_win.height * image_win.width; +} + +/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ +static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + ccdc_dev->hw_ops.enable(1); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + vpfe_dev->started = 1; +} + +/* + * vpfe_streamon. Assume the DMA queue is not empty. + * application is expected to call QBUF before calling + * this ioctl. If not, driver returns error + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + unsigned long addr; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); + return -EINVAL; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&vpfe_dev->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&vpfe_dev->buffer_queue); + if (ret) + return ret; + + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + goto streamoff; + /* Get the next frame from the buffer queue */ + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + vpfe_dev->cur_frm = vpfe_dev->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe_dev->cur_frm->queue); + /* Mark state of the current frame to active */ + vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vpfe_dev->field_id = 0; + if (V4L2_MEMORY_USERPTR == vpfe_dev->memory) + addr = vpfe_dev->cur_frm->boff; + else + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe_dev); + + if (vpfe_attach_irq(vpfe_dev) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in attaching interrupt handle\n"); + ret = -EFAULT; + goto unlock_out; + } + if (ccdc_dev->hw_ops.configure() < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in configuring ccdc\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); + vpfe_start_ccdc_capture(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +unlock_out: + mutex_unlock(&vpfe_dev->lock); +streamoff: + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + return ret; +} + +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); + + memset(crop, 0, sizeof(struct v4l2_cropcap)); + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sdinfo = vpfe_dev->current_subdev; + + if (!sdinfo->is_camera) { + + if (vpfe_dev->std_index < 0 || + vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) + return -EINVAL; + + crop->bounds.width = vpfe_standards[vpfe_dev->std_index].width; + crop->defrect.width = crop->bounds.width; + crop->bounds.height = + vpfe_standards[vpfe_dev->std_index].height; + crop->defrect.height = crop->bounds.height; + crop->pixelaspect = + vpfe_standards[vpfe_dev->std_index].pixelaspect; + } else { + /* camera interface */ + crop->bounds.width = vpfe_dev->std_info.active_pixels; + crop->defrect.width = crop->bounds.width; + crop->bounds.height = vpfe_dev->std_info.active_lines; + crop->defrect.height = crop->bounds.height; + crop->pixelaspect.numerator = 1; + crop->pixelaspect.denominator = 1; + } + return 0; +} + +static int vpfe_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n"); + + crop->c = vpfe_dev->crop; + return 0; +} + +static int vpfe_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n"); + + if (vpfe_dev->started) { + /* make sure streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, + "Cannot change crop when streaming is ON\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (crop->c.top < 0 || crop->c.left < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "doesn't support negative values for top & left\n"); + ret = -EINVAL; + goto unlock_out; + } + + /* adjust the width to 16 pixel boundry */ + crop->c.width = ((crop->c.width + 15) & ~0xf); + + /* make sure parameters are valid */ + if ((crop->c.left + crop->c.width > + vpfe_dev->std_info.active_pixels) || + (crop->c.top + crop->c.height > + vpfe_dev->std_info.active_lines)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.set_image_window(&crop->c); + vpfe_dev->fmt.fmt.pix.width = crop->c.width; + vpfe_dev->fmt.fmt.pix.height = crop->c.height; + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + vpfe_dev->crop = crop->c; +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +static long vpfe_param_handler(struct file *file, void *priv, + int cmd, void *param) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n"); + + if (vpfe_dev->started) { + /* only allowed if streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, "device already started\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + switch (cmd) { + case VPFE_CMD_S_CCDC_RAW_PARAMS: + v4l2_warn(&vpfe_dev->v4l2_dev, + "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n"); + ret = ccdc_dev->hw_ops.set_params(param); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in setting parameters in CCDC\n"); + goto unlock_out; + } + if (vpfe_get_ccdc_image_format(vpfe_dev, &vpfe_dev->fmt) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Invalid image format at CCDC\n"); + goto unlock_out; + } + break; + default: + ret = -EINVAL; + } +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_queryctrl = vpfe_queryctrl, + .vidioc_g_ctrl = vpfe_g_ctrl, + .vidioc_s_ctrl = vpfe_s_ctrl, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_cropcap = vpfe_cropcap, + .vidioc_g_crop = vpfe_g_crop, + .vidioc_s_crop = vpfe_s_crop, + .vidioc_default = vpfe_param_handler, +}; + +static struct vpfe_device *vpfe_initialize(void) +{ + struct vpfe_device *vpfe_dev; + + /* Default number of buffers should be 3 */ + if ((numbuffers > 0) && + (numbuffers < config_params.min_numbuffers)) + numbuffers = config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid buffer size is + * given + */ + if (bufsize < config_params.min_bufsize) + bufsize = config_params.min_bufsize; + + config_params.numbuffers = numbuffers; + + if (numbuffers) + config_params.device_bufsize = bufsize; + + /* Allocate memory for device objects */ + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + + return vpfe_dev; +} + +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int i; + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + clk_disable(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + kfree(vpfe_dev->clks); + v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n"); +} + +/** + * vpfe_enable_clock() - Enable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Enables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. re-visit this if this assumption is not true + */ +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -EFAULT, i; + + if (!vpfe_cfg->num_clocks) + return 0; + + vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks * + sizeof(struct clock *), GFP_KERNEL); + + if (NULL == vpfe_dev->clks) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (NULL == vpfe_cfg->clocks[i]) { + v4l2_err(vpfe_dev->pdev->driver, + "clock %s is not defined in vpfe config\n", + vpfe_cfg->clocks[i]); + goto out; + } + + vpfe_dev->clks[i] = clk_get(vpfe_dev->pdev, + vpfe_cfg->clocks[i]); + if (NULL == vpfe_dev->clks[i]) { + v4l2_err(vpfe_dev->pdev->driver, + "Failed to get clock %s\n", + vpfe_cfg->clocks[i]); + goto out; + } + + if (clk_enable(vpfe_dev->clks[i])) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe clock %s not enabled\n", + vpfe_cfg->clocks[i]); + goto out; + } + + v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled", + vpfe_cfg->clocks[i]); + } + return 0; +out: + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_dev->clks[i]) + clk_put(vpfe_dev->clks[i]); + } + kfree(vpfe_dev->clks); + return ret; +} + +/** + * vpfe_probe : vpfe probe function + * @pdev: platform device pointer + * + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each device objects + */ +static __init int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct resource *res1; + struct vpfe_device *vpfe_dev; + struct i2c_adapter *i2c_adap; + struct video_device *vfd; + int ret = -ENOMEM, i, j; + int num_subdevs = 0; + + /* Get the pointer to the device object */ + vpfe_dev = vpfe_initialize(); + + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + vpfe_dev->pdev = &pdev->dev; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_cfg = pdev->dev.platform_data; + vpfe_dev->cfg = vpfe_cfg; + if (NULL == vpfe_cfg->ccdc || + NULL == vpfe_cfg->card_name || + NULL == vpfe_cfg->sub_devs) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + mutex_lock(&ccdc_lock); + /* Allocate memory for ccdc configuration */ + ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); + if (NULL == ccdc_cfg) { + v4l2_err(pdev->dev.driver, + "Memory allocation failed for ccdc_cfg\n"); + goto probe_disable_clock; + } + + strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, + IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get address base of CCDC */ + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get register address map\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + + ccdc_cfg->ccdc_addr_size = res1->end - res1->start + 1; + if (!request_mem_region(res1->start, ccdc_cfg->ccdc_addr_size, + pdev->dev.driver->name)) { + v4l2_err(pdev->dev.driver, + "Failed request_mem_region for ccdc base\n"); + ret = -ENXIO; + goto probe_disable_clock; + } + ccdc_cfg->ccdc_addr = ioremap_nocache(res1->start, + ccdc_cfg->ccdc_addr_size); + if (!ccdc_cfg->ccdc_addr) { + v4l2_err(pdev->dev.driver, "Unable to ioremap ccdc addr\n"); + ret = -ENXIO; + goto probe_out_release_mem1; + } + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + + if (0 != ret) { + v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); + goto probe_out_unmap1; + } + + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (NULL == vfd) { + ret = -ENOMEM; + v4l2_err(pdev->dev.driver, + "Unable to alloc video device\n"); + goto probe_out_release_irq; + } + + /* Initialize field of video device */ + vfd->release = video_device_release; + vfd->fops = &vpfe_fops; + vfd->ioctl_ops = &vpfe_ioctl_ops; + vfd->minor = -1; + vfd->tvnorms = 0; + vfd->current_norm = V4L2_STD_NTSC; + vfd->v4l2_dev = &vpfe_dev->v4l2_dev; + snprintf(vfd->name, sizeof(vfd->name), + "%s_V%d.%d.%d", + CAPTURE_DRV_NAME, + (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPFE_CAPTURE_VERSION_CODE) & 0xff); + /* Set video_dev to the video device */ + vpfe_dev->video_dev = vfd; + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device.\n"); + goto probe_out_video_release; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + spin_lock_init(&vpfe_dev->irqlock); + spin_lock_init(&vpfe_dev->dma_queue_lock); + mutex_init(&vpfe_dev->lock); + + /* Initialize field of the device objects */ + vpfe_dev->numbuffers = config_params.numbuffers; + + /* Initialize prio member of device object */ + v4l2_prio_init(&vpfe_dev->prio); + /* register video device */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "trying to register vpfe device.\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "video_dev=%x\n", (int)&vpfe_dev->video_dev); + vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = video_register_device(vpfe_dev->video_dev, + VFL_TYPE_GRABBER, -1); + + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register video device.\n"); + goto probe_out_v4l2_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* set driver private data */ + video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); + i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, + GFP_KERNEL); + if (NULL == vpfe_dev->sd) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice pointers\n"); + ret = -ENOMEM; + goto probe_out_video_unregister; + } + + for (i = 0; i < num_subdevs; i++) { + struct v4l2_input *inps; + + sdinfo = &vpfe_cfg->sub_devs[i]; + /** + * register subdevices based on interface setting. Currently + * tvp5146 and mt9t031 cannot co-exists due to i2c address + * conflicts. So only one of them is registered. Re-visit this + * once we have support for i2c switch handling in i2c driver + * framework + */ + if (interface == sdinfo->is_camera) { + /* setup input path */ + if (vpfe_cfg->setup_input) { + if (vpfe_cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_info(&vpfe_dev->v4l2_dev, "could" + " not setup input for %s\n", + sdinfo->module_name); + goto probe_sd_out; + } + } + /* Load up the subdevice */ + vpfe_dev->sd[i] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, + sdinfo->module_name, + &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[i]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->module_name); + vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < sdinfo->num_inputs; j++) { + inps = &sdinfo->inputs[j]; + vfd->tvnorms |= inps->std; + } + sdinfo->registered = 1; + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s register fails\n", + sdinfo->module_name); + } + } + } + + /* We need at least one sub device to do capture */ + for (i = 0; i < num_subdevs; i++) { + sdinfo = &vpfe_cfg->sub_devs[i]; + if (sdinfo->registered) { + /* set this as the current sub device */ + vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[i]; + break; + } + } + + /* if we don't have any sub device registered, return error */ + if (i == num_subdevs) { + printk(KERN_NOTICE "No sub devices registered\n"); + goto probe_sd_out; + } + + /* We have at least one sub device to work with */ + mutex_unlock(&ccdc_lock); + return 0; + +probe_sd_out: + kfree(vpfe_dev->sd); +probe_out_video_unregister: + video_unregister_device(vpfe_dev->video_dev); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_video_release: + if (vpfe_dev->video_dev->minor == -1) + video_device_release(vpfe_dev->video_dev); +probe_out_release_irq: + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); +probe_out_unmap1: + iounmap(ccdc_cfg->ccdc_addr); +probe_out_release_mem1: + release_mem_region(res1->start, res1->end - res1->start + 1); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); + mutex_unlock(&ccdc_lock); + kfree(ccdc_cfg); +probe_free_dev_mem: + kfree(vpfe_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + struct resource *res; + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + kfree(vpfe_dev->sd); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + video_unregister_device(vpfe_dev->video_dev); + mutex_lock(&ccdc_lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + iounmap(ccdc_cfg->ccdc_addr); + mutex_unlock(&ccdc_lock); + vpfe_disable_clock(vpfe_dev); + kfree(vpfe_dev); + kfree(ccdc_cfg); + return 0; +} + +static int vpfe_suspend(struct device *dev) +{ + struct vpfe_device *vpfe_dev = dev_get_drvdata(dev);; + + if (ccdc_dev->hw_ops.save_context) + ccdc_dev->hw_ops.save_context(); + ccdc_dev->hw_ops.enable(0); + + if (vpfe_dev) + vpfe_disable_clock(vpfe_dev); + + return 0; +} + +static int vpfe_resume(struct device *dev) +{ + struct vpfe_device *vpfe_dev = dev_get_drvdata(dev);; + + if (vpfe_dev) + vpfe_enable_clock(vpfe_dev); + + if (ccdc_dev->hw_ops.restore_context) + ccdc_dev->hw_ops.restore_context(); + + return 0; +} + +static struct dev_pm_ops vpfe_dev_pm_ops = { + .suspend = vpfe_suspend, + .resume = vpfe_resume, +}; + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + .pm = &vpfe_dev_pm_ops, + }, + .probe = vpfe_probe, + .remove = __devexit_p(vpfe_remove), +}; + +static __init int vpfe_init(void) +{ + printk(KERN_NOTICE "vpfe_init\n"); + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/** + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/media/video/ti-media/vpif.c b/drivers/media/video/ti-media/vpif.c new file mode 100644 index 00000000000..3b8eac31eca --- /dev/null +++ b/drivers/media/video/ti-media/vpif.c @@ -0,0 +1,296 @@ +/* + * vpif - DM646x Video Port Interface driver + * VPIF is a receiver and transmitter for video data. It has two channels(0, 1) + * that receiveing video byte stream and two channels(2, 3) for video output. + * The hardware supports SDTV, HDTV formats, raw data capture. + * Currently, the driver supports NTSC and PAL standards. + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver"); +MODULE_LICENSE("GPL"); + +#define VPIF_CH0_MAX_MODES (22) +#define VPIF_CH1_MAX_MODES (02) +#define VPIF_CH2_MAX_MODES (15) +#define VPIF_CH3_MAX_MODES (02) + +static resource_size_t res_len; +static struct resource *res; +spinlock_t vpif_lock; + +void __iomem *vpif_base; + +static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) +{ + if (val) + vpif_set_bit(reg, bit); + else + vpif_clr_bit(reg, bit); +} + +/* This structure is used to keep track of VPIF size register's offsets */ +struct vpif_registers { + u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl; + u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt; + u32 vanc1_size, width_mask, len_mask; + u8 max_modes; +}; + +static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = { + /* Channel0 */ + { + VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01, + VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL, + VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, + VPIF_CH0_MAX_MODES, + }, + /* Channel1 */ + { + VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01, + VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL, + VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, + VPIF_CH1_MAX_MODES, + }, + /* Channel2 */ + { + VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01, + VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL, + VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE, + VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF, + VPIF_CH2_MAX_MODES + }, + /* Channel3 */ + { + VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01, + VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL, + VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE, + VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF, + VPIF_CH3_MAX_MODES + }, +}; + +/* vpif_set_mode_info: + * This function is used to set horizontal and vertical config parameters + * As per the standard in the channel, configure the values of L1, L3, + * L5, L7 L9, L11 in VPIF Register , also write width and height + */ +static void vpif_set_mode_info(const struct vpif_channel_config_params *config, + u8 channel_id, u8 config_channel_id) +{ + u32 value; + + value = (config->eav2sav & vpifregs[config_channel_id].width_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->sav2eav & vpifregs[config_channel_id].width_mask); + regw(value, vpifregs[channel_id].h_cfg); + + value = (config->l1 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l3 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_00); + + value = (config->l5 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l7 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_01); + + value = (config->l9 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l11 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_02); + + value = (config->vsize & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg); +} + +/* config_vpif_params + * Function to set the parameters of a channel + * Mainly modifies the channel ciontrol register + * It sets frame format, yc mux mode + */ +static void config_vpif_params(struct vpif_params *vpifparams, + u8 channel_id, u8 found) +{ + const struct vpif_channel_config_params *config = &vpifparams->std_info; + u32 value, ch_nip, reg; + u8 start, end; + int i; + + start = channel_id; + end = channel_id + found; + + for (i = start; i < end; i++) { + reg = vpifregs[i].ch_ctrl; + if (channel_id < 2) + ch_nip = VPIF_CAPTURE_CH_NIP; + else + ch_nip = VPIF_DISPLAY_CH_NIP; + + vpif_wr_bit(reg, ch_nip, config->frm_fmt); + vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode); + vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT, + vpifparams->video_params.storage_mode); + + /* Set raster scanning SDR Format */ + vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT); + vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format); + + if (channel_id > 1) /* Set the Pixel enable bit */ + vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT); + else if (config->capture_format) { + /* Set the polarity of various pins */ + vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT, + vpifparams->iface.fid_pol); + vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT, + vpifparams->iface.vd_pol); + vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT, + vpifparams->iface.hd_pol); + + value = regr(reg); + /* Set data width */ + value &= ((~(unsigned int)(0x3)) << + VPIF_CH_DATA_WIDTH_BIT); + value |= ((vpifparams->params.data_sz) << + VPIF_CH_DATA_WIDTH_BIT); + regw(value, reg); + } + + /* Write the pitch in the driver */ + regw((vpifparams->video_params.hpitch), + vpifregs[i].line_offset); + } +} + +/* vpif_set_video_params + * This function is used to set video parameters in VPIF register + */ +int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id) +{ + const struct vpif_channel_config_params *config = &vpifparams->std_info; + int found = 1; + + vpif_set_mode_info(config, channel_id, channel_id); + if (!config->ycmux_mode) { + /* YC are on separate channels (HDTV formats) */ + vpif_set_mode_info(config, channel_id + 1, channel_id); + found = 2; + } + + config_vpif_params(vpifparams, channel_id, found); + + regw(0x80, VPIF_REQ_SIZE); + regw(0x01, VPIF_EMULATION_CTRL); + + return found; +} +EXPORT_SYMBOL(vpif_set_video_params); + +void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, + u8 channel_id) +{ + u32 value; + + value = 0x3F8 & (vbiparams->hstart0); + value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16); + regw(value, vpifregs[channel_id].vanc0_strt); + + value = 0x3F8 & (vbiparams->hstart1); + value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16); + regw(value, vpifregs[channel_id].vanc1_strt); + + value = 0x3F8 & (vbiparams->hsize0); + value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16); + regw(value, vpifregs[channel_id].vanc0_size); + + value = 0x3F8 & (vbiparams->hsize1); + value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16); + regw(value, vpifregs[channel_id].vanc1_size); + +} +EXPORT_SYMBOL(vpif_set_vbi_display_params); + +int vpif_channel_getfid(u8 channel_id) +{ + return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK) + >> VPIF_CH_FID_SHIFT; +} +EXPORT_SYMBOL(vpif_channel_getfid); + +static int __init vpif_probe(struct platform_device *pdev) +{ + int status = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + res_len = res->end - res->start + 1; + + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + vpif_base = ioremap(res->start, res_len); + if (!vpif_base) { + status = -EBUSY; + goto fail; + } + + spin_lock_init(&vpif_lock); + dev_info(&pdev->dev, "vpif probe success\n"); + return 0; + +fail: + release_mem_region(res->start, res_len); + return status; +} + +static int vpif_remove(struct platform_device *pdev) +{ + iounmap(vpif_base); + release_mem_region(res->start, res_len); + return 0; +} + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(vpif_remove), + .probe = vpif_probe, +}; + +static void vpif_exit(void) +{ + platform_driver_unregister(&vpif_driver); +} + +static int __init vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} +subsys_initcall(vpif_init); +module_exit(vpif_exit); + diff --git a/drivers/media/video/ti-media/vpif.h b/drivers/media/video/ti-media/vpif.h new file mode 100644 index 00000000000..188841b476e --- /dev/null +++ b/drivers/media/video/ti-media/vpif.h @@ -0,0 +1,642 @@ +/* + * VPIF header file + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef VPIF_H +#define VPIF_H + +#include +#include +#include +#include + +/* Maximum channel allowed */ +#define VPIF_NUM_CHANNELS (4) +#define VPIF_CAPTURE_NUM_CHANNELS (2) +#define VPIF_DISPLAY_NUM_CHANNELS (2) + +/* Macros to read/write registers */ +extern void __iomem *vpif_base; +extern spinlock_t vpif_lock; + +#define regr(reg) readl((reg) + vpif_base) +#define regw(value, reg) writel(value, (reg + vpif_base)) + +/* Register Addresss Offsets */ +#define VPIF_PID (0x0000) +#define VPIF_CH0_CTRL (0x0004) +#define VPIF_CH1_CTRL (0x0008) +#define VPIF_CH2_CTRL (0x000C) +#define VPIF_CH3_CTRL (0x0010) + +#define VPIF_INTEN (0x0020) +#define VPIF_INTEN_SET (0x0024) +#define VPIF_INTEN_CLR (0x0028) +#define VPIF_STATUS (0x002C) +#define VPIF_STATUS_CLR (0x0030) +#define VPIF_EMULATION_CTRL (0x0034) +#define VPIF_REQ_SIZE (0x0038) + +#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040) +#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044) +#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048) +#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c) +#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050) +#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054) +#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058) +#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c) +#define VPIF_CH0_SP_CFG (0x0060) +#define VPIF_CH0_IMG_ADD_OFST (0x0064) +#define VPIF_CH0_HANC_ADD_OFST (0x0068) +#define VPIF_CH0_H_CFG (0x006c) +#define VPIF_CH0_V_CFG_00 (0x0070) +#define VPIF_CH0_V_CFG_01 (0x0074) +#define VPIF_CH0_V_CFG_02 (0x0078) +#define VPIF_CH0_V_CFG_03 (0x007c) + +#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080) +#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084) +#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088) +#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c) +#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090) +#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094) +#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098) +#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c) +#define VPIF_CH1_SP_CFG (0x00a0) +#define VPIF_CH1_IMG_ADD_OFST (0x00a4) +#define VPIF_CH1_HANC_ADD_OFST (0x00a8) +#define VPIF_CH1_H_CFG (0x00ac) +#define VPIF_CH1_V_CFG_00 (0x00b0) +#define VPIF_CH1_V_CFG_01 (0x00b4) +#define VPIF_CH1_V_CFG_02 (0x00b8) +#define VPIF_CH1_V_CFG_03 (0x00bc) + +#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0) +#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4) +#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8) +#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc) +#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0) +#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4) +#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8) +#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc) +#define VPIF_CH2_SP_CFG (0x00e0) +#define VPIF_CH2_IMG_ADD_OFST (0x00e4) +#define VPIF_CH2_HANC_ADD_OFST (0x00e8) +#define VPIF_CH2_H_CFG (0x00ec) +#define VPIF_CH2_V_CFG_00 (0x00f0) +#define VPIF_CH2_V_CFG_01 (0x00f4) +#define VPIF_CH2_V_CFG_02 (0x00f8) +#define VPIF_CH2_V_CFG_03 (0x00fc) +#define VPIF_CH2_HANC0_STRT (0x0100) +#define VPIF_CH2_HANC0_SIZE (0x0104) +#define VPIF_CH2_HANC1_STRT (0x0108) +#define VPIF_CH2_HANC1_SIZE (0x010c) +#define VPIF_CH2_VANC0_STRT (0x0110) +#define VPIF_CH2_VANC0_SIZE (0x0114) +#define VPIF_CH2_VANC1_STRT (0x0118) +#define VPIF_CH2_VANC1_SIZE (0x011c) + +#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140) +#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144) +#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148) +#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c) +#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150) +#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154) +#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158) +#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c) +#define VPIF_CH3_SP_CFG (0x0160) +#define VPIF_CH3_IMG_ADD_OFST (0x0164) +#define VPIF_CH3_HANC_ADD_OFST (0x0168) +#define VPIF_CH3_H_CFG (0x016c) +#define VPIF_CH3_V_CFG_00 (0x0170) +#define VPIF_CH3_V_CFG_01 (0x0174) +#define VPIF_CH3_V_CFG_02 (0x0178) +#define VPIF_CH3_V_CFG_03 (0x017c) +#define VPIF_CH3_HANC0_STRT (0x0180) +#define VPIF_CH3_HANC0_SIZE (0x0184) +#define VPIF_CH3_HANC1_STRT (0x0188) +#define VPIF_CH3_HANC1_SIZE (0x018c) +#define VPIF_CH3_VANC0_STRT (0x0190) +#define VPIF_CH3_VANC0_SIZE (0x0194) +#define VPIF_CH3_VANC1_STRT (0x0198) +#define VPIF_CH3_VANC1_SIZE (0x019c) + +#define VPIF_IODFT_CTRL (0x01c0) + +/* Functions for bit Manipulation */ +static inline void vpif_set_bit(u32 reg, u32 bit) +{ + regw((regr(reg)) | (0x01 << bit), reg); +} + +static inline void vpif_clr_bit(u32 reg, u32 bit) +{ + regw(((regr(reg)) & ~(0x01 << bit)), reg); +} + +/* Macro for Generating mask */ +#ifdef GENERATE_MASK +#undef GENERATE_MASK +#endif + +#define GENERATE_MASK(bits, pos) \ + ((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos) + +/* Bit positions in the channel control registers */ +#define VPIF_CH_DATA_MODE_BIT (2) +#define VPIF_CH_YC_MUX_BIT (3) +#define VPIF_CH_SDR_FMT_BIT (4) +#define VPIF_CH_HANC_EN_BIT (8) +#define VPIF_CH_VANC_EN_BIT (9) + +#define VPIF_CAPTURE_CH_NIP (10) +#define VPIF_DISPLAY_CH_NIP (11) + +#define VPIF_DISPLAY_PIX_EN_BIT (10) + +#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12) + +#define VPIF_CH_FID_POLARITY_BIT (15) +#define VPIF_CH_V_VALID_POLARITY_BIT (14) +#define VPIF_CH_H_VALID_POLARITY_BIT (13) +#define VPIF_CH_DATA_WIDTH_BIT (28) + +#define VPIF_CH_CLK_EDGE_CTRL_BIT (31) + +/* Mask various length */ +#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0) +#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0) +#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0) +#define VPIF_CH_LEN_SHIFT (16) + +/* VPIF masks for registers */ +#define VPIF_REQ_SIZE_MASK (0x1ff) + +/* bit posotion of interrupt vpif_ch_intr register */ +#define VPIF_INTEN_FRAME_CH0 (0x00000001) +#define VPIF_INTEN_FRAME_CH1 (0x00000002) +#define VPIF_INTEN_FRAME_CH2 (0x00000004) +#define VPIF_INTEN_FRAME_CH3 (0x00000008) + +/* bit position of clock and channel enable in vpif_chn_ctrl register */ + +#define VPIF_CH0_CLK_EN (0x00000002) +#define VPIF_CH0_EN (0x00000001) +#define VPIF_CH1_CLK_EN (0x00000002) +#define VPIF_CH1_EN (0x00000001) +#define VPIF_CH2_CLK_EN (0x00000002) +#define VPIF_CH2_EN (0x00000001) +#define VPIF_CH3_CLK_EN (0x00000002) +#define VPIF_CH3_EN (0x00000001) +#define VPIF_CH_CLK_EN (0x00000002) +#define VPIF_CH_EN (0x00000001) + +#define VPIF_INT_TOP (0x00) +#define VPIF_INT_BOTTOM (0x01) +#define VPIF_INT_BOTH (0x02) + +#define VPIF_CH0_INT_CTRL_SHIFT (6) +#define VPIF_CH1_INT_CTRL_SHIFT (6) +#define VPIF_CH2_INT_CTRL_SHIFT (6) +#define VPIF_CH3_INT_CTRL_SHIFT (6) +#define VPIF_CH_INT_CTRL_SHIFT (6) + +/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ +#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ +#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ +#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ +#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL)) + +#define VPIF_CH_FID_MASK (0x20) +#define VPIF_CH_FID_SHIFT (5) + +#define VPIF_NTSC_VBI_START_FIELD0 (1) +#define VPIF_NTSC_VBI_START_FIELD1 (263) +#define VPIF_PAL_VBI_START_FIELD0 (624) +#define VPIF_PAL_VBI_START_FIELD1 (311) + +#define VPIF_NTSC_HBI_START_FIELD0 (1) +#define VPIF_NTSC_HBI_START_FIELD1 (263) +#define VPIF_PAL_HBI_START_FIELD0 (624) +#define VPIF_PAL_HBI_START_FIELD1 (311) + +#define VPIF_NTSC_VBI_COUNT_FIELD0 (20) +#define VPIF_NTSC_VBI_COUNT_FIELD1 (19) +#define VPIF_PAL_VBI_COUNT_FIELD0 (24) +#define VPIF_PAL_VBI_COUNT_FIELD1 (25) + +#define VPIF_NTSC_HBI_COUNT_FIELD0 (263) +#define VPIF_NTSC_HBI_COUNT_FIELD1 (262) +#define VPIF_PAL_HBI_COUNT_FIELD0 (312) +#define VPIF_PAL_HBI_COUNT_FIELD1 (313) + +#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720) +#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720) +#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268) +#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280) + +#define VPIF_CH_VANC_EN (0x20) +#define VPIF_DMA_REQ_SIZE (0x080) +#define VPIF_EMULATION_DISABLE (0x01) + +extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS]; + +/* inline function to enable/disable channel0 */ +static inline void enable_channel0(int enable) +{ + if (enable) + regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL); + else + regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL); +} + +/* inline function to enable/disable channel1 */ +static inline void enable_channel1(int enable) +{ + if (enable) + regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL); + else + regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL); +} + +/* inline function to enable interrupt for channel0 */ +static inline void channel0_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to enable interrupt for channel1 */ +static inline void channel1_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to set buffer addresses in case of Y/C non mux mode */ +static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for video data */ +static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA); +} + +static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + + regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); +} + +static inline void ch0_set_vbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC); + regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC); +} + +static inline void ch0_set_hbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC); + regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC); +} + +static inline void ch1_set_vbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC); + regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC); +} + +static inline void ch1_set_hbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC); + regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC); +} + +/* Inline function to enable raw vbi in the given channel */ +static inline void disable_raw_feature(u8 channel_id, u8 index) +{ + u32 ctrl_reg; + if (0 == channel_id) + ctrl_reg = VPIF_CH0_CTRL; + else + ctrl_reg = VPIF_CH1_CTRL; + + if (1 == index) + vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); + else + vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); +} + +static inline void enable_raw_feature(u8 channel_id, u8 index) +{ + u32 ctrl_reg; + if (0 == channel_id) + ctrl_reg = VPIF_CH0_CTRL; + else + ctrl_reg = VPIF_CH1_CTRL; + + if (1 == index) + vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); + else + vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); +} + +/* inline function to enable/disable channel2 */ +static inline void enable_channel2(int enable) +{ + if (enable) { + regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); + regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL); + } else { + regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); + regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL); + } +} + +/* inline function to enable/disable channel3 */ +static inline void enable_channel3(int enable) +{ + if (enable) { + regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); + regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL); + } else { + regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); + regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL); + } +} + +/* inline function to enable interrupt for channel2 */ +static inline void channel2_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to enable interrupt for channel3 */ +static inline void channel3_intr_enable(int enable) +{ + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), + VPIF_INTEN_SET); + } + spin_unlock_irqrestore(&vpif_lock, flags); +} + +/* inline function to enable raw vbi data for channel2 */ +static inline void channel2_raw_enable(int enable, u8 index) +{ + u32 mask; + + if (1 == index) + mask = VPIF_CH_VANC_EN_BIT; + else + mask = VPIF_CH_HANC_EN_BIT; + + if (enable) + vpif_set_bit(VPIF_CH2_CTRL, mask); + else + vpif_clr_bit(VPIF_CH2_CTRL, mask); +} + +/* inline function to enable raw vbi data for channel3*/ +static inline void channel3_raw_enable(int enable, u8 index) +{ + u32 mask; + + if (1 == index) + mask = VPIF_CH_VANC_EN_BIT; + else + mask = VPIF_CH_HANC_EN_BIT; + + if (enable) + vpif_set_bit(VPIF_CH3_CTRL, mask); + else + vpif_clr_bit(VPIF_CH3_CTRL, mask); +} + +/* inline function to set buffer addresses in case of Y/C non mux mode */ +static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for video data */ +static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA); +} + +static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for vbi data */ +static inline void ch2_set_vbi_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC); +} + +static inline void ch3_set_vbi_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC); + regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC); +} + +#define VPIF_MAX_NAME (30) + +/* This structure will store size parameters as per the mode selected by user */ +struct vpif_channel_config_params { + char name[VPIF_MAX_NAME]; /* Name of the mode */ + u16 width; /* Indicates width of the image */ + u16 height; /* Indicates height of the image */ + u8 fps; + u8 frm_fmt; /* Indicates whether this is interlaced + * or progressive format */ + u8 ycmux_mode; /* Indicates whether this mode requires + * single or two channels */ + u16 eav2sav; /* length of sav 2 eav */ + u16 sav2eav; /* length of sav 2 eav */ + u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */ + u16 vsize; /* Vertical size of the image */ + u8 capture_format; /* Indicates whether capture format + * is in BT or in CCD/CMOS */ + u8 vbi_supported; /* Indicates whether this mode + * supports capturing vbi or not */ + u8 hd_sd; + v4l2_std_id stdid; +}; + +struct vpif_video_params; +struct vpif_params; +struct vpif_vbi_params; + +int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id); +void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, + u8 channel_id); +int vpif_channel_getfid(u8 channel_id); + +enum data_size { + _8BITS = 0, + _10BITS, + _12BITS, +}; + +/* Structure for vpif parameters for raw vbi data */ +struct vpif_vbi_params { + __u32 hstart0; /* Horizontal start of raw vbi data for first field */ + __u32 vstart0; /* Vertical start of raw vbi data for first field */ + __u32 hsize0; /* Horizontal size of raw vbi data for first field */ + __u32 vsize0; /* Vertical size of raw vbi data for first field */ + __u32 hstart1; /* Horizontal start of raw vbi data for second field */ + __u32 vstart1; /* Vertical start of raw vbi data for second field */ + __u32 hsize1; /* Horizontal size of raw vbi data for second field */ + __u32 vsize1; /* Vertical size of raw vbi data for second field */ +}; + +/* structure for vpif parameters */ +struct vpif_video_params { + __u8 storage_mode; /* Indicates field or frame mode */ + unsigned long hpitch; + v4l2_std_id stdid; +}; + +struct vpif_params { + struct vpif_interface iface; + struct vpif_video_params video_params; + struct vpif_channel_config_params std_info; + union param { + struct vpif_vbi_params vbi_params; + enum data_size data_sz; + } params; +}; + +#endif /* End of #ifndef VPIF_H */ + diff --git a/drivers/media/video/ti-media/vpif_capture.c b/drivers/media/video/ti-media/vpif_capture.c new file mode 100644 index 00000000000..d947ee5e4eb --- /dev/null +++ b/drivers/media/video/ti-media/vpif_capture.c @@ -0,0 +1,2168 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + * + * TODO : add support for VBI & HBI data service + * add static buffer allocation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vpif_capture.h" +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver"); +MODULE_LICENSE("GPL"); + +#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) +#define vpif_dbg(level, debug, fmt, arg...) \ + v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) + +static int debug = 1; +static u32 ch0_numbuffers = 3; +static u32 ch1_numbuffers = 3; +static u32 ch0_bufsize = 1920 * 1080 * 2; +static u32 ch1_bufsize = 720 * 576 * 2; + +module_param(debug, int, 0644); +module_param(ch0_numbuffers, uint, S_IRUGO); +module_param(ch1_numbuffers, uint, S_IRUGO); +module_param(ch0_bufsize, uint, S_IRUGO); +module_param(ch1_bufsize, uint, S_IRUGO); + +MODULE_PARM_DESC(debug, "Debug level 0-1"); +MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)"); +MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)"); +MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)"); +MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)"); + +static struct vpif_config_params config_params = { + .min_numbuffers = 3, + .numbuffers[0] = 3, + .numbuffers[1] = 3, + .min_bufsize[0] = 720 * 480 * 2, + .min_bufsize[1] = 720 * 480 * 2, + .channel_bufsize[0] = 1920 * 1080 * 2, + .channel_bufsize[1] = 720 * 576 * 2, +}; + +/* global variables */ +static struct vpif_device vpif_obj = { {NULL} }; +static struct device *vpif_dev; + +/** + * ch_params: video standard configuration parameters for vpif + */ +static const struct vpif_channel_config_params ch_params[] = { + { + "NTSC_M", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, + 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, + }, + { + "PAL_BDGHIK", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, + 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, + }, +}; + +/** + * vpif_uservirt_to_phys : translate user/virtual address to phy address + * @virtp: user/virtual address + * + * This inline function is used to convert user space virtual address to + * physical address. + */ +static inline u32 vpif_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 { + vpif_err("get_user_pages failed\n"); + return 0; + } + } + return physp; +} + +/** + * buffer_prepare : callback function for buffer prepare + * @q : buffer queue ptr + * @vb: ptr to video buffer + * @field: field info + * + * This is the callback function for buffer prepare when videobuf_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpif_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + unsigned long addr; + + + vpif_dbg(2, debug, "vpif_buffer_prepare\n"); + + common = &ch->common[VPIF_VIDEO_INDEX]; + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = common->width; + vb->height = common->height; + vb->size = vb->width * vb->height; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + /** + * if user pointer memory mechanism is used, get the physical + * address of the buffer + */ + if (V4L2_MEMORY_USERPTR == common->memory) { + if (0 == vb->baddr) { + vpif_dbg(1, debug, "buffer address is 0\n"); + return -EINVAL; + + } + vb->boff = vpif_uservirt_to_phys(vb->baddr); + if (!IS_ALIGNED(vb->boff, 8)) + goto exit; + } + + addr = vb->boff; + if (q->streaming) { + if (!IS_ALIGNED((addr + common->ytop_off), 8) || + !IS_ALIGNED((addr + common->ybtm_off), 8) || + !IS_ALIGNED((addr + common->ctop_off), 8) || + !IS_ALIGNED((addr + common->cbtm_off), 8)) + goto exit; + } + return 0; +exit: + vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n"); + return -EINVAL; +} + +/** + * vpif_buffer_setup : Callback function for buffer setup. + * @q: buffer queue ptr + * @count: number of buffers + * @size: size of the buffer + * + * This callback function is called when reqbuf() is called to adjust + * the buffer count and buffer size + */ +static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_buffer_setup\n"); + + /* If memory type is not mmap, return */ + if (V4L2_MEMORY_MMAP != common->memory) + return 0; + + /* Calculate the size of the buffer */ + *size = config_params.channel_bufsize[ch->channel_id]; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + return 0; +} + +/** + * vpif_buffer_queue : Callback function to add buffer to DMA queue + * @q: ptr to videobuf_queue + * @vb: ptr to videobuf_buffer + */ +static void vpif_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + list_add_tail(&vb->queue, &common->dma_queue); + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +/** + * vpif_buffer_release : Callback function to free buffer + * @q: buffer queue ptr + * @vb: ptr to video buffer + * + * This function is called from the videobuf layer to free memory + * allocated to the buffers + */ +static void vpif_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and channel object */ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + videobuf_dma_contig_free(q, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpif_buffer_setup, + .buf_prepare = vpif_buffer_prepare, + .buf_queue = vpif_buffer_queue, + .buf_release = vpif_buffer_release, +}; + +static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = + { {1, 1} }; + +/** + * vpif_process_buffer_complete: process a completed buffer + * @common: ptr to common channel object + * + * This function time stamp the buffer and mark it as DONE. It also + * wake up any process waiting on the QUEUE and set the next buffer + * as current + */ +static void vpif_process_buffer_complete(struct common_obj *common) +{ + do_gettimeofday(&common->cur_frm->ts); + common->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&common->cur_frm->done); + /* Make curFrm pointing to nextFrm */ + common->cur_frm = common->next_frm; +} + +/** + * vpif_schedule_next_buffer: set next buffer address for capture + * @common : ptr to common channel object + * + * This function will get next buffer from the dma queue and + * set the buffer address in the vpif register for capture. + * the buffer is marked active + */ +static void vpif_schedule_next_buffer(struct common_obj *common) +{ + unsigned long addr = 0; + + common->next_frm = list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&common->next_frm->queue); + common->next_frm->state = VIDEOBUF_ACTIVE; + if (V4L2_MEMORY_USERPTR == common->memory) + addr = common->next_frm->boff; + else + addr = videobuf_to_dma_contig(common->next_frm); + + /* Set top and bottom field addresses in VPIF registers */ + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); +} + +/** + * vpif_channel_isr : ISR handler for vpif capture + * @irq: irq number + * @dev_id: dev_id ptr + * + * It changes status of the captured buffer, takes next buffer from the queue + * and sets its address in VPIF registers + */ +static irqreturn_t vpif_channel_isr(int irq, void *dev_id) +{ + struct vpif_device *dev = &vpif_obj; + struct common_obj *common; + struct channel_obj *ch; + enum v4l2_field field; + int channel_id = 0; + int fid = -1, i; + + channel_id = *(int *)(dev_id); + ch = dev->dev[channel_id]; + + field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; + + for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) { + common = &ch->common[i]; + /* skip If streaming is not started in this channel */ + if (0 == common->started) + continue; + + /* Check the field format */ + if (1 == ch->vpifparams.std_info.frm_fmt) { + /* Progressive mode */ + if (list_empty(&common->dma_queue)) + continue; + + if (!channel_first_int[i][channel_id]) + vpif_process_buffer_complete(common); + + channel_first_int[i][channel_id] = 0; + + vpif_schedule_next_buffer(common); + + + channel_first_int[i][channel_id] = 0; + } else { + /** + * Interlaced mode. If it is first interrupt, ignore + * it + */ + if (channel_first_int[i][channel_id]) { + channel_first_int[i][channel_id] = 0; + continue; + } + if (0 == i) { + ch->field_id ^= 1; + /* Get field id from VPIF registers */ + fid = vpif_channel_getfid(ch->channel_id); + if (fid != ch->field_id) { + /** + * If field id does not match stored + * field id, make them in sync + */ + if (0 == fid) + ch->field_id = fid; + return IRQ_HANDLED; + } + } + /* device field id and local field id are in sync */ + if (0 == fid) { + /* this is even field */ + if (common->cur_frm == common->next_frm) + continue; + + /* mark the current buffer as done */ + vpif_process_buffer_complete(common); + } else if (1 == fid) { + /* odd field */ + if (list_empty(&common->dma_queue) || + (common->cur_frm != common->next_frm)) + continue; + + vpif_schedule_next_buffer(common); + } + } + } + return IRQ_HANDLED; +} + +/** + * vpif_update_std_info() - update standard related info + * @ch: ptr to channel object + * + * For a given standard selected by application, update values + * in the device data structures + */ +static int vpif_update_std_info(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct vpif_params *vpifparams = &ch->vpifparams; + const struct vpif_channel_config_params *config; + struct vpif_channel_config_params *std_info; + struct video_obj *vid_ch = &ch->video; + int index; + + vpif_dbg(2, debug, "vpif_update_std_info\n"); + + std_info = &vpifparams->std_info; + + for (index = 0; index < ARRAY_SIZE(ch_params); index++) { + config = &ch_params[index]; + if (config->stdid & vid_ch->stdid) { + memcpy(std_info, config, sizeof(*config)); + break; + } + } + + /* standard not found */ + if (index == ARRAY_SIZE(ch_params)) + return -EINVAL; + + common->fmt.fmt.pix.width = std_info->width; + common->width = std_info->width; + common->fmt.fmt.pix.height = std_info->height; + common->height = std_info->height; + common->fmt.fmt.pix.bytesperline = std_info->width; + vpifparams->video_params.hpitch = std_info->width; + vpifparams->video_params.storage_mode = std_info->frm_fmt; + return 0; +} + +/** + * vpif_calculate_offsets : This function calculates buffers offsets + * @ch : ptr to channel object + * + * This function calculates buffer offsets for Y and C in the top and + * bottom field + */ +static void vpif_calculate_offsets(struct channel_obj *ch) +{ + unsigned int hpitch, vpitch, sizeimage; + struct video_obj *vid_ch = &(ch->video); + struct vpif_params *vpifparams = &ch->vpifparams; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + enum v4l2_field field = common->fmt.fmt.pix.field; + + vpif_dbg(2, debug, "vpif_calculate_offsets\n"); + + if (V4L2_FIELD_ANY == field) { + if (vpifparams->std_info.frm_fmt) + vid_ch->buf_field = V4L2_FIELD_NONE; + else + vid_ch->buf_field = V4L2_FIELD_INTERLACED; + } else + vid_ch->buf_field = common->fmt.fmt.pix.field; + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = common->fmt.fmt.pix.sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + hpitch = common->fmt.fmt.pix.bytesperline; + vpitch = sizeimage / (hpitch * 2); + + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ + common->ytop_off = 0; + common->ybtm_off = hpitch; + common->ctop_off = sizeimage / 2; + common->cbtm_off = sizeimage / 2 + hpitch; + } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { + /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ + common->ytop_off = 0; + common->ybtm_off = sizeimage / 4; + common->ctop_off = sizeimage / 2; + common->cbtm_off = common->ctop_off + sizeimage / 4; + } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { + /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ + common->ybtm_off = 0; + common->ytop_off = sizeimage / 4; + common->cbtm_off = sizeimage / 2; + common->ctop_off = common->cbtm_off + sizeimage / 4; + } + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) + vpifparams->video_params.storage_mode = 1; + else + vpifparams->video_params.storage_mode = 0; + + if (1 == vpifparams->std_info.frm_fmt) + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + else { + if ((field == V4L2_FIELD_ANY) + || (field == V4L2_FIELD_INTERLACED)) + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline * 2; + else + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } + + ch->vpifparams.video_params.stdid = vpifparams->std_info.stdid; +} + +/** + * vpif_config_format: configure default frame format in the device + * ch : ptr to channel object + */ +static void vpif_config_format(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_config_format\n"); + + common->fmt.fmt.pix.field = V4L2_FIELD_ANY; + if (config_params.numbuffers[ch->channel_id] == 0) + common->memory = V4L2_MEMORY_USERPTR; + else + common->memory = V4L2_MEMORY_MMAP; + + common->fmt.fmt.pix.sizeimage + = config_params.channel_bufsize[ch->channel_id]; + + if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + else + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +} + +/** + * vpif_get_default_field() - Get default field type based on interface + * @vpif_params - ptr to vpif params + */ +static inline enum v4l2_field vpif_get_default_field( + struct vpif_interface *iface) +{ + return (iface->if_type == VPIF_IF_RAW_BAYER) ? V4L2_FIELD_NONE : + V4L2_FIELD_INTERLACED; +} + +/** + * vpif_check_format() - check given pixel format for compatibility + * @ch - channel ptr + * @pixfmt - Given pixel format + * @update - update the values as per hardware requirement + * + * Check the application pixel format for S_FMT and update the input + * values as per hardware limits for TRY_FMT. The default pixel and + * field format is selected based on interface type. + */ +static int vpif_check_format(struct channel_obj *ch, + struct v4l2_pix_format *pixfmt, + int update) +{ + struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); + struct vpif_params *vpif_params = &ch->vpifparams; + enum v4l2_field field = pixfmt->field; + u32 sizeimage, hpitch, vpitch; + int ret = -EINVAL; + + vpif_dbg(2, debug, "vpif_check_format\n"); + /** + * first check for the pixel format. If if_type is Raw bayer, + * only V4L2_PIX_FMT_SBGGR8 format is supported. Otherwise only + * V4L2_PIX_FMT_YUV422P is supported + */ + if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) { + if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) { + if (!update) { + vpif_dbg(2, debug, "invalid pix format\n"); + goto exit; + } + pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8; + } + } else { + if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) { + if (!update) { + vpif_dbg(2, debug, "invalid pixel format\n"); + goto exit; + } + pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P; + } + } + + if (!(VPIF_VALID_FIELD(field))) { + if (!update) { + vpif_dbg(2, debug, "invalid field format\n"); + goto exit; + } + /** + * By default use FIELD_NONE for RAW Bayer capture + * and FIELD_INTERLACED for other interfaces + */ + field = vpif_get_default_field(&vpif_params->iface); + } else if (field == V4L2_FIELD_ANY) + /* unsupported field. Use default */ + field = vpif_get_default_field(&vpif_params->iface); + + /* validate the hpitch */ + hpitch = pixfmt->bytesperline; + if (hpitch < vpif_params->std_info.width) { + if (!update) { + vpif_dbg(2, debug, "invalid hpitch\n"); + goto exit; + } + hpitch = vpif_params->std_info.width; + } + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = pixfmt->sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + vpitch = sizeimage / (hpitch * 2); + + /* validate the vpitch */ + if (vpitch < vpif_params->std_info.height) { + if (!update) { + vpif_dbg(2, debug, "Invalid vpitch\n"); + goto exit; + } + vpitch = vpif_params->std_info.height; + } + + /* Check for 8 byte alignment */ + if (!ALIGN(hpitch, 8)) { + if (!update) { + vpif_dbg(2, debug, "invalid pitch alignment\n"); + goto exit; + } + /* adjust to next 8 byte boundary */ + hpitch = (((hpitch + 7) / 8) * 8); + } + /* if update is set, modify the bytesperline and sizeimage */ + if (update) { + pixfmt->bytesperline = hpitch; + pixfmt->sizeimage = hpitch * vpitch * 2; + } + /** + * Image width and height is always based on current standard width and + * height + */ + pixfmt->width = common->fmt.fmt.pix.width; + pixfmt->height = common->fmt.fmt.pix.height; + return 0; +exit: + return ret; +} + +/** + * vpif_config_addr() - function to configure buffer address in vpif + * @ch - channel ptr + * @muxmode - channel mux mode + */ +static void vpif_config_addr(struct channel_obj *ch, int muxmode) +{ + struct common_obj *common; + + vpif_dbg(2, debug, "vpif_config_addr\n"); + + common = &(ch->common[VPIF_VIDEO_INDEX]); + + if (VPIF_CHANNEL1_VIDEO == ch->channel_id) + common->set_addr = ch1_set_videobuf_addr; + else if (2 == muxmode) + common->set_addr = ch0_set_videobuf_addr_yc_nmux; + else + common->set_addr = ch0_set_videobuf_addr; +} + +/** + * vpfe_mmap : It is used to map kernel space buffers into user spaces + * @filep: file pointer + * @vma: ptr to vm_area_struct + */ +static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) +{ + /* Get the channel object and file handle object */ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); + + vpif_dbg(2, debug, "vpif_mmap\n"); + + return videobuf_mmap_mapper(&common->buffer_queue, vma); +} + +/** + * vpif_poll: It is used for select/poll system call + * @filep: file pointer + * @wait: poll table to wait + */ +static unsigned int vpif_poll(struct file *filep, poll_table * wait) +{ + int err = 0; + struct vpif_fh *fh = filep->private_data; + struct channel_obj *channel = fh->channel; + struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]); + + vpif_dbg(2, debug, "vpif_poll\n"); + + if (common->started) + err = videobuf_poll_stream(filep, &common->buffer_queue, wait); + + return 0; +} + +/** + * vpif_open : vpif open handler + * @filep: file ptr + * + * It creates object of file handle structure and stores it in private_data + * member of filepointer + */ +static int vpif_open(struct file *filep) +{ + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(filep); + struct common_obj *common; + struct video_obj *vid_ch; + struct channel_obj *ch; + struct vpif_fh *fh; + int i, ret = 0; + + vpif_dbg(2, debug, "vpif_open\n"); + + ch = video_get_drvdata(vdev); + + vid_ch = &ch->video; + common = &ch->common[VPIF_VIDEO_INDEX]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (NULL == ch->curr_subdev_info) { + /** + * search through the sub device to see a registered + * sub device and make it as current sub device + */ + for (i = 0; i < config->subdev_count; i++) { + if (vpif_obj.sd[i]) { + /* the sub device is registered */ + ch->curr_subdev_info = &config->subdev_info[i]; + /* make first input as the current input */ + vid_ch->input_idx = 0; + break; + } + } + if (i == config->subdev_count) { + vpif_err("No sub device registered\n"); + ret = -ENOENT; + goto exit; + } + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL); + if (NULL == fh) { + vpif_err("unable to allocate memory for file handle object\n"); + ret = -ENOMEM; + goto exit; + } + + /* store pointer to fh in private_data member of filep */ + filep->private_data = fh; + fh->channel = ch; + fh->initialized = 0; + /* If decoder is not initialized. initialize it */ + if (!ch->initialized) { + fh->initialized = 1; + ch->initialized = 1; + memset(&(ch->vpifparams), 0, sizeof(struct vpif_params)); + } + /* Increment channel usrs counter */ + ch->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed[VPIF_VIDEO_INDEX] = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&ch->prio, &fh->prio); +exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_release : function to clean up file close + * @filep: file pointer + * + * This function deletes buffer queue, frees the buffers and the vpfe file + * handle + */ +static int vpif_release(struct file *filep) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + + vpif_dbg(2, debug, "vpif_release\n"); + + common = &ch->common[VPIF_VIDEO_INDEX]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* if this instance is doing IO */ + if (fh->io_allowed[VPIF_VIDEO_INDEX]) { + /* Reset io_usrs member of channel object */ + common->io_usrs = 0; + /* Disable channel as per its device type and channel id */ + if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { + enable_channel0(0); + channel0_intr_enable(0); + } + if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel1(0); + channel1_intr_enable(0); + } + common->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&common->buffer_queue); + videobuf_mmap_free(&common->buffer_queue); + } + + /* Decrement channel usrs counter */ + ch->usrs--; + + /* unlock mutex on channel object */ + mutex_unlock(&common->lock); + + /* Close the priority */ + v4l2_prio_close(&ch->prio, &fh->prio); + + if (fh->initialized) + ch->initialized = 0; + + filep->private_data = NULL; + kfree(fh); + return 0; +} + +/** + * vpif_reqbufs() - request buffer handler + * @file: file ptr + * @priv: file handle + * @reqbuf: request buffer structure ptr + */ +static int vpif_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + u8 index = 0; + int ret = 0; + + vpif_dbg(2, debug, "vpif_reqbufs\n"); + + /** + * This file handle has not initialized the channel, + * It is not allowed to do settings + */ + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) + || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type) + return -EINVAL; + + index = VPIF_VIDEO_INDEX; + + common = &ch->common[index]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (0 != common->io_usrs) { + ret = -EBUSY; + goto reqbuf_exit; + } + + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&common->buffer_queue, + &video_qops, NULL, + &common->irqlock, + reqbuf->type, + common->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), fh); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed[index] = 1; + /* Increment io usrs member of channel object to 1 */ + common->io_usrs = 1; + /* Store type of memory requested in channel object */ + common->memory = reqbuf->memory; + INIT_LIST_HEAD(&common->dma_queue); + + /* Allocate buffers */ + ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); + +reqbuf_exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_querybuf() - query buffer handler + * @file: file ptr + * @priv: file handle + * @buf: v4l2 buffer structure ptr + */ +static int vpif_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_querybuf\n"); + + if (common->fmt.type != buf->type) + return -EINVAL; + + if (common->memory != V4L2_MEMORY_MMAP) { + vpif_dbg(1, debug, "Invalid memory\n"); + return -EINVAL; + } + + return videobuf_querybuf(&common->buffer_queue, buf); +} + +/** + * vpif_qbuf() - query buffer handler + * @file: file ptr + * @priv: file handle + * @buf: v4l2 buffer structure ptr + */ +static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_buffer tbuf = *buf; + struct videobuf_buffer *buf1; + unsigned long addr = 0; + unsigned long flags; + int ret = 0; + + vpif_dbg(2, debug, "vpif_qbuf\n"); + + if (common->fmt.type != tbuf.type) { + vpif_err("invalid buffer type\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh io not allowed \n"); + return -EACCES; + } + + if (!(list_empty(&common->dma_queue)) || + (common->cur_frm != common->next_frm) || + !common->started || + (common->started && (0 == ch->field_id))) + return videobuf_qbuf(&common->buffer_queue, buf); + + /* bufferqueue is empty store buffer address in VPIF registers */ + mutex_lock(&common->buffer_queue.vb_lock); + buf1 = common->buffer_queue.bufs[tbuf.index]; + + if ((buf1->state == VIDEOBUF_QUEUED) || + (buf1->state == VIDEOBUF_ACTIVE)) { + vpif_err("invalid state\n"); + goto qbuf_exit; + } + + switch (buf1->memory) { + case V4L2_MEMORY_MMAP: + if (buf1->baddr == 0) + goto qbuf_exit; + break; + + case V4L2_MEMORY_USERPTR: + if (tbuf.length < buf1->bsize) + goto qbuf_exit; + + if ((VIDEOBUF_NEEDS_INIT != buf1->state) + && (buf1->baddr != tbuf.m.userptr)) + vpif_buffer_release(&common->buffer_queue, buf1); + buf1->baddr = tbuf.m.userptr; + break; + + default: + goto qbuf_exit; + } + + local_irq_save(flags); + ret = vpif_buffer_prepare(&common->buffer_queue, buf1, + common->buffer_queue.field); + if (ret < 0) { + local_irq_restore(flags); + goto qbuf_exit; + } + + buf1->state = VIDEOBUF_ACTIVE; + + if (V4L2_MEMORY_USERPTR == common->memory) + addr = buf1->boff; + else + addr = videobuf_to_dma_contig(buf1); + + common->next_frm = buf1; + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); + + local_irq_restore(flags); + list_add_tail(&buf1->stream, &common->buffer_queue.stream); + mutex_unlock(&common->buffer_queue.vb_lock); + return 0; + +qbuf_exit: + mutex_unlock(&common->buffer_queue.vb_lock); + return -EINVAL; +} + +/** + * vpif_dqbuf() - query buffer handler + * @file: file ptr + * @priv: file handle + * @buf: v4l2 buffer structure ptr + */ +static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + vpif_dbg(2, debug, "vpif_dqbuf\n"); + + return videobuf_dqbuf(&common->buffer_queue, buf, + file->f_flags & O_NONBLOCK); +} + +/** + * vpif_streamon() - streamon handler + * @file: file ptr + * @priv: file handle + * @buftype: v4l2 buffer type + */ +static int vpif_streamon(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + + struct vpif_capture_config *config = vpif_dev->platform_data; + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; + struct vpif_params *vpif; + unsigned long addr = 0; + int ret = 0; + + vpif_dbg(2, debug, "vpif_streamon\n"); + + vpif = &ch->vpifparams; + + if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + vpif_dbg(1, debug, "buffer type not supported\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_dbg(1, debug, "io not allowed\n"); + return -EACCES; + } + + /* If Streaming is already started, return error */ + if (common->started) { + vpif_dbg(1, debug, "channel->started\n"); + return -EBUSY; + } + + if ((ch->channel_id == VPIF_CHANNEL0_VIDEO && + oth_ch->common[VPIF_VIDEO_INDEX].started && + vpif->std_info.ycmux_mode == 0) || + ((ch->channel_id == VPIF_CHANNEL1_VIDEO) && + (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { + vpif_dbg(1, debug, "other channel is being used\n"); + return -EBUSY; + } + + ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0); + if (ret) + return ret; + + /* Enable streamon on the sub device */ + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video, + s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + vpif_dbg(1, debug, "stream on failed in subdev\n"); + return ret; + } + + /* Call videobuf_streamon to start streaming in videobuf */ + ret = videobuf_streamon(&common->buffer_queue); + if (ret) { + vpif_dbg(1, debug, "videobuf_streamon\n"); + return ret; + } + + if (mutex_lock_interruptible(&common->lock)) { + ret = -ERESTARTSYS; + goto streamoff_exit; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&common->dma_queue)) { + vpif_dbg(1, debug, "buffer queue is empty\n"); + ret = -EIO; + goto exit; + } + + /* Get the next frame from the buffer queue */ + common->cur_frm = list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + common->next_frm = common->cur_frm; + + /* Remove buffer from the buffer queue */ + list_del(&common->cur_frm->queue); + /* Mark state of the current frame to active */ + common->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + ch->field_id = 0; + common->started = 1; + + if (V4L2_MEMORY_USERPTR == common->memory) + addr = common->cur_frm->boff; + else + addr = videobuf_to_dma_contig(common->cur_frm); + + /* Calculate the offset for Y and C data in the buffer */ + vpif_calculate_offsets(ch); + + if ((vpif->std_info.frm_fmt && + ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) && + (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) || + (!vpif->std_info.frm_fmt && + (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { + vpif_dbg(1, debug, "conflict in field format and std format\n"); + ret = -EINVAL; + goto exit; + } + + /* configure 1 or 2 channel mode */ + ret = config->setup_input_channel_mode(vpif->std_info.ycmux_mode); + + if (ret < 0) { + vpif_dbg(1, debug, "can't set vpif channel mode\n"); + goto exit; + } + + /* Call vpif_set_params function to set the parameters and addresses */ + ret = vpif_set_video_params(vpif, ch->channel_id); + + if (ret < 0) { + vpif_dbg(1, debug, "can't set video params\n"); + goto exit; + } + + common->started = ret; + vpif_config_addr(ch, ret); + + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); + + /** + * Set interrupt for both the fields in VPIF Register enable channel in + * VPIF register + */ + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) { + channel0_intr_assert(); + channel0_intr_enable(1); + enable_channel0(1); + } + if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || + (common->started == 2)) { + channel1_intr_assert(); + channel1_intr_enable(1); + enable_channel1(1); + } + channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; + mutex_unlock(&common->lock); + return ret; + +exit: + mutex_unlock(&common->lock); +streamoff_exit: + ret = videobuf_streamoff(&common->buffer_queue); + return ret; +} + +/** + * vpif_streamoff() - streamoff handler + * @file: file ptr + * @priv: file handle + * @buftype: v4l2 buffer type + */ +static int vpif_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret; + + vpif_dbg(2, debug, "vpif_streamoff\n"); + + if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + vpif_dbg(1, debug, "buffer type not supported\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_dbg(1, debug, "io not allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!common->started) { + vpif_dbg(1, debug, "channel->started\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* disable channel */ + if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { + enable_channel0(0); + channel0_intr_enable(0); + } else { + enable_channel1(0); + channel1_intr_enable(0); + } + + common->started = 0; + + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video, + s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + vpif_dbg(1, debug, "stream off failed in subdev\n"); + + mutex_unlock(&common->lock); + + return videobuf_streamoff(&common->buffer_queue); +} + +/** + * vpif_map_sub_device_to_input() - Maps sub device to input + * @ch - ptr to channel + * @config - ptr to capture configuration + * @input_index - Given input index from application + * @sub_device_index - index into sd table + * + * lookup the sub device information for a given input index. + * we report all the inputs to application. inputs table also + * has sub device name for the each input + */ +static struct vpif_subdev_info *vpif_map_sub_device_to_input( + struct channel_obj *ch, + struct vpif_capture_config *vpif_cfg, + int input_index, + int *sub_device_index) +{ + struct vpif_capture_chan_config *chan_cfg; + struct vpif_subdev_info *subdev_info = NULL; + const char *subdev_name = NULL; + int i; + + vpif_dbg(2, debug, "vpif_map_sub_device_to_input\n"); + + chan_cfg = &vpif_cfg->chan_config[ch->channel_id]; + + /** + * search through the inputs to find the sub device supporting + * the input + */ + for (i = 0; i < chan_cfg->input_count; i++) { + /* For each sub device, loop through input */ + if (i == input_index) { + subdev_name = chan_cfg->inputs[i].subdev_name; + break; + } + } + + /* if reached maximum. return null */ + if (i == chan_cfg->input_count || (NULL == subdev_name)) + return subdev_info; + + /* loop through the sub device list to get the sub device info */ + for (i = 0; i < vpif_cfg->subdev_count; i++) { + subdev_info = &vpif_cfg->subdev_info[i]; + if (!strcmp(subdev_info->name, subdev_name)) + break; + } + + if (i == vpif_cfg->subdev_count) + return subdev_info; + + /* check if the sub device is registered */ + if (NULL == vpif_obj.sd[i]) + return NULL; + + *sub_device_index = i; + return subdev_info; +} + +/** + * vpif_querystd() - querystd handler + * @file: file ptr + * @priv: file handle + * @std_id: ptr to std id + * + * This function is called to detect standard at the selected input + */ +static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + vpif_dbg(2, debug, "vpif_querystd\n"); + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* Call querystd function of decoder device */ + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video, + querystd, std_id); + if (ret < 0) + vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); + + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_g_std() - get STD handler + * @file: file ptr + * @priv: file handle + * @std_id: ptr to std id + */ +static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + vpif_dbg(2, debug, "vpif_g_std\n"); + + *std = ch->video.stdid; + return 0; +} + +/** + * vpif_s_std() - set STD handler + * @file: file ptr + * @priv: file handle + * @std_id: ptr to std id + */ +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + vpif_dbg(2, debug, "vpif_s_std\n"); + + if (common->started) { + vpif_err("streaming in progress\n"); + return -EBUSY; + } + + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || + (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + + fh->initialized = 1; + + /* Call encoder subdevice function to set the standard */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + ch->video.stdid = *std_id; + + /* Get the information about the standard */ + if (vpif_update_std_info(ch)) { + ret = -EINVAL; + vpif_err("Error getting the standard info\n"); + goto s_std_exit; + } + + /* Configure the default format information */ + vpif_config_format(ch); + + /* set standard in the sub device */ + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core, + s_std, *std_id); + if (ret < 0) + vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); + +s_std_exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_enum_input() - ENUMINPUT handler + * @file: file ptr + * @priv: file handle + * @input: ptr to input structure + */ +static int vpif_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + + struct vpif_capture_config *config = vpif_dev->platform_data; + struct vpif_capture_chan_config *chan_cfg; + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + chan_cfg = &config->chan_config[ch->channel_id]; + + if (input->index >= chan_cfg->input_count) { + vpif_dbg(1, debug, "Invalid input index\n"); + return -EINVAL; + } + + memcpy(input, &chan_cfg->inputs[input->index].input, + sizeof(*input)); + return 0; +} + +/** + * vpif_g_input() - Get INPUT handler + * @file: file ptr + * @priv: file handle + * @index: ptr to input index + */ +static int vpif_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + *index = vid_ch->input_idx; + + return 0; +} + +/** + * vpif_s_input() - Set INPUT handler + * @file: file ptr + * @priv: file handle + * @index: input index + */ +static int vpif_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpif_capture_config *config = vpif_dev->platform_data; + struct vpif_capture_chan_config *chan_cfg; + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct video_obj *vid_ch = &ch->video; + struct vpif_subdev_info *subdev_info; + int ret = 0, sd_index = 0; + u32 input = 0, output = 0; + + chan_cfg = &config->chan_config[ch->channel_id]; + + if (common->started) { + vpif_err("Streaming in progress\n"); + return -EBUSY; + } + + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || + (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + + fh->initialized = 1; + subdev_info = vpif_map_sub_device_to_input(ch, config, index, + &sd_index); + if (NULL == subdev_info) { + vpif_dbg(1, debug, + "couldn't lookup sub device for the input index\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* first setup input path from sub device to vpif */ + if (config->setup_input_path) { + ret = config->setup_input_path(ch->channel_id, + subdev_info->name); + if (ret < 0) { + vpif_dbg(1, debug, "couldn't setup input path for the" + " sub device %s, for input index %d\n", + subdev_info->name, index); + goto exit; + } + } + + if (subdev_info->can_route) { + input = subdev_info->input; + output = subdev_info->output; + ret = v4l2_subdev_call(vpif_obj.sd[sd_index], video, s_routing, + input, output, 0); + if (ret < 0) { + vpif_dbg(1, debug, "Failed to set input\n"); + goto exit; + } + } + vid_ch->input_idx = index; + ch->curr_subdev_info = subdev_info; + ch->curr_sd_index = sd_index; + /* copy interface parameters to vpif */ + ch->vpifparams.iface = subdev_info->vpif_if; + + /* update tvnorms from the sub device input info */ + ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std; + +exit: + mutex_unlock(&common->lock); + return ret; +} + +/** + * vpif_enum_fmt_vid_cap() - ENUM_FMT handler + * @file: file ptr + * @priv: file handle + * @index: input index + */ +static int vpif_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + if (fmt->index != 0) { + vpif_dbg(1, debug, "Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) { + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(fmt->description, "Raw Mode -Bayer Pattern GrRBGb"); + fmt->pixelformat = V4L2_PIX_FMT_SBGGR8; + } else { + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); + fmt->pixelformat = V4L2_PIX_FMT_YUV422P; + } + return 0; +} + +/** + * vpif_try_fmt_vid_cap() - TRY_FMT handler + * @file: file ptr + * @priv: file handle + * @fmt: ptr to v4l2 format structure + */ +static int vpif_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + return vpif_check_format(ch, pixfmt, 1); +} + + +/** + * vpif_g_fmt_vid_cap() - Set INPUT handler + * @file: file ptr + * @priv: file handle + * @fmt: ptr to v4l2 format structure + */ +static int vpif_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + /* Check the validity of the buffer type */ + if (common->fmt.type != fmt->type) + return -EINVAL; + + /* Fill in the information about format */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + *fmt = common->fmt; + mutex_unlock(&common->lock); + return 0; +} + +/** + * vpif_s_fmt_vid_cap() - Set FMT handler + * @file: file ptr + * @priv: file handle + * @fmt: ptr to v4l2 format structure + */ +static int vpif_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_pix_format *pixfmt; + int ret = 0; + + vpif_dbg(2, debug, "VIDIOC_S_FMT\n"); + + /* If streaming is started, return error */ + if (common->started) { + vpif_dbg(1, debug, "Streaming is started\n"); + return -EBUSY; + } + + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || + (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + + fh->initialized = 1; + + pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + ret = vpif_check_format(ch, pixfmt, 0); + + if (ret) + return ret; + /* store the format in the channel object */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + common->fmt = *fmt; + mutex_unlock(&common->lock); + + return 0; +} + +/** + * vpif_querycap() - QUERYCAP handler + * @file: file ptr + * @priv: file handle + * @cap: ptr to v4l2_capability structure + */ +static int vpif_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpif_capture_config *config = vpif_dev->platform_data; + + cap->version = VPIF_CAPTURE_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, "vpif capture", sizeof(cap->driver)); + strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info)); + strlcpy(cap->card, config->card_name, sizeof(cap->card)); + + return 0; +} + +/** + * vpif_g_priority() - get priority handler + * @file: file ptr + * @priv: file handle + * @prio: ptr to v4l2_priority structure + */ +static int vpif_g_priority(struct file *file, void *priv, + enum v4l2_priority *prio) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *prio = v4l2_prio_max(&ch->prio); + + return 0; +} + +/** + * vpif_s_priority() - set priority handler + * @file: file ptr + * @priv: file handle + * @prio: ptr to v4l2_priority structure + */ +static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_prio_change(&ch->prio, &fh->prio, p); +} + +/** + * vpif_cropcap() - cropcap handler + * @file: file ptr + * @priv: file handle + * @crop: ptr to v4l2_cropcap structure + */ +static int vpif_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type) + return -EINVAL; + + crop->bounds.left = 0; + crop->bounds.top = 0; + crop->bounds.height = common->height; + crop->bounds.width = common->width; + crop->defrect = crop->bounds; + return 0; +} + +/* vpif capture ioctl operations */ +static const struct v4l2_ioctl_ops vpif_ioctl_ops = { + .vidioc_querycap = vpif_querycap, + .vidioc_g_priority = vpif_g_priority, + .vidioc_s_priority = vpif_s_priority, + .vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap, + .vidioc_enum_input = vpif_enum_input, + .vidioc_s_input = vpif_s_input, + .vidioc_g_input = vpif_g_input, + .vidioc_reqbufs = vpif_reqbufs, + .vidioc_querybuf = vpif_querybuf, + .vidioc_querystd = vpif_querystd, + .vidioc_s_std = vpif_s_std, + .vidioc_g_std = vpif_g_std, + .vidioc_qbuf = vpif_qbuf, + .vidioc_dqbuf = vpif_dqbuf, + .vidioc_streamon = vpif_streamon, + .vidioc_streamoff = vpif_streamoff, + .vidioc_cropcap = vpif_cropcap, +}; + +/* vpif file operations */ +static struct v4l2_file_operations vpif_fops = { + .owner = THIS_MODULE, + .open = vpif_open, + .release = vpif_release, + .ioctl = video_ioctl2, + .mmap = vpif_mmap, + .poll = vpif_poll +}; + +/* vpif video template */ +static struct video_device vpif_video_template = { + .name = "vpif", + .fops = &vpif_fops, + .minor = -1, + .ioctl_ops = &vpif_ioctl_ops, +}; + +/** + * initialize_vpif() - Initialize vpif data structures + * + * Allocate memory for data structures and initialize them + */ +static int initialize_vpif(void) +{ + int err = 0, i, j; + int free_channel_objects_index; + + /* Default number of buffers should be 3 */ + if ((ch0_numbuffers > 0) && + (ch0_numbuffers < config_params.min_numbuffers)) + ch0_numbuffers = config_params.min_numbuffers; + if ((ch1_numbuffers > 0) && + (ch1_numbuffers < config_params.min_numbuffers)) + ch1_numbuffers = config_params.min_numbuffers; + + /* Set buffer size to min buffers size if it is invalid */ + if (ch0_bufsize < config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]) + ch0_bufsize = + config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]; + if (ch1_bufsize < config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]) + ch1_bufsize = + config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]; + + config_params.numbuffers[VPIF_CHANNEL0_VIDEO] = ch0_numbuffers; + config_params.numbuffers[VPIF_CHANNEL1_VIDEO] = ch1_numbuffers; + if (ch0_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL0_VIDEO] + = ch0_bufsize; + } + if (ch1_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL1_VIDEO] + = ch1_bufsize; + } + + /* Allocate memory for six channel objects */ + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + vpif_obj.dev[i] = + kzalloc(sizeof(*vpif_obj.dev[i]), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!vpif_obj.dev[i]) { + free_channel_objects_index = i; + err = -ENOMEM; + goto vpif_init_free_channel_objects; + } + } + return 0; + +vpif_init_free_channel_objects: + for (j = 0; j < free_channel_objects_index; j++) + kfree(vpif_obj.dev[j]); + return err; +} + +/** + * vpif_probe : This function probes the vpif capture driver + * @pdev: platform device pointer + * + * This creates device entries by register itself to the V4L2 driver and + * initializes fields of each channel objects + */ +static __init int vpif_probe(struct platform_device *pdev) +{ + struct vpif_subdev_info *subdevdata; + struct vpif_capture_config *config; + int i, j, k, m, q, err; + struct i2c_adapter *i2c_adap; + struct channel_obj *ch; + struct common_obj *common; + struct video_device *vfd; + struct resource *res; + int subdev_count; + + vpif_dev = &pdev->dev; + + err = initialize_vpif(); + if (err) { + v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); + return err; + } + + k = 0; + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) { + for (i = res->start; i <= res->end; i++) { + if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, + "DM646x_Capture", + (void *)(&vpif_obj.dev[k]->channel_id))) { + err = -EBUSY; + i--; + goto vpif_int_err; + } + } + k++; + } + + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (NULL == vfd) { + for (j = 0; j < i; j++) { + ch = vpif_obj.dev[j]; + video_device_release(ch->video_dev); + } + err = -ENOMEM; + goto vpif_dev_alloc_err; + } + + /* Initialize field of video device */ + *vfd = vpif_video_template; + vfd->v4l2_dev = &vpif_obj.v4l2_dev; + vfd->release = video_device_release; + snprintf(vfd->name, sizeof(vfd->name), + "DM646x_VPIFCapture_DRIVER_V%d.%d.%d", + (VPIF_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPIF_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPIF_CAPTURE_VERSION_CODE) & 0xff); + /* Set video_dev to the video device */ + ch->video_dev = vfd; + } + + for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + ch->channel_id = j; + common = &(ch->common[VPIF_VIDEO_INDEX]); + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 1 : 0)); + if (err) + goto probe_out; + + video_set_drvdata(ch->video_dev, ch); + + } + + i2c_adap = i2c_get_adapter(1); + config = pdev->dev.platform_data; + + subdev_count = config->subdev_count; + vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count, + GFP_KERNEL); + if (vpif_obj.sd == NULL) { + vpif_err("unable to allocate memory for subdevice pointers\n"); + err = -ENOMEM; + goto probe_out; + } + + err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); + if (err) { + v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); + goto probe_subdev_out; + } + + for (i = 0; i < subdev_count; i++) { + subdevdata = &config->subdev_info[i]; + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + subdevdata->name, + &subdevdata->board_info, + NULL); + + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n", + subdevdata->name); + + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; + } + v4l2_info(&vpif_obj.v4l2_dev, "DM646x VPIF Capture driver" + " initialized\n"); + + return 0; + +probe_subdev_out: + /* free sub devices memory */ + kfree(vpif_obj.sd); + + j = VPIF_CAPTURE_MAX_DEVICES; +probe_out: + v4l2_device_unregister(&vpif_obj.v4l2_dev); + for (k = 0; k < j; k++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[k]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + } + +vpif_dev_alloc_err: + k = VPIF_CAPTURE_MAX_DEVICES-1; + res = platform_get_resource(pdev, IORESOURCE_IRQ, k); + i = res->end; + +vpif_int_err: + for (q = k; q >= 0; q--) { + for (m = i; m >= (int)res->start; m--) + free_irq(m, (void *)(&vpif_obj.dev[q]->channel_id)); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, q-1); + if (res) + i = res->end; + } + return err; +} + +/** + * vpif_remove() - driver remove handler + * @device: ptr to platform device structure + * + * The vidoe device is unregistered + */ +static int vpif_remove(struct platform_device *device) +{ + int i; + struct channel_obj *ch; + + v4l2_device_unregister(&vpif_obj.v4l2_dev); + + /* un-register device */ + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + } + return 0; +} + +/** + * vpif_suspend: vpif device suspend + * + * TODO: Add suspend code here + */ +static int +vpif_suspend(struct device *dev) +{ + return -1; +} + +/** + * vpif_resume: vpif device suspend + * + * TODO: Add resume code here + */ +static int +vpif_resume(struct device *dev) +{ + return -1; +} + +static struct dev_pm_ops vpif_dev_pm_ops = { + .suspend = vpif_suspend, + .resume = vpif_resume, +}; + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif_capture", + .owner = THIS_MODULE, + .pm = &vpif_dev_pm_ops, + }, + .probe = vpif_probe, + .remove = vpif_remove, +}; + +/** + * vpif_init: initialize the vpif driver + * + * This function registers device and driver to the kernel, requests irq + * handler and allocates memory + * for channel objects + */ +static __init int vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} + +/** + * vpif_cleanup : This function clean up the vpif capture resources + * + * This will un-registers device and driver to the kernel, frees + * requested irq handler and de-allocates memory allocated for channel + * objects. + */ +static void vpif_cleanup(void) +{ + struct platform_device *pdev; + struct resource *res; + int irq_num; + int i = 0; + + pdev = container_of(vpif_dev, struct platform_device, dev); + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) + free_irq(irq_num, + (void *)(&vpif_obj.dev[i]->channel_id)); + i++; + } + + platform_driver_unregister(&vpif_driver); + + kfree(vpif_obj.sd); + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + +/* Function for module initialization and cleanup */ +module_init(vpif_init); +module_exit(vpif_cleanup); diff --git a/drivers/media/video/ti-media/vpif_capture.h b/drivers/media/video/ti-media/vpif_capture.h new file mode 100644 index 00000000000..4e12ec8cac6 --- /dev/null +++ b/drivers/media/video/ti-media/vpif_capture.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef VPIF_CAPTURE_H +#define VPIF_CAPTURE_H + +#ifdef __KERNEL__ + +/* Header files */ +#include +#include +#include +#include +#include +#include +#include + +#include "vpif.h" + +/* Macros */ +#define VPIF_MAJOR_RELEASE 0 +#define VPIF_MINOR_RELEASE 0 +#define VPIF_BUILD 1 +#define VPIF_CAPTURE_VERSION_CODE ((VPIF_MAJOR_RELEASE << 16) | \ + (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD) + +#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \ + (V4L2_FIELD_NONE == field)) || \ + (((V4L2_FIELD_INTERLACED == field) || \ + (V4L2_FIELD_SEQ_TB == field)) || \ + (V4L2_FIELD_SEQ_BT == field))) + +#define VPIF_CAPTURE_MAX_DEVICES 2 +#define VPIF_VIDEO_INDEX 0 +#define VPIF_NUMBER_OF_OBJECTS 1 + +/* Enumerated data type to give id to each device per channel */ +enum vpif_channel_id { + VPIF_CHANNEL0_VIDEO = 0, + VPIF_CHANNEL1_VIDEO, +}; + +struct video_obj { + enum v4l2_field buf_field; + /* Currently selected or default standard */ + v4l2_std_id stdid; + /* This is to track the last input that is passed to application */ + u32 input_idx; +}; + +struct common_obj { + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* Buffer queue used in video-buf */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Indicates whether streaming started */ + u8 started; + /* Function pointer to set the addresses */ + void (*set_addr) (unsigned long, unsigned long, unsigned long, + unsigned long); + /* offset where Y top starts from the starting of the buffer */ + u32 ytop_off; + /* offset where Y bottom starts from the starting of the buffer */ + u32 ybtm_off; + /* offset where C top starts from the starting of the buffer */ + u32 ctop_off; + /* offset where C bottom starts from the starting of the buffer */ + u32 cbtm_off; + /* Indicates width of the image data */ + u32 width; + /* Indicates height of the image data */ + u32 height; +}; + +struct channel_obj { + /* Identifies video device for this channel */ + struct video_device *video_dev; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + int usrs; + /* Indicates id of the field which is being displayed */ + u32 field_id; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* Identifies channel */ + enum vpif_channel_id channel_id; + /* index into sd table */ + int curr_sd_index; + /* ptr to current sub device information */ + struct vpif_subdev_info *curr_subdev_info; + /* vpif configuration params */ + struct vpif_params vpifparams; + /* common object array */ + struct common_obj common[VPIF_NUMBER_OF_OBJECTS]; + /* video object */ + struct video_obj video; +}; + +/* File handle structure */ +struct vpif_fh { + /* pointer to channel object for opened device */ + struct channel_obj *channel; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed[VPIF_NUMBER_OF_OBJECTS]; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; + /* Used to indicate channel is initialize or not */ + u8 initialized; +}; + +struct vpif_device { + struct v4l2_device v4l2_dev; + struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS]; + struct v4l2_subdev **sd; +}; + +struct vpif_config_params { + u8 min_numbuffers; + u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS]; + s8 device_type; + u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; + u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; + u8 default_device[VPIF_CAPTURE_NUM_CHANNELS]; + u8 max_device_type; +}; +/* Struct which keeps track of the line numbers for the sliced vbi service */ +struct vpif_service_line { + u16 service_id; + u16 service_line[2]; +}; +#endif /* End of __KERNEL__ */ +#endif /* VPIF_CAPTURE_H */ diff --git a/drivers/media/video/ti-media/vpif_display.c b/drivers/media/video/ti-media/vpif_display.c new file mode 100644 index 00000000000..c015da813dd --- /dev/null +++ b/drivers/media/video/ti-media/vpif_display.c @@ -0,0 +1,1656 @@ +/* + * vpif-display - VPIF display driver + * Display driver for TI DaVinci VPIF + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "vpif_display.h" +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci VPIF Display driver"); +MODULE_LICENSE("GPL"); + +#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50) + +#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) +#define vpif_dbg(level, debug, fmt, arg...) \ + v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) + +static int debug = 1; +static u32 ch2_numbuffers = 3; +static u32 ch3_numbuffers = 3; +static u32 ch2_bufsize = 1920 * 1080 * 2; +static u32 ch3_bufsize = 720 * 576 * 2; + +module_param(debug, int, 0644); +module_param(ch2_numbuffers, uint, S_IRUGO); +module_param(ch3_numbuffers, uint, S_IRUGO); +module_param(ch2_bufsize, uint, S_IRUGO); +module_param(ch3_bufsize, uint, S_IRUGO); + +MODULE_PARM_DESC(debug, "Debug level 0-1"); +MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)"); +MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)"); +MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)"); +MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)"); + +static struct vpif_config_params config_params = { + .min_numbuffers = 3, + .numbuffers[0] = 3, + .numbuffers[1] = 3, + .min_bufsize[0] = 720 * 480 * 2, + .min_bufsize[1] = 720 * 480 * 2, + .channel_bufsize[0] = 1920 * 1080 * 2, + .channel_bufsize[1] = 720 * 576 * 2, +}; + +static struct vpif_device vpif_obj = { {NULL} }; +static struct device *vpif_dev; + +static const struct vpif_channel_config_params ch_params[] = { + { + "NTSC", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, + 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, + }, + { + "PAL", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, + 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, + }, +}; + +/* + * vpif_uservirt_to_phys: This function is used to convert user + * space virtual address to physical address. + */ +static u32 vpif_uservirt_to_phys(u32 virtp) +{ + struct mm_struct *mm = current->mm; + unsigned long physp = 0; + 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 addr */ + 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 { + vpif_err("get_user_pages failed\n"); + return 0; + } + } + + return physp; +} + +/* + * buffer_prepare: This is the callback function called from videobuf_qbuf() + * function the buffer is prepared and user space virtual address is converted + * into physical address + */ +static int vpif_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpif_fh *fh = q->priv_data; + struct common_obj *common; + unsigned long addr; + + common = &fh->channel->common[VPIF_VIDEO_INDEX]; + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = common->width; + vb->height = common->height; + vb->size = vb->width * vb->height; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + + /* if user pointer memory mechanism is used, get the physical + * address of the buffer */ + if (V4L2_MEMORY_USERPTR == common->memory) { + if (!vb->baddr) { + vpif_err("buffer_address is 0\n"); + return -EINVAL; + } + + vb->boff = vpif_uservirt_to_phys(vb->baddr); + if (!ISALIGNED(vb->boff)) + goto buf_align_exit; + } + + addr = vb->boff; + if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) { + if (!ISALIGNED(addr + common->ytop_off) || + !ISALIGNED(addr + common->ybtm_off) || + !ISALIGNED(addr + common->ctop_off) || + !ISALIGNED(addr + common->cbtm_off)) + goto buf_align_exit; + } + return 0; + +buf_align_exit: + vpif_err("buffer offset not aligned to 8 bytes\n"); + return -EINVAL; +} + +/* + * vpif_buffer_setup: This function allocates memory for the buffers + */ +static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (V4L2_MEMORY_MMAP != common->memory) + return 0; + + *size = config_params.channel_bufsize[ch->channel_id]; + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + + return 0; +} + +/* + * vpif_buffer_queue: This function adds the buffer to DMA queue + */ +static void vpif_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct vpif_fh *fh = q->priv_data; + struct common_obj *common; + + common = &fh->channel->common[VPIF_VIDEO_INDEX]; + + /* add the buffer to the DMA queue */ + list_add_tail(&vb->queue, &common->dma_queue); + vb->state = VIDEOBUF_QUEUED; +} + +/* + * vpif_buffer_release: This function is called from the videobuf layer to + * free memory allocated to the buffers + */ +static void vpif_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + unsigned int buf_size = 0; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + videobuf_dma_contig_free(q, vb); + vb->state = VIDEOBUF_NEEDS_INIT; + + if (V4L2_MEMORY_MMAP != common->memory) + return; + + buf_size = config_params.channel_bufsize[ch->channel_id]; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpif_buffer_setup, + .buf_prepare = vpif_buffer_prepare, + .buf_queue = vpif_buffer_queue, + .buf_release = vpif_buffer_release, +}; +static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; + +static void process_progressive_mode(struct common_obj *common) +{ + unsigned long addr = 0; + + /* Get the next buffer from buffer queue */ + common->next_frm = list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&common->next_frm->queue); + /* Mark status of the buffer as active */ + common->next_frm->state = VIDEOBUF_ACTIVE; + + /* Set top and bottom field addrs in VPIF registers */ + addr = videobuf_to_dma_contig(common->next_frm); + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); +} + +static void process_interlaced_mode(int fid, struct common_obj *common) +{ + /* device field id and local field id are in sync */ + /* If this is even field */ + if (0 == fid) { + if (common->cur_frm == common->next_frm) + return; + + /* one frame is displayed If next frame is + * available, release cur_frm and move on */ + /* Copy frame display time */ + do_gettimeofday(&common->cur_frm->ts); + /* Change status of the cur_frm */ + common->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&common->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + common->cur_frm = common->next_frm; + + } else if (1 == fid) { /* odd field */ + if (list_empty(&common->dma_queue) + || (common->cur_frm != common->next_frm)) { + return; + } + /* one field is displayed configure the next + * frame if it is available else hold on current + * frame */ + /* Get next from the buffer queue */ + process_progressive_mode(common); + + } +} + +/* + * vpif_channel_isr: It changes status of the displayed buffer, takes next + * buffer from the queue and sets its address in VPIF registers + */ +static irqreturn_t vpif_channel_isr(int irq, void *dev_id) +{ + struct vpif_device *dev = &vpif_obj; + struct channel_obj *ch; + struct common_obj *common; + enum v4l2_field field; + int fid = -1, i; + int channel_id = 0; + + channel_id = *(int *)(dev_id); + ch = dev->dev[channel_id]; + field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; + for (i = 0; i < VPIF_NUMOBJECTS; i++) { + common = &ch->common[i]; + /* If streaming is started in this channel */ + if (0 == common->started) + continue; + + if (1 == ch->vpifparams.std_info.frm_fmt) { + if (list_empty(&common->dma_queue)) + continue; + + /* Progressive mode */ + if (!channel_first_int[i][channel_id]) { + /* Mark status of the cur_frm to + * done and unlock semaphore on it */ + do_gettimeofday(&common->cur_frm->ts); + common->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&common->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + common->cur_frm = common->next_frm; + } + + channel_first_int[i][channel_id] = 0; + process_progressive_mode(common); + } else { + /* Interlaced mode */ + /* If it is first interrupt, ignore it */ + + if (channel_first_int[i][channel_id]) { + channel_first_int[i][channel_id] = 0; + continue; + } + + if (0 == i) { + ch->field_id ^= 1; + /* Get field id from VPIF registers */ + fid = vpif_channel_getfid(ch->channel_id + 2); + /* If fid does not match with stored field id */ + if (fid != ch->field_id) { + /* Make them in sync */ + if (0 == fid) + ch->field_id = fid; + + return IRQ_HANDLED; + } + } + process_interlaced_mode(fid, common); + } + } + + return IRQ_HANDLED; +} + +static int vpif_get_std_info(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct video_obj *vid_ch = &ch->video; + struct vpif_params *vpifparams = &ch->vpifparams; + struct vpif_channel_config_params *std_info = &vpifparams->std_info; + const struct vpif_channel_config_params *config; + + int index; + + std_info->stdid = vid_ch->stdid; + if (!std_info) + return -1; + + for (index = 0; index < ARRAY_SIZE(ch_params); index++) { + config = &ch_params[index]; + if (config->stdid & std_info->stdid) { + memcpy(std_info, config, sizeof(*config)); + break; + } + } + + if (index == ARRAY_SIZE(ch_params)) + return -1; + + common->fmt.fmt.pix.width = std_info->width; + common->fmt.fmt.pix.height = std_info->height; + vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", + common->fmt.fmt.pix.width, common->fmt.fmt.pix.height); + + /* Set height and width paramateres */ + ch->common[VPIF_VIDEO_INDEX].height = std_info->height; + ch->common[VPIF_VIDEO_INDEX].width = std_info->width; + + return 0; +} + +/* + * vpif_calculate_offsets: This function calculates buffers offset for Y and C + * in the top and bottom field + */ +static void vpif_calculate_offsets(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct vpif_params *vpifparams = &ch->vpifparams; + enum v4l2_field field = common->fmt.fmt.pix.field; + struct video_obj *vid_ch = &ch->video; + unsigned int hpitch, vpitch, sizeimage; + + if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) { + if (ch->vpifparams.std_info.frm_fmt) + vid_ch->buf_field = V4L2_FIELD_NONE; + else + vid_ch->buf_field = V4L2_FIELD_INTERLACED; + } else { + vid_ch->buf_field = common->fmt.fmt.pix.field; + } + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = common->fmt.fmt.pix.sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + hpitch = common->fmt.fmt.pix.bytesperline; + vpitch = sizeimage / (hpitch * 2); + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + common->ytop_off = 0; + common->ybtm_off = hpitch; + common->ctop_off = sizeimage / 2; + common->cbtm_off = sizeimage / 2 + hpitch; + } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { + common->ytop_off = 0; + common->ybtm_off = sizeimage / 4; + common->ctop_off = sizeimage / 2; + common->cbtm_off = common->ctop_off + sizeimage / 4; + } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { + common->ybtm_off = 0; + common->ytop_off = sizeimage / 4; + common->cbtm_off = sizeimage / 2; + common->ctop_off = common->cbtm_off + sizeimage / 4; + } + + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + vpifparams->video_params.storage_mode = 1; + } else { + vpifparams->video_params.storage_mode = 0; + } + + if (ch->vpifparams.std_info.frm_fmt == 1) { + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } else { + if ((field == V4L2_FIELD_ANY) || + (field == V4L2_FIELD_INTERLACED)) + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline * 2; + else + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } + + ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid; +} + +static void vpif_config_format(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + common->fmt.fmt.pix.field = V4L2_FIELD_ANY; + if (config_params.numbuffers[ch->channel_id] == 0) + common->memory = V4L2_MEMORY_USERPTR; + else + common->memory = V4L2_MEMORY_MMAP; + + common->fmt.fmt.pix.sizeimage = + config_params.channel_bufsize[ch->channel_id]; + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; +} + +static int vpif_check_format(struct channel_obj *ch, + struct v4l2_pix_format *pixfmt) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + enum v4l2_field field = pixfmt->field; + u32 sizeimage, hpitch, vpitch; + + if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) + goto invalid_fmt_exit; + + if (!(VPIF_VALID_FIELD(field))) + goto invalid_fmt_exit; + + if (pixfmt->bytesperline <= 0) + goto invalid_pitch_exit; + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = pixfmt->sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + hpitch = pixfmt->bytesperline; + vpitch = sizeimage / (hpitch * 2); + + /* Check for valid value of pitch */ + if ((hpitch < ch->vpifparams.std_info.width) || + (vpitch < ch->vpifparams.std_info.height)) + goto invalid_pitch_exit; + + /* Check for 8 byte alignment */ + if (!ISALIGNED(hpitch)) { + vpif_err("invalid pitch alignment\n"); + return -EINVAL; + } + pixfmt->width = common->fmt.fmt.pix.width; + pixfmt->height = common->fmt.fmt.pix.height; + + return 0; + +invalid_fmt_exit: + vpif_err("invalid field format\n"); + return -EINVAL; + +invalid_pitch_exit: + vpif_err("invalid pitch\n"); + return -EINVAL; +} + +static void vpif_config_addr(struct channel_obj *ch, int muxmode) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (VPIF_CHANNEL3_VIDEO == ch->channel_id) { + common->set_addr = ch3_set_videobuf_addr; + } else { + if (2 == muxmode) + common->set_addr = ch2_set_videobuf_addr_yc_nmux; + else + common->set_addr = ch2_set_videobuf_addr; + } +} + +/* + * vpif_mmap: It is used to map kernel space buffers into user spaces + */ +static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct vpif_fh *fh = filep->private_data; + struct common_obj *common = &fh->channel->common[VPIF_VIDEO_INDEX]; + + return videobuf_mmap_mapper(&common->buffer_queue, vma); +} + +/* + * vpif_poll: It is used for select/poll system call + */ +static unsigned int vpif_poll(struct file *filep, poll_table *wait) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (common->started) + return videobuf_poll_stream(filep, &common->buffer_queue, wait); + + return 0; +} + +/* + * vpif_open: It creates object of file handle structure and stores it in + * private_data member of filepointer + */ +static int vpif_open(struct file *filep) +{ + struct video_device *vdev = video_devdata(filep); + struct channel_obj *ch = NULL; + struct vpif_fh *fh = NULL; + + ch = video_get_drvdata(vdev); + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL); + if (fh == NULL) { + vpif_err("unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + + /* store pointer to fh in private_data member of filep */ + filep->private_data = fh; + fh->channel = ch; + fh->initialized = 0; + if (!ch->initialized) { + fh->initialized = 1; + ch->initialized = 1; + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + } + + /* Increment channel usrs counter */ + atomic_inc(&ch->usrs); + /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */ + fh->io_allowed[VPIF_VIDEO_INDEX] = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&ch->prio, &fh->prio); + + return 0; +} + +/* + * vpif_release: This function deletes buffer queue, frees the buffers and + * the vpif file handle + */ +static int vpif_release(struct file *filep) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* if this instance is doing IO */ + if (fh->io_allowed[VPIF_VIDEO_INDEX]) { + /* Reset io_usrs member of channel object */ + common->io_usrs = 0; + /* Disable channel */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + enable_channel2(0); + channel2_intr_enable(0); + } + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel3(0); + channel3_intr_enable(0); + } + common->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&common->buffer_queue); + videobuf_mmap_free(&common->buffer_queue); + common->numbuffers = + config_params.numbuffers[ch->channel_id]; + } + + mutex_unlock(&common->lock); + + /* Decrement channel usrs counter */ + atomic_dec(&ch->usrs); + /* If this file handle has initialize encoder device, reset it */ + if (fh->initialized) + ch->initialized = 0; + + /* Close the priority */ + v4l2_prio_close(&ch->prio, &fh->prio); + filep->private_data = NULL; + fh->initialized = 0; + kfree(fh); + + return 0; +} + +/* functions implementing ioctls */ + +static int vpif_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpif_display_config *config = vpif_dev->platform_data; + + cap->version = VPIF_DISPLAY_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, "vpif display", sizeof(cap->driver)); + strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info)); + strlcpy(cap->card, config->card_name, sizeof(cap->card)); + + return 0; +} + +static int vpif_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index != 0) { + vpif_err("Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); + fmt->pixelformat = V4L2_PIX_FMT_YUV422P; + + return 0; +} + +static int vpif_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + /* Check the validity of the buffer type */ + if (common->fmt.type != fmt->type) + return -EINVAL; + + /* Fill in the information about format */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + *fmt = common->fmt; + mutex_unlock(&common->lock); + return 0; +} + +static int vpif_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct v4l2_pix_format *pixfmt; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) + || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + + /* Check for the priority */ + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + fh->initialized = 1; + } + + if (common->started) { + vpif_dbg(1, debug, "Streaming in progress\n"); + return -EBUSY; + } + + pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + ret = vpif_check_format(ch, pixfmt); + if (ret) + return ret; + + /* store the pix format in the channel object */ + common->fmt.fmt.pix = *pixfmt; + /* store the format in the channel object */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + common->fmt = *fmt; + mutex_unlock(&common->lock); + + return 0; +} + +static int vpif_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + int ret = 0; + + ret = vpif_check_format(ch, pixfmt); + if (ret) { + *pixfmt = common->fmt.fmt.pix; + pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2; + } + + return ret; +} + +static int vpif_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + enum v4l2_field field; + u8 index = 0; + int ret = 0; + + /* This file handle has not initialized the channel, + It is not allowed to do settings */ + if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) + || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_err("Channel Busy\n"); + return -EBUSY; + } + } + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type) + return -EINVAL; + + index = VPIF_VIDEO_INDEX; + + common = &ch->common[index]; + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (common->fmt.type != reqbuf->type) { + ret = -EINVAL; + goto reqbuf_exit; + } + + if (0 != common->io_usrs) { + ret = -EBUSY; + goto reqbuf_exit; + } + + if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) + field = V4L2_FIELD_INTERLACED; + else + field = common->fmt.fmt.pix.field; + } else { + field = V4L2_VBI_INTERLACED; + } + + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&common->buffer_queue, + &video_qops, NULL, + &common->irqlock, + reqbuf->type, field, + sizeof(struct videobuf_buffer), fh); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed[index] = 1; + /* Increment io usrs member of channel object to 1 */ + common->io_usrs = 1; + /* Store type of memory requested in channel object */ + common->memory = reqbuf->memory; + INIT_LIST_HEAD(&common->dma_queue); + + /* Allocate buffers */ + ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); + +reqbuf_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_querybuf(struct file *file, void *priv, + struct v4l2_buffer *tbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (common->fmt.type != tbuf->type) + return -EINVAL; + + return videobuf_querybuf(&common->buffer_queue, tbuf); +} + +static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_buffer tbuf = *buf; + struct videobuf_buffer *buf1; + unsigned long addr = 0; + unsigned long flags; + int ret = 0; + + if (common->fmt.type != tbuf.type) + return -EINVAL; + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + if (!(list_empty(&common->dma_queue)) || + (common->cur_frm != common->next_frm) || + !(common->started) || + (common->started && (0 == ch->field_id))) + return videobuf_qbuf(&common->buffer_queue, buf); + + /* bufferqueue is empty store buffer address in VPIF registers */ + mutex_lock(&common->buffer_queue.vb_lock); + buf1 = common->buffer_queue.bufs[tbuf.index]; + if (buf1->memory != tbuf.memory) { + vpif_err("invalid buffer type\n"); + goto qbuf_exit; + } + + if ((buf1->state == VIDEOBUF_QUEUED) || + (buf1->state == VIDEOBUF_ACTIVE)) { + vpif_err("invalid state\n"); + goto qbuf_exit; + } + + switch (buf1->memory) { + case V4L2_MEMORY_MMAP: + if (buf1->baddr == 0) + goto qbuf_exit; + break; + + case V4L2_MEMORY_USERPTR: + if (tbuf.length < buf1->bsize) + goto qbuf_exit; + + if ((VIDEOBUF_NEEDS_INIT != buf1->state) + && (buf1->baddr != tbuf.m.userptr)) + vpif_buffer_release(&common->buffer_queue, buf1); + buf1->baddr = tbuf.m.userptr; + break; + + default: + goto qbuf_exit; + } + + local_irq_save(flags); + ret = vpif_buffer_prepare(&common->buffer_queue, buf1, + common->buffer_queue.field); + if (ret < 0) { + local_irq_restore(flags); + goto qbuf_exit; + } + + buf1->state = VIDEOBUF_ACTIVE; + addr = buf1->boff; + common->next_frm = buf1; + if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + common->set_addr((addr + common->ytop_off), + (addr + common->ybtm_off), + (addr + common->ctop_off), + (addr + common->cbtm_off)); + } + + local_irq_restore(flags); + list_add_tail(&buf1->stream, &common->buffer_queue.stream); + mutex_unlock(&common->buffer_queue.vb_lock); + return 0; + +qbuf_exit: + mutex_unlock(&common->buffer_queue.vb_lock); + return -EINVAL; +} + +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if (!(*std_id & DM646X_V4L2_STD)) + return -EINVAL; + + if (common->started) { + vpif_err("streaming in progress\n"); + return -EBUSY; + } + + /* Call encoder subdevice function to set the standard */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + ch->video.stdid = *std_id; + /* Get the information about the standard */ + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + if ((ch->vpifparams.std_info.width * + ch->vpifparams.std_info.height * 2) > + config_params.channel_bufsize[ch->channel_id]) { + vpif_err("invalid std for this size\n"); + ret = -EINVAL; + goto s_std_exit; + } + + common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; + /* Configure the default format information */ + vpif_config_format(ch); + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, + s_std_output, *std_id); + if (ret < 0) { + vpif_err("Failed to set output standard\n"); + goto s_std_exit; + } + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, + s_std, *std_id); + if (ret < 0) + vpif_err("Failed to set standard for sub devices\n"); + +s_std_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *std = ch->video.stdid; + return 0; +} + +static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + return videobuf_dqbuf(&common->buffer_queue, p, + (file->f_flags & O_NONBLOCK)); +} + +static int vpif_streamon(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; + struct vpif_params *vpif = &ch->vpifparams; + struct vpif_display_config *vpif_config_data = + vpif_dev->platform_data; + unsigned long addr = 0; + int ret = 0; + + if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + vpif_err("buffer type not supported\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + /* If Streaming is already started, return error */ + if (common->started) { + vpif_err("channel->started\n"); + return -EBUSY; + } + + if ((ch->channel_id == VPIF_CHANNEL2_VIDEO + && oth_ch->common[VPIF_VIDEO_INDEX].started && + ch->vpifparams.std_info.ycmux_mode == 0) + || ((ch->channel_id == VPIF_CHANNEL3_VIDEO) + && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { + vpif_err("other channel is using\n"); + return -EBUSY; + } + + ret = vpif_check_format(ch, &common->fmt.fmt.pix); + if (ret < 0) + return ret; + + /* Call videobuf_streamon to start streaming in videobuf */ + ret = videobuf_streamon(&common->buffer_queue); + if (ret < 0) { + vpif_err("videobuf_streamon\n"); + return ret; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* If buffer queue is empty, return error */ + if (list_empty(&common->dma_queue)) { + vpif_err("buffer queue is empty\n"); + ret = -EIO; + goto streamon_exit; + } + + /* Get the next frame from the buffer queue */ + common->next_frm = common->cur_frm = + list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + + list_del(&common->cur_frm->queue); + /* Mark state of the current frame to active */ + common->cur_frm->state = VIDEOBUF_ACTIVE; + + /* Initialize field_id and started member */ + ch->field_id = 0; + common->started = 1; + if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + addr = common->cur_frm->boff; + /* Calculate the offset for Y and C data in the buffer */ + vpif_calculate_offsets(ch); + + if ((ch->vpifparams.std_info.frm_fmt && + ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) + && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) + || (!ch->vpifparams.std_info.frm_fmt + && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { + vpif_err("conflict in field format and std format\n"); + ret = -EINVAL; + goto streamon_exit; + } + + /* clock settings */ + ret = + vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode, + ch->vpifparams.std_info.hd_sd); + if (ret < 0) { + vpif_err("can't set clock\n"); + goto streamon_exit; + } + + /* set the parameters and addresses */ + ret = vpif_set_video_params(vpif, ch->channel_id + 2); + if (ret < 0) + goto streamon_exit; + + common->started = ret; + vpif_config_addr(ch, ret); + common->set_addr((addr + common->ytop_off), + (addr + common->ybtm_off), + (addr + common->ctop_off), + (addr + common->cbtm_off)); + + /* Set interrupt for both the fields in VPIF + Register enable channel in VPIF register */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + channel2_intr_assert(); + channel2_intr_enable(1); + enable_channel2(1); + } + + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) + || (common->started == 2)) { + channel3_intr_assert(); + channel3_intr_enable(1); + enable_channel3(1); + } + channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; + } + +streamon_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + vpif_err("buffer type not supported\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + if (!common->started) { + vpif_err("channel->started\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* disable channel */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + enable_channel2(0); + channel2_intr_enable(0); + } + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel3(0); + channel3_intr_enable(0); + } + } + + common->started = 0; + mutex_unlock(&common->lock); + + return videobuf_streamoff(&common->buffer_queue); +} + +static int vpif_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type) + return -EINVAL; + + crop->bounds.left = crop->bounds.top = 0; + crop->defrect.left = crop->defrect.top = 0; + crop->defrect.height = crop->bounds.height = common->height; + crop->defrect.width = crop->bounds.width = common->width; + + return 0; +} + +static int vpif_enum_output(struct file *file, void *fh, + struct v4l2_output *output) +{ + + struct vpif_display_config *config = vpif_dev->platform_data; + + if (output->index >= config->output_count) { + vpif_dbg(1, debug, "Invalid output index\n"); + return -EINVAL; + } + + strcpy(output->name, config->output[output->index]); + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->std = DM646X_V4L2_STD; + + return 0; +} + +static int vpif_s_output(struct file *file, void *priv, unsigned int i) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (common->started) { + vpif_err("Streaming in progress\n"); + ret = -EBUSY; + goto s_output_exit; + } + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, + s_routing, 0, i, 0); + + if (ret < 0) + vpif_err("Failed to set output standard\n"); + + vid_ch->output_id = i; + +s_output_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_g_output(struct file *file, void *priv, unsigned int *i) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + *i = vid_ch->output_id; + + return 0; +} + +static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *p = v4l2_prio_max(&ch->prio); + + return 0; +} + +static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_prio_change(&ch->prio, &fh->prio, p); +} + +/* vpif display ioctl operations */ +static const struct v4l2_ioctl_ops vpif_ioctl_ops = { + .vidioc_querycap = vpif_querycap, + .vidioc_g_priority = vpif_g_priority, + .vidioc_s_priority = vpif_s_priority, + .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out, + .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out, + .vidioc_reqbufs = vpif_reqbufs, + .vidioc_querybuf = vpif_querybuf, + .vidioc_qbuf = vpif_qbuf, + .vidioc_dqbuf = vpif_dqbuf, + .vidioc_streamon = vpif_streamon, + .vidioc_streamoff = vpif_streamoff, + .vidioc_s_std = vpif_s_std, + .vidioc_g_std = vpif_g_std, + .vidioc_enum_output = vpif_enum_output, + .vidioc_s_output = vpif_s_output, + .vidioc_g_output = vpif_g_output, + .vidioc_cropcap = vpif_cropcap, +}; + +static const struct v4l2_file_operations vpif_fops = { + .owner = THIS_MODULE, + .open = vpif_open, + .release = vpif_release, + .ioctl = video_ioctl2, + .mmap = vpif_mmap, + .poll = vpif_poll +}; + +static struct video_device vpif_video_template = { + .name = "vpif", + .fops = &vpif_fops, + .minor = -1, + .ioctl_ops = &vpif_ioctl_ops, + .tvnorms = DM646X_V4L2_STD, + .current_norm = V4L2_STD_625_50, + +}; + +/*Configure the channels, buffer sizei, request irq */ +static int initialize_vpif(void) +{ + int free_channel_objects_index; + int free_buffer_channel_index; + int free_buffer_index; + int err = 0, i, j; + + /* Default number of buffers should be 3 */ + if ((ch2_numbuffers > 0) && + (ch2_numbuffers < config_params.min_numbuffers)) + ch2_numbuffers = config_params.min_numbuffers; + if ((ch3_numbuffers > 0) && + (ch3_numbuffers < config_params.min_numbuffers)) + ch3_numbuffers = config_params.min_numbuffers; + + /* Set buffer size to min buffers size if invalid buffer size is + * given */ + if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]) + ch2_bufsize = + config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]; + if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]) + ch3_bufsize = + config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]; + + config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers; + + if (ch2_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] = + ch2_bufsize; + } + config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers; + + if (ch3_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] = + ch3_bufsize; + } + + /* Allocate memory for six channel objects */ + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + vpif_obj.dev[i] = + kmalloc(sizeof(struct channel_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!vpif_obj.dev[i]) { + free_channel_objects_index = i; + err = -ENOMEM; + goto vpif_init_free_channel_objects; + } + } + + free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES; + free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS; + free_buffer_index = config_params.numbuffers[i - 1]; + + return 0; + +vpif_init_free_channel_objects: + for (j = 0; j < free_channel_objects_index; j++) + kfree(vpif_obj.dev[j]); + return err; +} + +/* + * vpif_probe: This function creates device entries by register itself to the + * V4L2 driver and initializes fields of each channel objects + */ +static __init int vpif_probe(struct platform_device *pdev) +{ + struct vpif_subdev_info *subdevdata; + struct vpif_display_config *config; + int i, j = 0, k, q, m, err = 0; + struct i2c_adapter *i2c_adap; + struct vpif_config *config; + struct common_obj *common; + struct channel_obj *ch; + struct video_device *vfd; + struct resource *res; + int subdev_count; + + vpif_dev = &pdev->dev; + + err = initialize_vpif(); + + if (err) { + v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); + return err; + } + + err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); + if (err) { + v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); + return err; + } + + k = 0; + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) { + for (i = res->start; i <= res->end; i++) { + if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, + "DM646x_Display", + (void *)(&vpif_obj.dev[k]->channel_id))) { + err = -EBUSY; + goto vpif_int_err; + } + } + k++; + } + + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (vfd == NULL) { + for (j = 0; j < i; j++) { + ch = vpif_obj.dev[j]; + video_device_release(ch->video_dev); + } + err = -ENOMEM; + goto vpif_int_err; + } + + /* Initialize field of video device */ + *vfd = vpif_video_template; + vfd->v4l2_dev = &vpif_obj.v4l2_dev; + vfd->release = video_device_release; + snprintf(vfd->name, sizeof(vfd->name), + "DM646x_VPIFDisplay_DRIVER_V%d.%d.%d", + (VPIF_DISPLAY_VERSION_CODE >> 16) & 0xff, + (VPIF_DISPLAY_VERSION_CODE >> 8) & 0xff, + (VPIF_DISPLAY_VERSION_CODE) & 0xff); + + /* Set video_dev to the video device */ + ch->video_dev = vfd; + } + + for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + /* Initialize field of the channel objects */ + atomic_set(&ch->usrs, 0); + for (k = 0; k < VPIF_NUMOBJECTS; k++) { + ch->common[k].numbuffers = 0; + common = &ch->common[k]; + common->io_usrs = 0; + common->started = 0; + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + common->numbuffers = 0; + common->set_addr = NULL; + common->ytop_off = common->ybtm_off = 0; + common->ctop_off = common->cbtm_off = 0; + common->cur_frm = common->next_frm = NULL; + memset(&common->fmt, 0, sizeof(common->fmt)); + common->numbuffers = config_params.numbuffers[k]; + + } + ch->initialized = 0; + ch->channel_id = j; + if (j < 2) + ch->common[VPIF_VIDEO_INDEX].numbuffers = + config_params.numbuffers[ch->channel_id]; + else + ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; + + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + ch->common[VPIF_VIDEO_INDEX].fmt.type = + V4L2_BUF_TYPE_VIDEO_OUTPUT; + + /* register video device */ + vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", + (int)ch, (int)&ch->video_dev); + + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 3 : 2)); + if (err < 0) + goto probe_out; + + video_set_drvdata(ch->video_dev, ch); + } + + i2c_adap = i2c_get_adapter(1); + config = pdev->dev.platform_data; + subdev_count = config->subdev_count; + subdevdata = config->subdevinfo; + vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count, + GFP_KERNEL); + if (vpif_obj.sd == NULL) { + vpif_err("unable to allocate memory for subdevice pointers\n"); + err = -ENOMEM; + goto probe_out; + } + + for (i = 0; i < subdev_count; i++) { + vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, subdevdata[i].name, + &subdevdata[i].board_info, + NULL); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; + } + + return 0; + +probe_subdev_out: + kfree(vpif_obj.sd); +probe_out: + for (k = 0; k < j; k++) { + ch = vpif_obj.dev[k]; + video_unregister_device(ch->video_dev); + video_device_release(ch->video_dev); + ch->video_dev = NULL; + } +vpif_int_err: + v4l2_device_unregister(&vpif_obj.v4l2_dev); + vpif_err("VPIF IRQ request failed\n"); + for (q = k; k >= 0; k--) { + for (m = i; m >= res->start; m--) + free_irq(m, (void *)(&vpif_obj.dev[k]->channel_id)); + res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1); + m = res->end; + } + + return err; +} + +/* + * vpif_remove: It un-register channels from V4L2 driver + */ +static int vpif_remove(struct platform_device *device) +{ + struct channel_obj *ch; + int i; + + v4l2_device_unregister(&vpif_obj.v4l2_dev); + + /* un-register device */ + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + + ch->video_dev = NULL; + } + + return 0; +} + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif_display", + .owner = THIS_MODULE, + }, + .probe = vpif_probe, + .remove = vpif_remove, +}; + +static __init int vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} + +/* + * vpif_cleanup: This function un-registers device and driver to the kernel, + * frees requested irq handler and de-allocates memory allocated for channel + * objects. + */ +static void vpif_cleanup(void) +{ + struct platform_device *pdev; + struct resource *res; + int irq_num; + int i = 0; + + pdev = container_of(vpif_dev, struct platform_device, dev); + + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) + free_irq(irq_num, + (void *)(&vpif_obj.dev[i]->channel_id)); + i++; + } + + platform_driver_unregister(&vpif_driver); + kfree(vpif_obj.sd); + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + +module_init(vpif_init); +module_exit(vpif_cleanup); diff --git a/drivers/media/video/ti-media/vpif_display.h b/drivers/media/video/ti-media/vpif_display.h new file mode 100644 index 00000000000..a2a7cd166bb --- /dev/null +++ b/drivers/media/video/ti-media/vpif_display.h @@ -0,0 +1,175 @@ +/* + * DM646x display header file + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DAVINCIHD_DISPLAY_H +#define DAVINCIHD_DISPLAY_H + +/* Header files */ +#include +#include +#include +#include +#include +#include + +#include "vpif.h" + +/* Macros */ +#define VPIF_MAJOR_RELEASE (0) +#define VPIF_MINOR_RELEASE (0) +#define VPIF_BUILD (1) + +#define VPIF_DISPLAY_VERSION_CODE \ + ((VPIF_MAJOR_RELEASE << 16) | (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD) + +#define VPIF_VALID_FIELD(field) \ + (((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \ + (((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \ + (V4L2_FIELD_SEQ_BT == field))) + +#define VPIF_DISPLAY_MAX_DEVICES (2) +#define VPIF_SLICED_BUF_SIZE (256) +#define VPIF_SLICED_MAX_SERVICES (3) +#define VPIF_VIDEO_INDEX (0) +#define VPIF_VBI_INDEX (1) +#define VPIF_HBI_INDEX (2) + +/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/ +#define VPIF_NUMOBJECTS (1) + +/* Macros */ +#define ISALIGNED(a) (0 == ((a) & 7)) + +/* enumerated data types */ +/* Enumerated data type to give id to each device per channel */ +enum vpif_channel_id { + VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */ + VPIF_CHANNEL3_VIDEO, /* Channel3 Video */ +}; + +/* structures */ + +struct video_obj { + enum v4l2_field buf_field; + u32 latest_only; /* indicate whether to return + * most recent displayed frame only */ + v4l2_std_id stdid; /* Currently selected or default + * standard */ + u32 output_id; /* Current output id */ +}; + +struct vbi_obj { + int num_services; + struct vpif_vbi_params vbiparams; /* vpif parameters for the raw + * vbi data */ +}; + +struct common_obj { + /* Buffer specific parameters */ + u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for + * storing frames */ + u32 numbuffers; /* number of buffers */ + struct videobuf_buffer *cur_frm; /* Pointer pointing to current + * videobuf_buffer */ + struct videobuf_buffer *next_frm; /* Pointer pointing to next + * videobuf_buffer */ + enum v4l2_memory memory; /* This field keeps track of + * type of buffer exchange + * method user has selected */ + struct v4l2_format fmt; /* Used to store the format */ + struct videobuf_queue buffer_queue; /* Buffer queue used in + * video-buf */ + struct list_head dma_queue; /* Queue of filled frames */ + spinlock_t irqlock; /* Used in video-buf */ + + /* channel specific parameters */ + struct mutex lock; /* lock used to access this + * structure */ + u32 io_usrs; /* number of users performing + * IO */ + u8 started; /* Indicates whether streaming + * started */ + u32 ytop_off; /* offset of Y top from the + * starting of the buffer */ + u32 ybtm_off; /* offset of Y bottom from the + * starting of the buffer */ + u32 ctop_off; /* offset of C top from the + * starting of the buffer */ + u32 cbtm_off; /* offset of C bottom from the + * starting of the buffer */ + /* Function pointer to set the addresses */ + void (*set_addr) (unsigned long, unsigned long, + unsigned long, unsigned long); + u32 height; + u32 width; +}; + +struct channel_obj { + /* V4l2 specific parameters */ + struct video_device *video_dev; /* Identifies video device for + * this channel */ + struct v4l2_prio_state prio; /* Used to keep track of state of + * the priority */ + atomic_t usrs; /* number of open instances of + * the channel */ + u32 field_id; /* Indicates id of the field + * which is being displayed */ + u8 initialized; /* flag to indicate whether + * encoder is initialized */ + + enum vpif_channel_id channel_id;/* Identifies channel */ + struct vpif_params vpifparams; + struct common_obj common[VPIF_NUMOBJECTS]; + struct video_obj video; + struct vbi_obj vbi; +}; + +/* File handle structure */ +struct vpif_fh { + struct channel_obj *channel; /* pointer to channel object for + * opened device */ + u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle + * is doing IO */ + enum v4l2_priority prio; /* Used to keep track priority of + * this instance */ + u8 initialized; /* Used to keep track of whether this + * file handle has initialized + * channel or not */ +}; + +/* vpif device structure */ +struct vpif_device { + struct v4l2_device v4l2_dev; + struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; + struct v4l2_subdev **sd; + +}; + +struct vpif_config_params { + u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; + u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; + u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS]; + u8 min_numbuffers; +}; + +/* Struct which keeps track of the line numbers for the sliced vbi service */ +struct vpif_service_line { + u16 service_id; + u16 service_line[2]; + u16 enc_service_id; + u8 bytestowrite; +}; + +#endif /* DAVINCIHD_DISPLAY_H */ diff --git a/drivers/media/video/ti-media/vpss.c b/drivers/media/video/ti-media/vpss.c new file mode 100644 index 00000000000..fe436bacd22 --- /dev/null +++ b/drivers/media/video/ti-media/vpss.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2009 Texas Instruments. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * common vpss driver for all video drivers. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VPSS Driver"); +MODULE_AUTHOR("Texas Instruments"); + +/* DM644x defines */ +#define DM644X_SBL_PCR_VPSS (4) + +/* vpss BL register offsets */ +#define DM355_VPSSBL_CCDCMUX 0x1c +/* vpss CLK register offsets */ +#define DM355_VPSSCLK_CLKCTRL 0x04 +/* masks and shifts */ +#define VPSS_HSSISEL_SHIFT 4 + +/* + * vpss operations. Depends on platform. Not all functions are available + * on all platforms. The api, first check if a functio is available before + * invoking it. In the probe, the function ptrs are intialized based on + * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. + */ +struct vpss_hw_ops { + /* enable clock */ + int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); + /* select input to ccdc */ + void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); + /* clear wbl overlflow bit */ + int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); +}; + +/* vpss configuration */ +struct vpss_oper_config { + __iomem void *vpss_bl_regs_base; + __iomem void *vpss_regs_base; + struct resource *r1; + resource_size_t len1; + struct resource *r2; + resource_size_t len2; + char vpss_name[32]; + spinlock_t vpss_lock; + struct vpss_hw_ops hw_ops; +}; + +static struct vpss_oper_config oper_cfg; + +/* register access routines */ +static inline u32 bl_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_bl_regs_base + offset); +} + +static inline void bl_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_bl_regs_base + offset); +} + +static inline u32 vpss_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_regs_base + offset); +} + +static inline void vpss_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_regs_base + offset); +} + +static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); +} + +int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + if (!oper_cfg.hw_ops.select_ccdc_source) + return -1; + + dm355_select_ccdc_source(src_sel); + return 0; +} +EXPORT_SYMBOL(vpss_select_ccdc_source); + +static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + u32 mask = 1, val; + + if (wbl_sel < VPSS_PCR_AEW_WBL_0 || + wbl_sel > VPSS_PCR_CCDC_WBL_O) + return -1; + + /* writing a 0 clear the overflow */ + mask = ~(mask << wbl_sel); + val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; + bl_regw(val, DM644X_SBL_PCR_VPSS); + return 0; +} + +int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + if (!oper_cfg.hw_ops.clear_wbl_overflow) + return -1; + + return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); +} +EXPORT_SYMBOL(vpss_clear_wbl_overflow); + +/* + * dm355_enable_clock - Enable VPSS Clock + * @clock_sel: CLock to be enabled/disabled + * @en: enable/disable flag + * + * This is called to enable or disable a vpss clock + */ +static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + unsigned long flags; + u32 utemp, mask = 0x1, shift = 0; + + switch (clock_sel) { + case VPSS_VPBE_CLOCK: + /* nothing since lsb */ + break; + case VPSS_VENC_CLOCK_SEL: + shift = 2; + break; + case VPSS_CFALD_CLOCK: + shift = 3; + break; + case VPSS_H3A_CLOCK: + shift = 4; + break; + case VPSS_IPIPE_CLOCK: + shift = 5; + break; + case VPSS_CCDC_CLOCK: + shift = 6; + break; + default: + printk(KERN_ERR "dm355_enable_clock:" + " Invalid selector: %d\n", clock_sel); + return -1; + } + + spin_lock_irqsave(&oper_cfg.vpss_lock, flags); + utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); + if (!en) + utemp &= ~(mask << shift); + else + utemp |= (mask << shift); + + vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); + spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); + return 0; +} + +int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + if (!oper_cfg.hw_ops.enable_clock) + return -1; + + return oper_cfg.hw_ops.enable_clock(clock_sel, en); +} +EXPORT_SYMBOL(vpss_enable_clock); + +static int __init vpss_probe(struct platform_device *pdev) +{ + int status, dm355 = 0; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENOENT; + } + strcpy(oper_cfg.vpss_name, pdev->dev.platform_data); + + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) + dm355 = 1; + else if (strcmp(oper_cfg.vpss_name, "dm644x_vpss")) { + dev_err(&pdev->dev, "vpss driver not supported on" + " this platform\n"); + return -ENODEV; + } + + dev_info(&pdev->dev, "%s vpss probed\n", oper_cfg.vpss_name); + oper_cfg.r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!oper_cfg.r1) + return -ENOENT; + + oper_cfg.len1 = oper_cfg.r1->end - oper_cfg.r1->start + 1; + + oper_cfg.r1 = request_mem_region(oper_cfg.r1->start, oper_cfg.len1, + oper_cfg.r1->name); + if (!oper_cfg.r1) + return -EBUSY; + + oper_cfg.vpss_bl_regs_base = ioremap(oper_cfg.r1->start, oper_cfg.len1); + if (!oper_cfg.vpss_bl_regs_base) { + status = -EBUSY; + goto fail1; + } + + if (dm355) { + oper_cfg.r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!oper_cfg.r2) { + status = -ENOENT; + goto fail2; + } + oper_cfg.len2 = oper_cfg.r2->end - oper_cfg.r2->start + 1; + oper_cfg.r2 = request_mem_region(oper_cfg.r2->start, + oper_cfg.len2, + oper_cfg.r2->name); + if (!oper_cfg.r2) { + status = -EBUSY; + goto fail2; + } + + oper_cfg.vpss_regs_base = ioremap(oper_cfg.r2->start, + oper_cfg.len2); + if (!oper_cfg.vpss_regs_base) { + status = -EBUSY; + goto fail3; + } + } + + if (dm355) { + oper_cfg.hw_ops.enable_clock = dm355_enable_clock; + oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; + } else + oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; + + spin_lock_init(&oper_cfg.vpss_lock); + dev_info(&pdev->dev, "%s vpss probe success\n", oper_cfg.vpss_name); + return 0; + +fail3: + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); +fail2: + iounmap(oper_cfg.vpss_bl_regs_base); +fail1: + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + return status; +} + +static int vpss_remove(struct platform_device *pdev) +{ + iounmap(oper_cfg.vpss_bl_regs_base); + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) { + iounmap(oper_cfg.vpss_regs_base); + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); + } + return 0; +} + +static struct platform_driver vpss_driver = { + .driver = { + .name = "vpss", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(vpss_remove), + .probe = vpss_probe, +}; + +static void vpss_exit(void) +{ + platform_driver_unregister(&vpss_driver); +} + +static int __init vpss_init(void) +{ + return platform_driver_register(&vpss_driver); +} +subsys_initcall(vpss_init); +module_exit(vpss_exit); diff --git a/drivers/media/video/tps61059.c b/drivers/media/video/tps61059.c new file mode 100644 index 00000000000..3b52600437d --- /dev/null +++ b/drivers/media/video/tps61059.c @@ -0,0 +1,390 @@ +/* + * tps61059.c - tps61059 Flash driver + * + * Copyright (C) 2009 Texas Instruments. + * + * Contributors: + * Nikolay Vladimirov + * Pallavi Kulkarni + * Sergio Aguirre + * + * Leverage mt9p012.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define CTRL_CAMERA_FLASH_STROBE 0 +#define CTRL_CAMERA_FLASH_TIMEOUT 1 +#define CTRL_CAMERA_FLASH_TORCH_INTENSITY 2 + +struct tps61059_flash { + struct device *dev; + struct tps61059_platform_data *pdata; + struct v4l2_int_device *v4l2_int_device; + u32 flash_timeout; + u32 torch_intensity; + struct timer_list strobe_timer; +}; + + +static void tps61059_flash_off(unsigned long data) +{ + struct tps61059_flash *flash = (struct tps61059_flash *)data; + + flash->pdata->flash_off(); +} + +static int tps61059_delay_strobe(struct tps61059_flash *flash) +{ + int ret = 0; + + setup_timer(&flash->strobe_timer, tps61059_flash_off, + (unsigned long)flash); + + if (flash->flash_timeout) { + ret = mod_timer(&flash->strobe_timer, + jiffies + + usecs_to_jiffies(flash->flash_timeout)); + if (ret) + dev_err(flash->dev, "tps61059 mod_timer() returned %i!\n", + ret); + } + + return ret; +} + +static int tps61059_strobe(struct v4l2_int_device *s) +{ + struct tps61059_flash *flash = s->priv; + + flash->pdata->flash_on(); + return tps61059_delay_strobe(flash); +} + +static void tps61059_update_hw(struct v4l2_int_device *s) +{ + struct tps61059_flash *flash = s->priv; + + flash->pdata->s_torch_intensity(flash->torch_intensity); +} + +static struct v4l2_queryctrl tps61059_ctrls[] = { + { + .id = V4L2_CID_FLASH_STROBE, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Flash strobe", + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + }, + + { + .id = V4L2_CID_FLASH_TIMEOUT, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Flash timeout [us]", + .minimum = 1, + .maximum = 1000000, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, + { + .id = V4L2_CID_FLASH_TORCH_INTENSITY, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Torch intensity", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +/** + * find_vctrl - Finds the requested ID in the video control structure array + * @id: ID of control to search the video control array for + * + * Returns the index of the requested ID from the control structure array + */ +static int find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(tps61059_ctrls) - 1); i >= 0; i--) { + if (tps61059_ctrls[i].id == id) + return i; + } + return -EINVAL; +} + +/** + * tps61059_ioctl_queryctrl - V4L2 flash interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the video_control[] array. Otherwise, returns -EINVAL if the + * control is not supported. + */ +static int tps61059_ioctl_queryctrl(struct v4l2_int_device *s, + struct v4l2_queryctrl *qc) +{ + int i; + + i = find_vctrl(qc->id); + if (i == -EINVAL) + qc->flags = V4L2_CTRL_FLAG_DISABLED; + + if (i < 0) + return -EINVAL; + *qc = tps61059_ctrls[i]; + return 0; +} + +/** + * tps61059_ioctl_g_ctrl - V4L2 tps61059 flash interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int tps61059_ioctl_g_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tps61059_flash *flash = s->priv; + + switch (vc->id) { + case V4L2_CID_FLASH_TIMEOUT: + vc->value = flash->flash_timeout; + break; + case V4L2_CID_FLASH_TORCH_INTENSITY: + vc->value = flash->torch_intensity; + break; + default: + return -EINVAL; + } + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 tps61059 flash interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int tps61059_ioctl_s_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tps61059_flash *flash = s->priv; + int ctrl; + int *value; + + switch (vc->id) { + case V4L2_CID_FLASH_STROBE: + return tps61059_strobe(s); + + case V4L2_CID_FLASH_TIMEOUT: + ctrl = CTRL_CAMERA_FLASH_TIMEOUT; + value = &flash->flash_timeout; + break; + case V4L2_CID_FLASH_TORCH_INTENSITY: + ctrl = CTRL_CAMERA_FLASH_TORCH_INTENSITY; + value = &flash->torch_intensity; + break; + default: + return -EINVAL; + } + + if (vc->value < tps61059_ctrls[ctrl].minimum) + vc->value = tps61059_ctrls[ctrl].minimum; + if (vc->value > tps61059_ctrls[ctrl].maximum) + vc->value = tps61059_ctrls[ctrl].maximum; + *value = vc->value; + + tps61059_update_hw(s); + return 0; +} + +/** + * tps61059_ioctl_s_power - V4L2 flash interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @state: power state to which device is to be set + * + * Sets devices power state to requested state, if possible. + */ +static int tps61059_ioctl_s_power(struct v4l2_int_device *s, + enum v4l2_power state) +{ + /* Dummy function, no powerup is needed, as we just use GPIOs + (no extra chip) */ + return 0; +} + +/** + * tps61059_ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold flash's private data address + * + * Returns device's (flash's) private data area address in p parameter + */ +static int tps61059_ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct tps61059_flash *flash = s->priv; + return flash->pdata->priv_data_set(p); + +} + +static struct v4l2_int_ioctl_desc tps61059_ioctl_desc[] = { + { vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *)tps61059_ioctl_queryctrl }, + { vidioc_int_g_ctrl_num, + (v4l2_int_ioctl_func *)tps61059_ioctl_g_ctrl }, + { vidioc_int_s_ctrl_num, + (v4l2_int_ioctl_func *)tps61059_ioctl_s_ctrl }, + { vidioc_int_s_power_num, + (v4l2_int_ioctl_func *)tps61059_ioctl_s_power }, + { vidioc_int_g_priv_num, + (v4l2_int_ioctl_func *)tps61059_ioctl_g_priv }, +}; + +static struct v4l2_int_slave tps61059_slave = { + .ioctls = tps61059_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tps61059_ioctl_desc), +}; + +static struct v4l2_int_device tps61059_int_device = { + .module = THIS_MODULE, + .name = "TPS61059", + .type = v4l2_int_type_slave, + .u = { + .slave = &tps61059_slave, + }, +}; + +static int tps61059_probe(struct platform_device *pdev) +{ + struct tps61059_flash *flash; + struct tps61059_platform_data *pdata = pdev->dev.platform_data; + int err; + + if (!pdata) { + dev_err(&pdev->dev, "tps61059 platform data not supplied\n"); + return -ENOENT; + } + + flash = kzalloc(sizeof(*flash), GFP_KERNEL); + if (!flash) + return -ENOMEM; + + /* Don't keep pointer to platform data, copy elements instead */ + flash->pdata = kzalloc(sizeof(*flash->pdata), GFP_KERNEL); + if (!flash->pdata) { + err = -ENOMEM; + goto on_err1; + } + + flash->pdata->flash_on = pdata->flash_on; + flash->pdata->flash_off = pdata->flash_off; + flash->pdata->s_torch_intensity = pdata->s_torch_intensity; + flash->pdata->priv_data_set = pdata->priv_data_set; + + /* Set flash default values */ + flash->flash_timeout = tps61059_ctrls + [CTRL_CAMERA_FLASH_TIMEOUT].default_value; + flash->torch_intensity = tps61059_ctrls + [CTRL_CAMERA_FLASH_TORCH_INTENSITY].default_value; + + flash->v4l2_int_device = &tps61059_int_device; + flash->v4l2_int_device->priv = flash; + + flash->dev = &pdev->dev; + platform_set_drvdata(pdev, flash); + + err = v4l2_int_device_register(flash->v4l2_int_device); + if (err) { + dev_err(flash->dev, "Could not register " + "tps61059 as v4l2_int_device\n"); + goto on_err2; + } + + return 0; + +on_err2: + platform_set_drvdata(pdev, NULL); + kfree(flash->pdata); +on_err1: + kfree(flash); + return err; +} + +static int tps61059_remove(struct platform_device *pdev) +{ + struct tps61059_flash *flash = platform_get_drvdata(pdev); + int ret = 0; + + ret = del_timer(&flash->strobe_timer); + if (ret) { + dev_err(flash->dev, "strobe_timer is still in use!\n"); + return -EBUSY; + } + + v4l2_int_device_unregister(flash->v4l2_int_device); + platform_set_drvdata(pdev, NULL); + kfree(flash->pdata); + kfree(flash); + return 0; +} + +static struct platform_driver tps61059_driver = { + .probe = tps61059_probe, + .remove = tps61059_remove, + .driver = { + .name = "tps61059", + }, +}; + +/** + * tps61059_init - Module initialisation. + * + * Returns 0 if successful, or -ENODEV if device couldn't be initialized, or + * added as a character device. + **/ +static int __init tps61059_init(void) +{ + return platform_driver_register(&tps61059_driver); +} + +/** + * tps61059_exit - Module cleanup. + **/ +static void __exit tps61059_exit(void) +{ + platform_driver_unregister(&tps61059_driver); +} + +late_initcall(tps61059_init); +module_exit(tps61059_exit); + +MODULE_AUTHOR("Nikolay Vladimirov "); +MODULE_DESCRIPTION("TPS61059 3430spd GPIO LED flash v4l2 device"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/video/tvp514x-int.c b/drivers/media/video/tvp514x-int.c new file mode 100644 index 00000000000..aa2048b43b9 --- /dev/null +++ b/drivers/media/video/tvp514x-int.c @@ -0,0 +1,1589 @@ +/* + * drivers/media/video/tvp514x.c + * + * TI TVP5146/47 decoder driver + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "tvp514x_regs.h" + +/* Module Name */ +#define TVP514X_MODULE_NAME "tvp514x" + +/* Private macros for TVP */ +#define I2C_RETRY_COUNT (5) +#define LOCK_RETRY_COUNT (5) +#define LOCK_RETRY_DELAY (200) + +/* Debug functions */ +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dump_reg(client, reg, val) \ + do { \ + val = tvp514x_read_reg(client, reg); \ + v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ + } while (0) + +/** + * enum tvp514x_std - enum for supported standards + */ +enum tvp514x_std { + STD_NTSC_MJ = 0, + STD_PAL_BDGHIN, + STD_INVALID +}; + +/** + * enum tvp514x_state - enum for different decoder states + */ +enum tvp514x_state { + STATE_NOT_DETECTED, + STATE_DETECTED +}; + +/** + * struct tvp514x_std_info - Structure to store standard informations + * @width: Line width in pixels + * @height:Number of active lines + * @video_std: Value to write in REG_VIDEO_STD register + * @standard: v4l2 standard structure information + */ +struct tvp514x_std_info { + unsigned long width; + unsigned long height; + u8 video_std; + struct v4l2_standard standard; +}; + +static struct tvp514x_reg tvp514x_reg_list_default[0x40]; +/** + * struct tvp514x_decoder - TVP5146/47 decoder object + * @v4l2_int_device: Slave handle + * @tvp514x_slave: Slave pointer which is used by @v4l2_int_device + * @tvp514x_regs: copy of hw's regs with preset values. + * @pdata: Board specific + * @client: I2C client data + * @id: Entry from I2C table + * @ver: Chip version + * @state: TVP5146/47 decoder state - detected or not-detected + * @pix: Current pixel format + * @num_fmts: Number of formats + * @fmt_list: Format list + * @current_std: Current standard + * @num_stds: Number of standards + * @std_list: Standards list + * @route: input and output routing at chip level + */ +struct tvp514x_decoder { + struct v4l2_int_device v4l2_int_device; + struct v4l2_int_slave tvp514x_slave; + struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; + const struct tvp514x_platform_data *pdata; + struct i2c_client *client; + + struct i2c_device_id *id; + + int ver; + enum tvp514x_state state; + + struct v4l2_pix_format pix; + int num_fmts; + const struct v4l2_fmtdesc *fmt_list; + + enum tvp514x_std current_std; + int num_stds; + struct tvp514x_std_info *std_list; + + struct v4l2_routing route; +}; + +/* TVP514x default register values */ +static struct tvp514x_reg tvp514x_reg_list_default[] = { + {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ + {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, + {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, + {TOK_WRITE, REG_COLOR_KILLER, 0x10}, + {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, + {TOK_WRITE, REG_BRIGHTNESS, 0x80}, + {TOK_WRITE, REG_CONTRAST, 0x80}, + {TOK_WRITE, REG_SATURATION, 0x80}, + {TOK_WRITE, REG_HUE, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, + {TOK_SKIP, 0x0F, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, + {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, + {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, + {TOK_SKIP, 0x13, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, + {TOK_SKIP, 0x15, 0x00}, /* Reserved */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, + {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, 0x26, 0x00}, /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, + {TOK_SKIP, 0x29, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, + {TOK_SKIP, 0x2B, 0x00}, /* Reserved */ + {TOK_SKIP, REG_SCART_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_CONTROL, 0x00}, + {TOK_SKIP, 0x2F, 0x00}, /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, /* Reserved */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */ + {TOK_TERM, 0, 0}, +}; + +/* List of image formats supported by TVP5146/47 decoder + * Currently we are using 8 bit mode only, but can be + * extended to 10/20 bit mode. + */ +static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "8-bit UYVY 4:2:2 Format", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + .index = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "8-bit YUYV 4:2:2 Format", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, +}; + +/* + * Supported standards - + * + * Currently supports two standards only, need to add support for rest of the + * modes, like SECAM, etc... + */ +static struct tvp514x_std_info tvp514x_std_list[] = { + /* Standard: STD_NTSC_MJ */ + [STD_NTSC_MJ] = { + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_NTSC_MJ_BIT, + .standard = { + .index = 0, + .id = V4L2_STD_NTSC, + .name = "NTSC", + .frameperiod = {1001, 30000}, + .framelines = 525 + }, + /* Standard: STD_PAL_BDGHIN */ + }, + [STD_PAL_BDGHIN] = { + .width = PAL_NUM_ACTIVE_PIXELS, + .height = PAL_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_PAL_BDGHIN_BIT, + .standard = { + .index = 1, + .id = V4L2_STD_PAL, + .name = "PAL", + .frameperiod = {1, 25}, + .framelines = 625 + }, + }, + /* Standard: need to add for additional standard */ +}; +/* + * Control structure for Auto Gain + * This is temporary data, will get replaced once + * v4l2_ctrl_query_fill supports it. + */ +static const struct v4l2_queryctrl tvp514x_autogain_ctrl = { + .id = V4L2_CID_AUTOGAIN, + .name = "Gain, Automatic", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, +}; + +/* + * Read a value from a register in an TVP5146/47 decoder device. + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp514x_read_reg(struct i2c_client *client, u8 reg) +{ + int err; + int retry = 0; +read_again: + + err = i2c_smbus_read_byte_data(client, reg); + if (err == -1) { + if (retry <= I2C_RETRY_COUNT) { + v4l_dbg(1, debug, client, "Read:retry ...%d\n", retry); + retry++; + msleep_interruptible(10); + goto read_again; + } + } + + return err; +} + +/* + * Write a value to a register in an TVP5146/47 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + int retry = 0; +write_again: + + err = i2c_smbus_write_byte_data(client, reg, val); + if (err) { + if (retry <= I2C_RETRY_COUNT) { + v4l_dbg(1, debug, client, "Write:retry ...%d\n", retry); + retry++; + msleep_interruptible(10); + goto write_again; + } + } + + return err; +} + +/* + * tvp514x_write_regs : Initializes a list of TVP5146/47 registers + * if token is TOK_TERM, then entire write operation terminates + * if token is TOK_DELAY, then a delay of 'val' msec is introduced + * if token is TOK_SKIP, then the register write is skipped + * if token is TOK_WRITE, then the register write is performed + * + * reglist - list of registers to be written + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_regs(struct i2c_client *client, + const struct tvp514x_reg reglist[]) +{ + int err; + const struct tvp514x_reg *next = reglist; + + for (; next->token != TOK_TERM; next++) { + if (next->token == TOK_DELAY) { + msleep(next->val); + continue; + } + + if (next->token == TOK_SKIP) + continue; + + err = tvp514x_write_reg(client, next->reg, (u8) next->val); + if (err) { + v4l_err(client, "Write failed. Err[%d]\n", err); + return err; + } + } + return 0; +} + +/* + * tvp514x_get_current_std: + * Returns the current standard detected by TVP5146/47 + */ +static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder + *decoder) +{ + u8 std, std_status; + + std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) { + /* use the standard status register */ + std_status = tvp514x_read_reg(decoder->client, + REG_VIDEO_STD_STATUS); + } else + std_status = std; /* use the standard register itself */ + + switch (std_status & VIDEO_STD_MASK) { + case VIDEO_STD_NTSC_MJ_BIT: + return STD_NTSC_MJ; + + case VIDEO_STD_PAL_BDGHIN_BIT: + return STD_PAL_BDGHIN; + + default: + return STD_INVALID; + } + + return STD_INVALID; +} + +/* + * TVP5146/47 register dump function + */ +static void tvp514x_reg_dump(struct tvp514x_decoder *decoder) +{ + u8 value; + + dump_reg(decoder->client, REG_INPUT_SEL, value); + dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value); + dump_reg(decoder->client, REG_VIDEO_STD, value); + dump_reg(decoder->client, REG_OPERATION_MODE, value); + dump_reg(decoder->client, REG_COLOR_KILLER, value); + dump_reg(decoder->client, REG_LUMA_CONTROL1, value); + dump_reg(decoder->client, REG_LUMA_CONTROL2, value); + dump_reg(decoder->client, REG_LUMA_CONTROL3, value); + dump_reg(decoder->client, REG_BRIGHTNESS, value); + dump_reg(decoder->client, REG_CONTRAST, value); + dump_reg(decoder->client, REG_SATURATION, value); + dump_reg(decoder->client, REG_HUE, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL1, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL2, value); + dump_reg(decoder->client, REG_COMP_PR_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value); + dump_reg(decoder->client, REG_COMP_PB_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_SYNC_CONTROL, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value); + dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value); +} + +/* + * Configure the TVP5146/47 with the current register settings + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_configure(struct tvp514x_decoder *decoder) +{ + int err; + + /* common register initialization */ + err = + tvp514x_write_regs(decoder->client, decoder->tvp514x_regs); + if (err) + return err; + + if (debug) + tvp514x_reg_dump(decoder); + + return 0; +} + +/* + * Detect if an tvp514x is present, and if so which revision. + * A device is considered to be detected if the chip ID (LSB and MSB) + * registers match the expected values. + * Any value of the rom version register is accepted. + * Returns ENODEV error number if no device is detected, or zero + * if a device is detected. + */ +static int tvp514x_detect(struct tvp514x_decoder *decoder) +{ + u8 chip_id_msb, chip_id_lsb, rom_ver; + + chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION); + + v4l_dbg(1, debug, decoder->client, + "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", + chip_id_msb, chip_id_lsb, rom_ver); + if ((chip_id_msb != TVP514X_CHIP_ID_MSB) + || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) + && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { + /* We didn't read the values we expected, so this must not be + * an TVP5146/47. + */ + v4l_err(decoder->client, + "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); + return -ENODEV; + } + + decoder->ver = rom_ver; + decoder->state = STATE_DETECTED; + + v4l_info(decoder->client, + "%s found at 0x%x (%s)\n", decoder->client->name, + decoder->client->addr << 1, + decoder->client->adapter->name); + return 0; +} + +/* + * Following are decoder interface functions implemented by + * TVP5146/47 decoder driver. + */ + +/** + * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 std_id ioctl enum + * + * Returns the current standard detected by TVP5146/47. If no active input is + * detected, returns -EINVAL + */ +static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + enum tvp514x_std current_std; + enum tvp514x_input input_sel; + u8 sync_lock_status, lock_mask; + + if (std_id == NULL) + return -EINVAL; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + input_sel = decoder->route.input; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + /* check whether signal is locked */ + sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1); + if (lock_mask != (sync_lock_status & lock_mask)) + return -EINVAL; /* No input detected */ + + decoder->current_std = current_std; + *std_id = decoder->std_list[current_std].standard.id; + + v4l_dbg(1, debug, decoder->client, "Current STD: %s", + decoder->std_list[current_std].standard.name); + return 0; +} + +/** + * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 v4l2_std_id ioctl enum + * + * If std_id is supported, sets the requested standard. Otherwise, returns + * -EINVAL + */ +static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + int err, i; + + if (std_id == NULL) + return -EINVAL; + + for (i = 0; i < decoder->num_stds; i++) + if (*std_id & decoder->std_list[i].standard.id) + break; + + if ((i == decoder->num_stds) || (i == STD_INVALID)) + return -EINVAL; + + err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD, + decoder->std_list[i].video_std); + if (err) + return err; + + decoder->current_std = i; + decoder->tvp514x_regs[REG_VIDEO_STD].val = + decoder->std_list[i].video_std; + + v4l_dbg(1, debug, decoder->client, "Standard set to: %s", + decoder->std_list[i].standard.name); + return 0; +} + +/** + * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl + * @s: pointer to standard V4L2 device structure + * @index: number of the input + * + * If index is valid, selects the requested input. Otherwise, returns -EINVAL if + * the input is not supported or there is no active signal present in the + * selected input. + */ +static int ioctl_s_routing(struct v4l2_int_device *s, + struct v4l2_routing *route) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + enum tvp514x_input input_sel; + enum tvp514x_output output_sel; + enum tvp514x_std current_std = STD_INVALID; + u8 sync_lock_status, lock_mask; + int try_count = LOCK_RETRY_COUNT; + + if ((!route) || (route->input >= INPUT_INVALID) || + (route->output >= OUTPUT_INVALID)) + return -EINVAL; /* Index out of bound */ + + input_sel = route->input; + output_sel = route->output; + + err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel); + if (err) + return err; + + output_sel |= tvp514x_read_reg(decoder->client, + REG_OUTPUT_FORMATTER1) & 0x7; + err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1, + output_sel); + if (err) + return err; + + decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; + + /* Clear status */ + msleep(LOCK_RETRY_DELAY); + err = + tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01); + if (err) + return err; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + + while (try_count-- > 0) { + /* Allow decoder to sync up with new input */ + msleep(LOCK_RETRY_DELAY); + + /* get the current standard for future reference */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + continue; + + sync_lock_status = tvp514x_read_reg(decoder->client, + REG_STATUS1); + if (lock_mask == (sync_lock_status & lock_mask)) + break; /* Input detected */ + } + + if ((current_std == STD_INVALID) || (try_count < 0)) + return -EINVAL; + + decoder->current_std = current_std; + decoder->route.input = route->input; + decoder->route.output = route->output; + + v4l_dbg(1, debug, decoder->client, + "Input set to: %d, std : %d", + input_sel, current_std); + + return 0; +} + +/** + * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qctrl: standard V4L2 v4l2_queryctrl structure + * + * If the requested control is supported, returns the control information. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL; + + if (qctrl == NULL) + return err; + + switch (qctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* Brightness supported is (0-255), + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); + break; + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + /* Saturation and Contrast supported is - + * Contrast: 0 - 255 (Default - 128) + * Saturation: 0 - 255 (Default - 128) + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); + break; + case V4L2_CID_HUE: + /* Hue Supported is - + * Hue - -180 - +180 (Default - 0, Step - +180) + */ + err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); + break; + case V4L2_CID_AUTOGAIN: + /* Autogain is either 0 or 1*/ + memcpy(qctrl, &tvp514x_autogain_ctrl, + sizeof(struct v4l2_queryctrl)); + err = 0; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", qctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Query Control: %s : Min - %d, Max - %d, Def - %d", + qctrl->name, + qctrl->minimum, + qctrl->maximum, + qctrl->default_value); + + return err; +} + +/** + * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, returns the control's current + * value from the decoder. Otherwise, returns -EINVAL if the control is not + * supported. + */ +static int +ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (ctrl == NULL) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = decoder->tvp514x_regs[REG_BRIGHTNESS].val; + break; + case V4L2_CID_CONTRAST: + ctrl->value = decoder->tvp514x_regs[REG_CONTRAST].val; + break; + case V4L2_CID_SATURATION: + ctrl->value = decoder->tvp514x_regs[REG_SATURATION].val; + break; + case V4L2_CID_HUE: + ctrl->value = decoder->tvp514x_regs[REG_HUE].val; + if (ctrl->value == 0x7F) + ctrl->value = 180; + else if (ctrl->value == 0x80) + ctrl->value = -180; + else + ctrl->value = 0; + + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val; + if ((ctrl->value & 0x3) == 3) + ctrl->value = 1; + else + ctrl->value = 0; + + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return -EINVAL; + } + + v4l_dbg(1, debug, decoder->client, + "Get Control: ID - %d - %d", + ctrl->id, ctrl->value); + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, sets the control's current + * value in HW. Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL, value; + + if (ctrl == NULL) + return err; + + value = (__s32) ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid brightness setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS, + value); + if (err) + return err; + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; + break; + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid contrast setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_CONTRAST, + value); + if (err) + return err; + decoder->tvp514x_regs[REG_CONTRAST].val = value; + break; + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid saturation setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_SATURATION, + value); + if (err) + return err; + decoder->tvp514x_regs[REG_SATURATION].val = value; + break; + case V4L2_CID_HUE: + if (value == 180) + value = 0x7F; + else if (value == -180) + value = 0x80; + else if (value == 0) + value = 0; + else { + v4l_err(decoder->client, + "invalid hue setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_HUE, + value); + if (err) + return err; + decoder->tvp514x_regs[REG_HUE].val = value; + break; + case V4L2_CID_AUTOGAIN: + if (value == 1) + value = 0x0F; + else if (value == 0) + value = 0x0C; + else { + v4l_err(decoder->client, + "invalid auto gain setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL, + value); + if (err) + return err; + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Set Control: ID - %d - %d", + ctrl->id, ctrl->value); + + return err; +} + +/** + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats + */ +static int +ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) +{ + struct tvp514x_decoder *decoder = s->priv; + int index; + + if (fmt == NULL) + return -EINVAL; + + index = fmt->index; + if ((index >= decoder->num_fmts) || (index < 0)) + return -EINVAL; /* Index out of bound */ + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memcpy(fmt, &decoder->fmt_list[index], + sizeof(struct v4l2_fmtdesc)); + + v4l_dbg(1, debug, decoder->client, + "Current FMT: index - %d (%s)", + decoder->fmt_list[index].index, + decoder->fmt_list[index].description); + return 0; +} + +/** + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int +ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + int ifmt; + struct v4l2_pix_format *pix; + enum tvp514x_std current_std; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix = &f->fmt.pix; + + /* Calculate height and width based on current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + pix->width = decoder->std_list[current_std].width; + pix->height = decoder->std_list[current_std].height; + + for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) { + if (pix->pixelformat == + decoder->fmt_list[ifmt].pixelformat) + break; + } + if (ifmt == decoder->num_fmts) + ifmt = 0; /* None of the format matched, select default */ + pix->pixelformat = decoder->fmt_list[ifmt].pixelformat; + + pix->field = V4L2_FIELD_INTERLACED; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + + v4l_dbg(1, debug, decoder->client, + "Try FMT: pixelformat - %s, bytesperline - %d" + "Width - %d, Height - %d", + decoder->fmt_list[ifmt].description, pix->bytesperline, + pix->width, pix->height); + return 0; +} + +/** + * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int +ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_pix_format *pix; + int rval; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + pix = &f->fmt.pix; + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + decoder->pix = *pix; + + return rval; +} + +/** + * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the decoder's current pixel format in the v4l2_format + * parameter. + */ +static int +ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + f->fmt.pix = decoder->pix; + + v4l_dbg(1, debug, decoder->client, + "Current FMT: bytesperline - %d" + "Width - %d, Height - %d", + decoder->pix.bytesperline, + decoder->pix.width, decoder->pix.height); + return 0; +} + +/** + * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the decoder's video CAPTURE parameters. + */ +static int +ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_captureparm *cparm; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + cparm = &a->parm.capture; + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the decoder to use the input parameters, if possible. If + * not possible, returns the appropriate error code. + */ +static int +ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_fract *timeperframe; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + timeperframe = &a->parm.capture.timeperframe; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + *timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num + * @s: pointer to standard V4L2 device structure + * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure + * + * Gets slave interface parameters. + * Calculates the required xclk value to support the requested + * clock parameters in p. This value is returned in the p + * parameter. + */ +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + struct tvp514x_decoder *decoder = s->priv; + int rval; + + if (p == NULL) + return -EINVAL; + + if (NULL == decoder->pdata->ifparm) + return -EINVAL; + + rval = decoder->pdata->ifparm(p); + if (rval) { + v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval); + return rval; + } + + p->u.bt656.clock_curr = TVP514X_XCLK_BT656; + + return 0; +} + +/** + * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold decoder's private data address + * + * Returns device's (decoder's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (NULL == decoder->pdata->priv_data_set) + return -EINVAL; + + return decoder->pdata->priv_data_set(s, p); +} + +/** + * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = 0; + + switch (on) { + case V4L2_POWER_OFF: + /* Power Down Sequence */ + err = + tvp514x_write_reg(decoder->client, REG_OPERATION_MODE, + 0x01); + /* Disable mux for TVP5146/47 decoder data path */ + if (decoder->pdata->power_set) + err |= decoder->pdata->power_set(s, on); + decoder->state = STATE_NOT_DETECTED; + break; + + case V4L2_POWER_STANDBY: + if (decoder->pdata->power_set) + err = decoder->pdata->power_set(s, on); + break; + + case V4L2_POWER_ON: + /* Enable mux for TVP5146/47 decoder data path */ + if ((decoder->pdata->power_set) && + (decoder->state == STATE_NOT_DETECTED)) { + int i; + struct tvp514x_init_seq *int_seq = + (struct tvp514x_init_seq *) + decoder->id->driver_data; + + err = decoder->pdata->power_set(s, on); + + /* Power Up Sequence */ + for (i = 0; i < int_seq->no_regs; i++) { + err |= tvp514x_write_reg(decoder->client, + int_seq->init_reg_seq[i].reg, + int_seq->init_reg_seq[i].val); + } + /* Detect the sensor is not already detected */ + err |= tvp514x_detect(decoder); + if (err) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + } + err |= tvp514x_configure(decoder); + break; + + default: + err = -ENODEV; + break; + } + + return err; +} + +/** + * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the decoder device (calls tvp514x_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + + /* Set default standard to auto */ + decoder->tvp514x_regs[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; + + return tvp514x_configure(decoder); +} + +/** + * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * TVP5146/47 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + + err = tvp514x_detect(decoder); + if (err < 0) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + + v4l_info(decoder->client, + "chip version 0x%.2x detected\n", decoder->ver); + + return 0; +} + +static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { + {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init}, + {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit}, + {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power}, + {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, + {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init}, + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, + {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, + {vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, + {vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, + {vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *) ioctl_queryctrl}, + {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, + {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd}, + {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std}, + {vidioc_int_s_video_routing_num, + (v4l2_int_ioctl_func *) ioctl_s_routing}, +}; + +static struct tvp514x_decoder tvp514x_dev = { + .state = STATE_NOT_DETECTED, + + .fmt_list = tvp514x_fmt_list, + .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), + + .pix = { /* Default to NTSC 8-bit YUV 422 */ + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2, + .sizeimage = + NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }, + + .current_std = STD_NTSC_MJ, + .std_list = tvp514x_std_list, + .num_stds = ARRAY_SIZE(tvp514x_std_list), + .v4l2_int_device = { + .module = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + .type = v4l2_int_type_slave, + }, + .tvp514x_slave = { + .ioctls = tvp514x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), + }, +}; + +/** + * tvp514x_probe - decoder driver i2c probe handler + * @client: i2c driver client device structure + * + * Register decoder as an i2c client device and V4L2 + * device. + */ +static int +tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct tvp514x_decoder *decoder; + int err; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + if (!client->dev.platform_data) { + v4l_err(client, "No platform data!!\n"); + err = -ENODEV; + goto out_free; + } + + *decoder = tvp514x_dev; + decoder->v4l2_int_device.priv = decoder; + decoder->pdata = client->dev.platform_data; + decoder->v4l2_int_device.u.slave = &decoder->tvp514x_slave; + memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, + sizeof(tvp514x_reg_list_default)); + /* + * Fetch platform specific data, and configure the + * tvp514x_reg_list[] accordingly. Since this is one + * time configuration, no need to preserve. + */ + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= + (decoder->pdata->clk_polarity << 1); + decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* + * Save the id data, required for power up sequence + */ + decoder->id = (struct i2c_device_id *)id; + /* Attach to Master */ + strcpy(decoder->v4l2_int_device.u.slave->attach_to, + decoder->pdata->master); + decoder->client = client; + i2c_set_clientdata(client, decoder); + + /* Register with V4L2 layer as slave device */ + err = v4l2_int_device_register(&decoder->v4l2_int_device); + if (err) { + i2c_set_clientdata(client, NULL); + v4l_err(client, + "Unable to register to v4l2. Err[%d]\n", err); + goto out_free; + + } else + v4l_info(client, "Registered to v4l2 master %s!!\n", + decoder->pdata->master); + return 0; + +out_free: + kfree(decoder); + return err; +} + +/** + * tvp514x_remove - decoder driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister decoder as an i2c client device and V4L2 + * device. Complement of tvp514x_probe(). + */ +static int __exit tvp514x_remove(struct i2c_client *client) +{ + struct tvp514x_decoder *decoder = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(&decoder->v4l2_int_device); + i2c_set_clientdata(client, NULL); + kfree(decoder); + return 0; +} +/* + * TVP5146 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5146_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5146_init = { + .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq), + .init_reg_seq = tvp5146_init_reg_seq, +}; +/* + * TVP5147 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5147_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5147_init = { + .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq), + .init_reg_seq = tvp5147_init_reg_seq, +}; +/* + * TVP5146M2/TVP5147M1 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp514xm_init = { + .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq), + .init_reg_seq = tvp514xm_init_reg_seq, +}; +/* + * I2C Device Table - + * + * name - Name of the actual device/chip. + * driver_data - Driver data + */ +static const struct i2c_device_id tvp514x_id[] = { + {"tvp5146", (unsigned long)&tvp5146_init}, + {"tvp5146m2", (unsigned long)&tvp514xm_init}, + {"tvp5147", (unsigned long)&tvp5147_init}, + {"tvp5147m1", (unsigned long)&tvp514xm_init}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tvp514x_id); + +static struct i2c_driver tvp514x_i2c_driver = { + .driver = { + .name = TVP514X_MODULE_NAME, + .owner = THIS_MODULE, + }, + .probe = tvp514x_probe, + .remove = __exit_p(tvp514x_remove), + .id_table = tvp514x_id, +}; + +/** + * tvp514x_init + * + * Module init function + */ +static int __init tvp514x_init(void) +{ + return i2c_add_driver(&tvp514x_i2c_driver); +} + +/** + * tvp514x_cleanup + * + * Module exit function + */ +static void __exit tvp514x_cleanup(void) +{ + i2c_del_driver(&tvp514x_i2c_driver); +} + +module_init(tvp514x_init); +module_exit(tvp514x_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c new file mode 100644 index 00000000000..39329e67364 --- /dev/null +++ b/drivers/net/can/ti_hecc.c @@ -0,0 +1,1038 @@ +/* + * TI HECC (CAN) device driver + * + * This driver supports TI's HECC (High End CAN Controller module) and the + * specs for the same is available at + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed as is WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Your platform definitions should specify module ram offsets and interrupt + * number to use as follows: + * + * static struct ti_hecc_platform_data am3517_evm_hecc_pdata = { + * .scc_hecc_offset = 0, + * .scc_ram_offset = 0x3000, + * .hecc_ram_offset = 0x3000, + * .mbx_offset = 0x2000, + * .int_line = 0, + * .revision = 1, + * }; + * + * Please see include/can/platform/ti_hecc.h for description of above fields + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DRV_NAME "ti_hecc" +#define HECC_MODULE_VERSION "0.7" +MODULE_VERSION(HECC_MODULE_VERSION); +#define DRV_DESC "TI High End CAN Controller Driver " HECC_MODULE_VERSION + +/* TX / RX Mailbox Configuration */ +#define HECC_MAX_MAILBOXES 32 /* hardware mailboxes - do not change */ +#define MAX_TX_PRIO 0x3F /* hardware value - do not change */ + +/* + * Important Note: TX mailbox configuration + * TX mailboxes should be restricted to the number of SKB buffers to avoid + * maintaining SKB buffers separately. TX mailboxes should be a power of 2 + * for the mailbox logic to work. Top mailbox numbers are reserved for RX + * and lower mailboxes for TX. + * + * HECC_MAX_TX_MBOX HECC_MB_TX_SHIFT + * 4 (default) 2 + * 8 3 + * 16 4 + */ +#define HECC_MB_TX_SHIFT 2 /* as per table above */ +#define HECC_MAX_TX_MBOX BIT(HECC_MB_TX_SHIFT) + +#define HECC_TX_PRIO_SHIFT (HECC_MB_TX_SHIFT) +#define HECC_TX_PRIO_MASK (MAX_TX_PRIO << HECC_MB_TX_SHIFT) +#define HECC_TX_MB_MASK (HECC_MAX_TX_MBOX - 1) +#define HECC_TX_MASK ((HECC_MAX_TX_MBOX - 1) | HECC_TX_PRIO_MASK) +#define HECC_TX_MBOX_MASK (~(BIT(HECC_MAX_TX_MBOX) - 1)) +#define HECC_DEF_NAPI_WEIGHT HECC_MAX_RX_MBOX + +/* + * Important Note: RX mailbox configuration + * RX mailboxes are further logically split into two - main and buffer + * mailboxes. The goal is to get all packets into main mailboxes as + * driven by mailbox number and receive priority (higher to lower) and + * buffer mailboxes are used to receive pkts while main mailboxes are being + * processed. This ensures in-order packet reception. + * + * Here are the recommended values for buffer mailbox. Note that RX mailboxes + * start after TX mailboxes: + * + * HECC_MAX_RX_MBOX HECC_RX_BUFFER_MBOX No of buffer mailboxes + * 28 12 8 + * 16 20 4 + */ + +#define HECC_MAX_RX_MBOX (HECC_MAX_MAILBOXES - HECC_MAX_TX_MBOX) +#define HECC_RX_BUFFER_MBOX 12 /* as per table above */ +#define HECC_RX_FIRST_MBOX (HECC_MAX_MAILBOXES - 1) +#define HECC_RX_HIGH_MBOX_MASK (~(BIT(HECC_RX_BUFFER_MBOX) - 1)) + +/* TI HECC module registers */ +#define HECC_CANME 0x0 /* Mailbox enable */ +#define HECC_CANMD 0x4 /* Mailbox direction */ +#define HECC_CANTRS 0x8 /* Transmit request set */ +#define HECC_CANTRR 0xC /* Transmit request */ +#define HECC_CANTA 0x10 /* Transmission acknowledge */ +#define HECC_CANAA 0x14 /* Abort acknowledge */ +#define HECC_CANRMP 0x18 /* Receive message pending */ +#define HECC_CANRML 0x1C /* Remote message lost */ +#define HECC_CANRFP 0x20 /* Remote frame pending */ +#define HECC_CANGAM 0x24 /* SECC only:Global acceptance mask */ +#define HECC_CANMC 0x28 /* Master control */ +#define HECC_CANBTC 0x2C /* Bit timing configuration */ +#define HECC_CANES 0x30 /* Error and status */ +#define HECC_CANTEC 0x34 /* Transmit error counter */ +#define HECC_CANREC 0x38 /* Receive error counter */ +#define HECC_CANGIF0 0x3C /* Global interrupt flag 0 */ +#define HECC_CANGIM 0x40 /* Global interrupt mask */ +#define HECC_CANGIF1 0x44 /* Global interrupt flag 1 */ +#define HECC_CANMIM 0x48 /* Mailbox interrupt mask */ +#define HECC_CANMIL 0x4C /* Mailbox interrupt level */ +#define HECC_CANOPC 0x50 /* Overwrite protection control */ +#define HECC_CANTIOC 0x54 /* Transmit I/O control */ +#define HECC_CANRIOC 0x58 /* Receive I/O control */ +#define HECC_CANLNT 0x5C /* HECC only: Local network time */ +#define HECC_CANTOC 0x60 /* HECC only: Time-out control */ +#define HECC_CANTOS 0x64 /* HECC only: Time-out status */ +#define HECC_CANTIOCE 0x68 /* SCC only:Enhanced TX I/O control */ +#define HECC_CANRIOCE 0x6C /* SCC only:Enhanced RX I/O control */ + +/* Mailbox registers */ +#define HECC_CANMID 0x0 +#define HECC_CANMCF 0x4 +#define HECC_CANMDL 0x8 +#define HECC_CANMDH 0xC + +#define HECC_SET_REG 0xFFFFFFFF +#define HECC_CANID_MASK 0x3FF /* 18 bits mask for extended id's */ +#define HECC_CCE_WAIT_COUNT 100 /* Wait for ~1 sec for CCE bit */ + +#define HECC_CANMC_SCM BIT(13) /* SCC compat mode */ +#define HECC_CANMC_CCR BIT(12) /* Change config request */ +#define HECC_CANMC_PDR BIT(11) /* Local Power down - for sleep mode */ +#define HECC_CANMC_ABO BIT(7) /* Auto Bus On */ +#define HECC_CANMC_STM BIT(6) /* Self test mode - loopback */ +#define HECC_CANMC_SRES BIT(5) /* Software reset */ + +#define HECC_CANTIOC_EN BIT(3) /* Enable CAN TX I/O pin */ +#define HECC_CANRIOC_EN BIT(3) /* Enable CAN RX I/O pin */ + +#define HECC_CANMID_IDE BIT(31) /* Extended frame format */ +#define HECC_CANMID_AME BIT(30) /* Acceptance mask enable */ +#define HECC_CANMID_AAM BIT(29) /* Auto answer mode */ + +#define HECC_CANES_FE BIT(24) /* form error */ +#define HECC_CANES_BE BIT(23) /* bit error */ +#define HECC_CANES_SA1 BIT(22) /* stuck at dominant error */ +#define HECC_CANES_CRCE BIT(21) /* CRC error */ +#define HECC_CANES_SE BIT(20) /* stuff bit error */ +#define HECC_CANES_ACKE BIT(19) /* ack error */ +#define HECC_CANES_BO BIT(18) /* Bus off status */ +#define HECC_CANES_EP BIT(17) /* Error passive status */ +#define HECC_CANES_EW BIT(16) /* Error warning status */ +#define HECC_CANES_SMA BIT(5) /* suspend mode ack */ +#define HECC_CANES_CCE BIT(4) /* Change config enabled */ +#define HECC_CANES_PDA BIT(3) /* Power down mode ack */ + +#define HECC_CANBTC_SAM BIT(7) /* sample points */ + +#define HECC_BUS_ERROR (HECC_CANES_FE | HECC_CANES_BE |\ + HECC_CANES_CRCE | HECC_CANES_SE |\ + HECC_CANES_ACKE) + +#define HECC_CANMCF_RTR BIT(4) /* Remote transmit request */ + +#define HECC_CANGIF_MAIF BIT(17) /* Message alarm interrupt */ +#define HECC_CANGIF_TCOIF BIT(16) /* Timer counter overflow int */ +#define HECC_CANGIF_GMIF BIT(15) /* Global mailbox interrupt */ +#define HECC_CANGIF_AAIF BIT(14) /* Abort ack interrupt */ +#define HECC_CANGIF_WDIF BIT(13) /* Write denied interrupt */ +#define HECC_CANGIF_WUIF BIT(12) /* Wake up interrupt */ +#define HECC_CANGIF_RMLIF BIT(11) /* Receive message lost interrupt */ +#define HECC_CANGIF_BOIF BIT(10) /* Bus off interrupt */ +#define HECC_CANGIF_EPIF BIT(9) /* Error passive interrupt */ +#define HECC_CANGIF_WLIF BIT(8) /* Warning level interrupt */ +#define HECC_CANGIF_MBOX_MASK 0x1F /* Mailbox number mask */ +#define HECC_CANGIM_I1EN BIT(1) /* Int line 1 enable */ +#define HECC_CANGIM_I0EN BIT(0) /* Int line 0 enable */ +#define HECC_CANGIM_DEF_MASK 0x700 /* only busoff/warning/passive */ +#define HECC_CANGIM_SIL BIT(2) /* system interrupts to int line 1 */ + +/* CAN Bittiming constants as per HECC specs */ +static struct can_bittiming_const ti_hecc_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +struct ti_hecc_priv { + struct can_priv can; /* MUST be first member/field */ + struct napi_struct napi; + struct net_device *ndev; + struct clk *clk; + void __iomem *base; + u32 scc_ram_offset; + u32 hecc_ram_offset; + u32 mbx_offset; + u32 int_line; + spinlock_t mbx_lock; /* CANME register needs protection */ + u32 tx_head; + u32 tx_tail; + u32 rx_next; +}; + +static inline int get_tx_head_mb(struct ti_hecc_priv *priv) +{ + return priv->tx_head & HECC_TX_MB_MASK; +} + +static inline int get_tx_tail_mb(struct ti_hecc_priv *priv) +{ + return priv->tx_tail & HECC_TX_MB_MASK; +} + +static inline int get_tx_head_prio(struct ti_hecc_priv *priv) +{ + return (priv->tx_head >> HECC_TX_PRIO_SHIFT) & MAX_TX_PRIO; +} + +static inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val) +{ + __raw_writel(val, priv->base + priv->hecc_ram_offset + mbxno * 4); +} + +static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno, + u32 reg, u32 val) +{ + __raw_writel(val, priv->base + priv->mbx_offset + mbxno * 0x10 + + reg); +} + +static inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg) +{ + return __raw_readl(priv->base + priv->mbx_offset + mbxno * 0x10 + + reg); +} + +static inline void hecc_write(struct ti_hecc_priv *priv, u32 reg, u32 val) +{ + __raw_writel(val, priv->base + reg); +} + +static inline u32 hecc_read(struct ti_hecc_priv *priv, int reg) +{ + return __raw_readl(priv->base + reg); +} + +static inline void hecc_set_bit(struct ti_hecc_priv *priv, int reg, + u32 bit_mask) +{ + hecc_write(priv, reg, hecc_read(priv, reg) | bit_mask); +} + +static inline void hecc_clear_bit(struct ti_hecc_priv *priv, int reg, + u32 bit_mask) +{ + hecc_write(priv, reg, hecc_read(priv, reg) & ~bit_mask); +} + +static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask) +{ + return (hecc_read(priv, reg) & bit_mask) ? 1 : 0; +} + +static int ti_hecc_get_state(const struct net_device *ndev, + enum can_state *state) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + *state = priv->can.state; + return 0; +} + +static int ti_hecc_set_btc(struct ti_hecc_priv *priv) +{ + struct can_bittiming *bit_timing = &priv->can.bittiming; + u32 can_btc; + + can_btc = (bit_timing->phase_seg2 - 1) & 0x7; + can_btc |= ((bit_timing->phase_seg1 + bit_timing->prop_seg - 1) + & 0xF) << 3; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) { + if (bit_timing->brp > 4) + can_btc |= HECC_CANBTC_SAM; + else + dev_warn(priv->ndev->dev.parent, "WARN: Triple" \ + "sampling not set due to h/w limitations"); + } + can_btc |= ((bit_timing->sjw - 1) & 0x3) << 8; + can_btc |= ((bit_timing->brp - 1) & 0xFF) << 16; + + /* ERM being set to 0 by default meaning resync at falling edge */ + + hecc_write(priv, HECC_CANBTC, can_btc); + dev_info(priv->ndev->dev.parent, "setting CANBTC=%#x\n", can_btc); + + return 0; +} + +static void ti_hecc_reset(struct net_device *ndev) +{ + u32 cnt; + struct ti_hecc_priv *priv = netdev_priv(ndev); + + dev_dbg(ndev->dev.parent, "resetting hecc ...\n"); + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SRES); + + /* Set change control request and wait till enabled */ + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + + /* + * INFO: It has been observed that at times CCE bit may not be + * set and hw seems to be ok even if this bit is not set so + * timing out with a timing of 1ms to respect the specs + */ + cnt = HECC_CCE_WAIT_COUNT; + while (!hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { + --cnt; + udelay(10); + } + + /* + * Note: On HECC, BTC can be programmed only in initialization mode, so + * it is expected that the can bittiming parameters are set via ip + * utility before the device is opened + */ + ti_hecc_set_btc(priv); + + /* Clear CCR (and CANMC register) and wait for CCE = 0 enable */ + hecc_write(priv, HECC_CANMC, 0); + + /* + * INFO: CAN net stack handles bus off and hence disabling auto-bus-on + * hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_ABO); + */ + + /* + * INFO: It has been observed that at times CCE bit may not be + * set and hw seems to be ok even if this bit is not set so + */ + cnt = HECC_CCE_WAIT_COUNT; + while (hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { + --cnt; + udelay(10); + } + + /* Enable TX and RX I/O Control pins */ + hecc_write(priv, HECC_CANTIOC, HECC_CANTIOC_EN); + hecc_write(priv, HECC_CANRIOC, HECC_CANRIOC_EN); + + /* Clear registers for clean operation */ + hecc_write(priv, HECC_CANTA, HECC_SET_REG); + hecc_write(priv, HECC_CANRMP, HECC_SET_REG); + hecc_write(priv, HECC_CANGIF0, HECC_SET_REG); + hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); + hecc_write(priv, HECC_CANME, 0); + hecc_write(priv, HECC_CANMD, 0); + + /* SCC compat mode NOT supported (and not needed too) */ + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SCM); +} + +static void ti_hecc_start(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + u32 cnt, mbxno, mbx_mask; + + /* put HECC in initialization mode and set btc */ + ti_hecc_reset(ndev); + + priv->tx_head = priv->tx_tail = HECC_TX_MASK; + priv->rx_next = HECC_RX_FIRST_MBOX; + + /* Enable local and global acceptance mask registers */ + hecc_write(priv, HECC_CANGAM, HECC_SET_REG); + + /* Prepare configured mailboxes to receive messages */ + for (cnt = 0; cnt < HECC_MAX_RX_MBOX; cnt++) { + mbxno = HECC_MAX_MAILBOXES - 1 - cnt; + mbx_mask = BIT(mbxno); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + hecc_write_mbx(priv, mbxno, HECC_CANMID, HECC_CANMID_AME); + hecc_write_lam(priv, mbxno, HECC_SET_REG); + hecc_set_bit(priv, HECC_CANMD, mbx_mask); + hecc_set_bit(priv, HECC_CANME, mbx_mask); + hecc_set_bit(priv, HECC_CANMIM, mbx_mask); + } + + /* Prevent message over-write & Enable interrupts */ + hecc_write(priv, HECC_CANOPC, HECC_SET_REG); + if (priv->int_line) { + hecc_write(priv, HECC_CANMIL, HECC_SET_REG); + hecc_write(priv, HECC_CANGIM, HECC_CANGIM_DEF_MASK | + HECC_CANGIM_I1EN | HECC_CANGIM_SIL); + } else { + hecc_write(priv, HECC_CANMIL, 0); + hecc_write(priv, HECC_CANGIM, + HECC_CANGIM_DEF_MASK | HECC_CANGIM_I0EN); + } + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static void ti_hecc_stop(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + /* Disable interrupts and disable mailboxes */ + hecc_write(priv, HECC_CANGIM, 0); + hecc_write(priv, HECC_CANMIM, 0); + hecc_write(priv, HECC_CANME, 0); + priv->can.state = CAN_STATE_STOPPED; +} + +static int ti_hecc_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + ti_hecc_start(ndev); + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +/* + * ti_hecc_xmit: HECC Transmit + * + * The transmit mailboxes start from 0 to HECC_MAX_TX_MBOX. In HECC the + * priority of the mailbox for tranmission is dependent upon priority setting + * field in mailbox registers. The mailbox with highest value in priority field + * is transmitted first. Only when two mailboxes have the same value in + * priority field the highest numbered mailbox is transmitted first. + * + * To utilize the HECC priority feature as described above we start with the + * highest numbered mailbox with highest priority level and move on to the next + * mailbox with the same priority level and so on. Once we loop through all the + * transmit mailboxes we choose the next priority level (lower) and so on + * until we reach the lowest priority level on the lowest numbered mailbox + * when we stop transmission until all mailboxes are transmitted and then + * restart at highest numbered mailbox with highest priority. + * + * Two counters (head and tail) are used to track the next mailbox to transmit + * and to track the echo buffer for already transmitted mailbox. The queue + * is stopped when all the mailboxes are busy or when there is a priority + * value roll-over happens. + */ +static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + u32 mbxno, mbx_mask, data; + unsigned long flags; + + mbxno = get_tx_head_mb(priv); + mbx_mask = BIT(mbxno); + spin_lock_irqsave(&priv->mbx_lock, flags); + if (unlikely(hecc_read(priv, HECC_CANME) & mbx_mask)) { + spin_unlock_irqrestore(&priv->mbx_lock, flags); + netif_stop_queue(ndev); + dev_err(priv->ndev->dev.parent, + "BUG: TX mbx not ready tx_head=%08X, tx_tail=%08X\n", + priv->tx_head, priv->tx_tail); + return NETDEV_TX_BUSY; + } + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + /* Prepare mailbox for transmission */ + data = min_t(u8, cf->can_dlc, 8); + if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ + data |= HECC_CANMCF_RTR; + data |= get_tx_head_prio(priv) << 8; + hecc_write_mbx(priv, mbxno, HECC_CANMCF, data); + + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + data = (cf->can_id & CAN_EFF_MASK) | HECC_CANMID_IDE; + else /* Standard frame format */ + data = (cf->can_id & CAN_SFF_MASK) << 18; + hecc_write_mbx(priv, mbxno, HECC_CANMID, data); + hecc_write_mbx(priv, mbxno, HECC_CANMDL, + be32_to_cpu(*(u32 *)(cf->data))); + if (cf->can_dlc > 4) + hecc_write_mbx(priv, mbxno, HECC_CANMDH, + be32_to_cpu(*(u32 *)(cf->data + 4))); + else + *(u32 *)(cf->data + 4) = 0; + can_put_echo_skb(skb, ndev, mbxno); + + spin_lock_irqsave(&priv->mbx_lock, flags); + --priv->tx_head; + if ((hecc_read(priv, HECC_CANME) & BIT(get_tx_head_mb(priv))) || + (priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK) { + netif_stop_queue(ndev); + } + hecc_set_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + hecc_clear_bit(priv, HECC_CANMD, mbx_mask); + hecc_set_bit(priv, HECC_CANMIM, mbx_mask); + hecc_write(priv, HECC_CANTRS, mbx_mask); + + return NETDEV_TX_OK; +} + +static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 data, mbx_mask; + unsigned long flags; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->ndev->dev.parent, + "ti_hecc_rx_pkt: alloc_can_skb() failed\n"); + return -ENOMEM; + } + + mbx_mask = BIT(mbxno); + data = hecc_read_mbx(priv, mbxno, HECC_CANMID); + if (data & HECC_CANMID_IDE) + cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (data >> 18) & CAN_SFF_MASK; + data = hecc_read_mbx(priv, mbxno, HECC_CANMCF); + if (data & HECC_CANMCF_RTR) + cf->can_id |= CAN_RTR_FLAG; + cf->can_dlc = data & 0xF; + data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); + *(u32 *)(cf->data) = cpu_to_be32(data); + if (cf->can_dlc > 4) { + data = hecc_read_mbx(priv, mbxno, HECC_CANMDH); + *(u32 *)(cf->data + 4) = cpu_to_be32(data); + } else { + *(u32 *)(cf->data + 4) = 0; + } + spin_lock_irqsave(&priv->mbx_lock, flags); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + hecc_write(priv, HECC_CANRMP, mbx_mask); + /* enable mailbox only if it is part of rx buffer mailboxes */ + if (priv->rx_next < HECC_RX_BUFFER_MBOX) + hecc_set_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + stats->rx_packets++; + + return 0; +} + +/* + * ti_hecc_rx_poll - HECC receive pkts + * + * The receive mailboxes start from highest numbered mailbox till last xmit + * mailbox. On CAN frame reception the hardware places the data into highest + * numbered mailbox that matches the CAN ID filter. Since all receive mailboxes + * have same filtering (ALL CAN frames) packets will arrive in the highest + * available RX mailbox and we need to ensure in-order packet reception. + * + * To ensure the packets are received in the right order we logically divide + * the RX mailboxes into main and buffer mailboxes. Packets are received as per + * mailbox priotity (higher to lower) in the main bank and once it is full we + * disable further reception into main mailboxes. While the main mailboxes are + * processed in NAPI, further packets are received in buffer mailboxes. + * + * We maintain a RX next mailbox counter to process packets and once all main + * mailboxe packets are passed to the upper stack we enable all of them but + * continue to process packets received in buffer mailboxes. With each packet + * received from buffer mailbox we enable it immediately so as to handle the + * overflow from higher mailboxes. + */ +static int ti_hecc_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct ti_hecc_priv *priv = netdev_priv(ndev); + u32 num_pkts = 0; + u32 mbx_mask; + unsigned long pending_pkts, flags; + + if (!netif_running(ndev)) + return 0; + + while ((pending_pkts = hecc_read(priv, HECC_CANRMP)) && + num_pkts < quota) { + mbx_mask = BIT(priv->rx_next); /* next rx mailbox to process */ + if (mbx_mask & pending_pkts) { + if (ti_hecc_rx_pkt(priv, priv->rx_next) < 0) + return num_pkts; + ++num_pkts; + } else if (priv->rx_next > HECC_RX_BUFFER_MBOX) { + break; /* pkt not received yet */ + } + --priv->rx_next; + if (priv->rx_next == HECC_RX_BUFFER_MBOX) { + /* enable high bank mailboxes */ + spin_lock_irqsave(&priv->mbx_lock, flags); + mbx_mask = hecc_read(priv, HECC_CANME); + mbx_mask |= HECC_RX_HIGH_MBOX_MASK; + hecc_write(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + } else if (priv->rx_next == HECC_MAX_TX_MBOX - 1) { + priv->rx_next = HECC_RX_FIRST_MBOX; + break; + } + } + + /* Enable packet interrupt if all pkts are handled */ + if (hecc_read(priv, HECC_CANRMP) == 0) { + napi_complete(napi); + /* Re-enable RX mailbox interrupts */ + mbx_mask = hecc_read(priv, HECC_CANMIM); + mbx_mask |= HECC_TX_MBOX_MASK; + hecc_write(priv, HECC_CANMIM, mbx_mask); + } + + return num_pkts; +} + +static int ti_hecc_error(struct net_device *ndev, int int_status, + int err_status) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* propogate the error condition to the can stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->ndev->dev.parent, + "ti_hecc_error: alloc_can_err_skb() failed\n"); + return -ENOMEM; + } + + if (int_status & HECC_CANGIF_WLIF) { /* warning level int */ + if ((int_status & HECC_CANGIF_BOIF) == 0) { + priv->can.state = CAN_STATE_ERROR_WARNING; + ++priv->can.can_stats.error_warning; + cf->can_id |= CAN_ERR_CRTL; + if (hecc_read(priv, HECC_CANTEC) > 96) + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; + if (hecc_read(priv, HECC_CANREC) > 96) + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; + } + hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW); + dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n"); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + } + + if (int_status & HECC_CANGIF_EPIF) { /* error passive int */ + if ((int_status & HECC_CANGIF_BOIF) == 0) { + priv->can.state = CAN_STATE_ERROR_PASSIVE; + ++priv->can.can_stats.error_passive; + cf->can_id |= CAN_ERR_CRTL; + if (hecc_read(priv, HECC_CANTEC) > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (hecc_read(priv, HECC_CANREC) > 127) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + } + hecc_set_bit(priv, HECC_CANES, HECC_CANES_EP); + dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + } + + /* + * Need to check busoff condition in error status register too to + * ensure warning interrupts don't hog the system + */ + if ((int_status & HECC_CANGIF_BOIF) || (err_status & HECC_CANES_BO)) { + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + hecc_set_bit(priv, HECC_CANES, HECC_CANES_BO); + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); + /* Disable all interrupts in bus-off to avoid int hog */ + hecc_write(priv, HECC_CANGIM, 0); + can_bus_off(ndev); + } + + if (err_status & HECC_BUS_ERROR) { + ++priv->can.can_stats.bus_error; + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + if (err_status & HECC_CANES_FE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_FE); + cf->data[2] |= CAN_ERR_PROT_FORM; + } + if (err_status & HECC_CANES_BE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_BE); + cf->data[2] |= CAN_ERR_PROT_BIT; + } + if (err_status & HECC_CANES_SE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_SE); + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + if (err_status & HECC_CANES_CRCE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE); + cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL; + } + if (err_status & HECC_CANES_ACKE) { + hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE); + cf->data[2] |= CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL; + } + } + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct ti_hecc_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 mbxno, mbx_mask, int_status, err_status; + unsigned long ack, flags; + + int_status = hecc_read(priv, + (priv->int_line) ? HECC_CANGIF1 : HECC_CANGIF0); + + if (!int_status) + return IRQ_NONE; + + err_status = hecc_read(priv, HECC_CANES); + if (err_status & (HECC_BUS_ERROR | HECC_CANES_BO | + HECC_CANES_EP | HECC_CANES_EW)) + ti_hecc_error(ndev, int_status, err_status); + + if (int_status & HECC_CANGIF_GMIF) { + while (priv->tx_tail - priv->tx_head > 0) { + mbxno = get_tx_tail_mb(priv); + mbx_mask = BIT(mbxno); + if (!(mbx_mask & hecc_read(priv, HECC_CANTA))) + break; + hecc_clear_bit(priv, HECC_CANMIM, mbx_mask); + hecc_write(priv, HECC_CANTA, mbx_mask); + spin_lock_irqsave(&priv->mbx_lock, flags); + hecc_clear_bit(priv, HECC_CANME, mbx_mask); + spin_unlock_irqrestore(&priv->mbx_lock, flags); + stats->tx_bytes += hecc_read_mbx(priv, mbxno, + HECC_CANMCF) & 0xF; + stats->tx_packets++; + can_get_echo_skb(ndev, mbxno); + --priv->tx_tail; + } + + /* restart queue if wrap-up or if queue stalled on last pkt */ + if (((priv->tx_head == priv->tx_tail) && + ((priv->tx_head & HECC_TX_MASK) != HECC_TX_MASK)) || + (((priv->tx_tail & HECC_TX_MASK) == HECC_TX_MASK) && + ((priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK))) + netif_wake_queue(ndev); + + /* Disable RX mailbox interrupts and let NAPI reenable them */ + if (hecc_read(priv, HECC_CANRMP)) { + ack = hecc_read(priv, HECC_CANMIM); + ack &= BIT(HECC_MAX_TX_MBOX) - 1; + hecc_write(priv, HECC_CANMIM, ack); + napi_schedule(&priv->napi); + } + } + + /* clear all interrupt conditions - read back to avoid spurious ints */ + if (priv->int_line) { + hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); + int_status = hecc_read(priv, HECC_CANGIF1); + } else { + hecc_write(priv, HECC_CANGIF0, HECC_SET_REG); + int_status = hecc_read(priv, HECC_CANGIF0); + } + + return IRQ_HANDLED; +} + +static int ti_hecc_open(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + int err; + + err = request_irq(ndev->irq, ti_hecc_interrupt, IRQF_SHARED, + ndev->name, ndev); + if (err) { + dev_err(ndev->dev.parent, "error requesting interrupt\n"); + return err; + } + + /* Open common can device */ + err = open_candev(ndev); + if (err) { + dev_err(ndev->dev.parent, "open_candev() failed %d\n", err); + free_irq(ndev->irq, ndev); + return err; + } + + ti_hecc_start(ndev); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +} + +static int ti_hecc_close(struct net_device *ndev) +{ + struct ti_hecc_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + ti_hecc_stop(ndev); + free_irq(ndev->irq, ndev); + close_candev(ndev); + + return 0; +} + +static const struct net_device_ops ti_hecc_netdev_ops = { + .ndo_open = ti_hecc_open, + .ndo_stop = ti_hecc_close, + .ndo_start_xmit = ti_hecc_xmit, +}; + +static int ti_hecc_probe(struct platform_device *pdev) +{ + struct net_device *ndev = (struct net_device *)0; + struct ti_hecc_priv *priv; + struct ti_hecc_platform_data *pdata; + struct resource *mem, *irq; + void __iomem *addr; + int err = -ENODEV; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data\n"); + goto probe_exit; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No mem resources\n"); + goto probe_exit; + } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No irq resource\n"); + goto probe_exit; + } + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { + dev_err(&pdev->dev, "HECC region already claimed\n"); + err = -EBUSY; + goto probe_exit; + } + addr = ioremap(mem->start, resource_size(mem)); + if (!addr) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -ENOMEM; + goto probe_exit_free_region; + } + + ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + err = -ENOMEM; + goto probe_exit_iounmap; + } + + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->base = addr; + priv->scc_ram_offset = pdata->scc_ram_offset; + priv->hecc_ram_offset = pdata->hecc_ram_offset; + priv->mbx_offset = pdata->mbx_offset; + priv->int_line = pdata->int_line; + + priv->can.bittiming_const = &ti_hecc_bittiming_const; + priv->can.do_set_mode = ti_hecc_do_set_mode; + priv->can.do_get_state = ti_hecc_get_state; + + ndev->irq = irq->start; + ndev->flags |= IFF_ECHO; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &ti_hecc_netdev_ops; + + priv->clk = clk_get(&pdev->dev, "hecc_ck"); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "No clock available\n"); + err = PTR_ERR(priv->clk); + priv->clk = NULL; + goto probe_exit_candev; + } + priv->can.clock.freq = clk_get_rate(priv->clk); + netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll, + HECC_DEF_NAPI_WEIGHT); + + clk_enable(priv->clk); + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + goto probe_exit_clk; + } + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n", + priv->base, (u32) ndev->irq); + + return 0; + +probe_exit_clk: + clk_put(priv->clk); +probe_exit_candev: + free_candev(ndev); +probe_exit_iounmap: + iounmap(addr); +probe_exit_free_region: + release_mem_region(mem->start, resource_size(mem)); +probe_exit: + return err; +} + +static int __devexit ti_hecc_remove(struct platform_device *pdev) +{ + struct resource *res; + struct net_device *ndev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(ndev); + + clk_disable(priv->clk); + clk_put(priv->clk); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(priv->base); + release_mem_region(res->start, resource_size(res)); + unregister_candev(ndev); + free_candev(ndev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + + +#ifdef CONFIG_PM +static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->clk); + + return 0; +} + +static int ti_hecc_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + clk_enable(priv->clk); + + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#else +#define ti_hecc_suspend NULL +#define ti_hecc_resume NULL +#endif + +/* TI HECC netdevice driver: platform driver structure */ +static struct platform_driver ti_hecc_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ti_hecc_probe, + .remove = __devexit_p(ti_hecc_remove), + .suspend = ti_hecc_suspend, + .resume = ti_hecc_resume, +}; + +static int __init ti_hecc_init_driver(void) +{ + printk(KERN_INFO DRV_DESC "\n"); + return platform_driver_register(&ti_hecc_driver); +} + +static void __exit ti_hecc_exit_driver(void) +{ + printk(KERN_INFO DRV_DESC " unloaded\n"); + platform_driver_unregister(&ti_hecc_driver); +} + +module_exit(ti_hecc_exit_driver); +module_init(ti_hecc_init_driver); + +MODULE_AUTHOR("Anant Gole "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/usb/musb/am3517.c b/drivers/usb/musb/am3517.c new file mode 100644 index 00000000000..8b8648d6e1b --- /dev/null +++ b/drivers/usb/musb/am3517.c @@ -0,0 +1,968 @@ +/* + * Texas Instruments AM3517 "glue layer" + * + * Copyright (c) 2008, MontaVista Software, Inc. + * + * Based on the DaVinci "glue layer" code. + * Copyright (C) 2005-2006 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux 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. + * + * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include +#include +#include "cppi41.h" + +#include "musb_core.h" +#include "cppi41_dma.h" + +struct musb *g_musb; +/* + * AM3517 specific definitions + */ + +/* CPPI 4.1 queue manager registers */ +#define QMGR_PEND0_REG 0x4090 +#define QMGR_PEND1_REG 0x4094 +#define QMGR_PEND2_REG 0x4098 + +/* USB 2.0 PHY Control */ +#define CONF2_PHY_GPIOMODE (1 << 23) +#define CONF2_OTGMODE (3 << 14) +#define CONF2_SESENDEN (1 << 13) /* Vsess_end comparator */ +#define CONF2_VBDTCTEN (1 << 12) /* Vbus comparator */ +#define CONF2_REFFREQ_24MHZ (2 << 8) +#define CONF2_REFFREQ_26MHZ (7 << 8) +#define CONF2_REFFREQ_13MHZ (6 << 8) +#define CONF2_REFFREQ (0xf << 8) +#define CONF2_PHYCLKGD (1 << 7) +#define CONF2_VBUSSENSE (1 << 6) +#define CONF2_PHY_PLLON (1 << 5) /* override PLL suspend */ +#define CONF2_RESET (1 << 4) +#define CONF2_PHYPWRDN (1 << 3) +#define CONF2_OTGPWRDN (1 << 2) +#define CONF2_DATPOL (1 << 1) + + +#define AM3517_TX_EP_MASK 0xffff /* EP0 + 15 Tx EPs */ +#define AM3517_RX_EP_MASK 0xfffe /* 15 Rx EPs */ + +#define AM3517_TX_INTR_MASK (AM3517_TX_EP_MASK << USB_INTR_TX_SHIFT) +#define AM3517_RX_INTR_MASK (AM3517_RX_EP_MASK << USB_INTR_RX_SHIFT) + +#define A_WAIT_BCON_TIMEOUT 1100 /* in ms */ + +/* AM3517 specific read/write functions */ +u16 musb_readw(const void __iomem *addr, unsigned offset) +{ + u32 tmp; + u16 val; + + if (addr == g_musb->mregs) { + + switch (offset) { + case MUSB_INTRTXE: + if (g_musb->read_mask & AM3517_READ_ISSUE_INTRTXE) + return g_musb->intrtxe; + case MUSB_INTRRXE: + if (g_musb->read_mask & AM3517_READ_ISSUE_INTRRXE) + return g_musb->intrrxe; + default: + break; + } + } + + tmp = __raw_readl(addr + (offset & ~3)); + + switch (offset & 0x3) { + case 0: + val = (tmp & 0xffff); + break; + case 1: + val = (tmp >> 8) & 0xffff; + break; + case 2: + case 3: + default: + val = (tmp >> 16) & 0xffff; + break; + } + return val; +} +void musb_writew(void __iomem *addr, unsigned offset, u16 data) +{ + if (addr == g_musb->mregs) { + + switch (offset) { + case MUSB_INTRTXE: + g_musb->read_mask |= AM3517_READ_ISSUE_INTRTXE; + g_musb->intrtxe = data; + break; + case MUSB_INTRRXE: + g_musb->read_mask |= AM3517_READ_ISSUE_INTRRXE; + g_musb->intrrxe = data; + default: + break; + } + } + + __raw_writew(data, addr + offset); +} +u8 musb_readb(const void __iomem *addr, unsigned offset) +{ + u32 tmp; + u8 val; + + if (addr == g_musb->mregs) { + + switch (offset) { + case MUSB_FADDR: + if (g_musb->read_mask & AM3517_READ_ISSUE_FADDR) + return g_musb->faddr; + case MUSB_POWER: + if (g_musb->read_mask & AM3517_READ_ISSUE_POWER) { + return g_musb->power; + } else { + tmp = __raw_readl(addr); + val = (tmp >> 8); + if (tmp & 0xffff0000) { + DBG(2, "Missing Tx interrupt\ + event = 0x%x\n", (u16)\ + ((tmp & 0xffff0000) >> 16)); + } + g_musb->power = val; + g_musb->read_mask |= AM3517_READ_ISSUE_POWER; + return val; + } + case MUSB_INTRUSBE: + if (g_musb->read_mask & AM3517_READ_ISSUE_INTRUSBE) + return g_musb->intrusbe; + default: + break; + } + } + + tmp = __raw_readl(addr + (offset & ~3)); + + switch (offset & 0x3) { + case 0: + val = tmp & 0xff; + break; + case 1: + val = (tmp >> 8); + break; + case 2: + val = (tmp >> 16); + break; + case 3: + default: + val = (tmp >> 24); + break; + } + return val; +} +void musb_writeb(void __iomem *addr, unsigned offset, u8 data) +{ + if (addr == g_musb->mregs) { + + switch (offset) { + case MUSB_FADDR: + g_musb->read_mask |= AM3517_READ_ISSUE_FADDR; + g_musb->faddr = data; + break; + case MUSB_POWER: + g_musb->read_mask |= AM3517_READ_ISSUE_POWER; + g_musb->power = data; + break; + case MUSB_INTRUSBE: + g_musb->read_mask |= AM3517_READ_ISSUE_INTRUSBE; + g_musb->intrusbe = data; + default: + break; + } + } + + __raw_writeb(data, addr + offset); +} +#ifdef CONFIG_USB_TI_CPPI41_DMA + +/* + * CPPI 4.1 resources used for USB OTG controller module: + * + * USB DMA DMA QMgr Tx Src + * Tx Rx QNum Port + * --------------------------------- + * EP0 0 0 0 16,17 1 + * --------------------------------- + * EP1 1 1 0 18,19 2 + * --------------------------------- + * EP2 2 2 0 20,21 3 + * --------------------------------- + * EP3 3 3 0 22,23 4 + * --------------------------------- + */ + +static const u16 tx_comp_q[] = { 63, 64 }; +static const u16 rx_comp_q[] = { 65, 66 }; + +const struct usb_cppi41_info usb_cppi41_info = { + .dma_block = 0, + .ep_dma_ch = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, + .q_mgr = 0, + .num_tx_comp_q = 2, + .num_rx_comp_q = 2, + .tx_comp_q = tx_comp_q, + .rx_comp_q = rx_comp_q +}; + +/* Fair scheduling */ +u32 dma_sched_table[] = { + 0x81018000, 0x83038202, 0x85058404, 0x87078606, + 0x89098808, 0x8b0b8a0a, 0x8d0d8c0c, 0x00008e0e +}; + +/* DMA block configuration */ +static const struct cppi41_tx_ch tx_ch_info[] = { + [0] = { + .port_num = 1, + .num_tx_queue = 2, + .tx_queue = { {0, 32} , {0, 33} } + }, + [1] = { + .port_num = 2, + .num_tx_queue = 2, + .tx_queue = { {0, 34} , {0, 35} } + }, + [2] = { + .port_num = 3, + .num_tx_queue = 2, + .tx_queue = { {0, 36} , {0, 37} } + }, + [3] = { + .port_num = 4, + .num_tx_queue = 2, + .tx_queue = { {0, 38} , {0, 39} } + }, + [4] = { + .port_num = 5, + .num_tx_queue = 2, + .tx_queue = { {0, 40} , {0, 41} } + }, + [5] = { + .port_num = 6, + .num_tx_queue = 2, + .tx_queue = { {0, 42} , {0, 43} } + }, + [6] = { + .port_num = 7, + .num_tx_queue = 2, + .tx_queue = { {0, 44} , {0, 45} } + }, + [7] = { + .port_num = 8, + .num_tx_queue = 2, + .tx_queue = { {0, 46} , {0, 47} } + }, + [8] = { + .port_num = 9, + .num_tx_queue = 2, + .tx_queue = { {0, 48} , {0, 49} } + }, + [9] = { + .port_num = 10, + .num_tx_queue = 2, + .tx_queue = { {0, 50} , {0, 51} } + }, + [10] = { + .port_num = 11, + .num_tx_queue = 2, + .tx_queue = { {0, 52} , {0, 53} } + }, + [11] = { + .port_num = 12, + .num_tx_queue = 2, + .tx_queue = { {0, 54} , {0, 55} } + }, + [12] = { + .port_num = 13, + .num_tx_queue = 2, + .tx_queue = { {0, 56} , {0, 57} } + }, + [13] = { + .port_num = 14, + .num_tx_queue = 2, + .tx_queue = { {0, 58} , {0, 59} } + }, + [14] = { + .port_num = 15, + .num_tx_queue = 2, + .tx_queue = { {0, 60} , {0, 61} } + } +}; + +struct cppi41_dma_block cppi41_dma_block[CPPI41_NUM_DMA_BLOCK] = { + [0] = { + .num_tx_ch = 15, + .num_rx_ch = 15, + .tx_ch_info = tx_ch_info + } +}; +EXPORT_SYMBOL(cppi41_dma_block); + +/* Queues 0 to 66 are pre-assigned, others are spare */ +static const u32 assigned_queues[] = { 0xffffffff, 0xffffffff, 0x7 }; + +/* Queue manager information */ +struct cppi41_queue_mgr cppi41_queue_mgr[CPPI41_NUM_QUEUE_MGR] = { + [0] = { + .num_queue = 96, + .queue_types = CPPI41_FREE_DESC_BUF_QUEUE | + CPPI41_UNASSIGNED_QUEUE, + .base_fdbq_num = 0, + .assigned = assigned_queues + } +}; +EXPORT_SYMBOL(cppi41_queue_mgr); + +int __init cppi41_init(struct musb *musb) +{ + u16 numch, blknum = usb_cppi41_info.dma_block, order; + + /* init mappings */ + cppi41_queue_mgr[0].q_mgr_rgn_base = musb->ctrl_base + 0x4000; + cppi41_queue_mgr[0].desc_mem_rgn_base = musb->ctrl_base + 0x5000; + cppi41_queue_mgr[0].q_mgmt_rgn_base = musb->ctrl_base + 0x6000; + cppi41_queue_mgr[0].q_stat_rgn_base = musb->ctrl_base + 0x6800; + + cppi41_dma_block[0].global_ctrl_base = musb->ctrl_base + 0x1000; + cppi41_dma_block[0].ch_ctrl_stat_base = musb->ctrl_base + 0x1800; + cppi41_dma_block[0].sched_ctrl_base = musb->ctrl_base + 0x2000; + cppi41_dma_block[0].sched_table_base = musb->ctrl_base + 0x2800; + + /* Initialize for Linking RAM region 0 alone */ + cppi41_queue_mgr_init(usb_cppi41_info.q_mgr, 0, 0x3fff); + + numch = USB_CPPI41_NUM_CH * 2; + order = get_count_order(numch); + + /* TODO: check two teardown desc per channel (5 or 7 ?)*/ + if (order < 5) + order = 5; + + cppi41_dma_block_init(blknum, usb_cppi41_info.q_mgr, order, + dma_sched_table, numch); + return 0; +} + +#endif /* CONFIG_USB_TI_CPPI41_DMA */ + +#ifdef CONFIG_USB_TI_CPPI41_DMA +int cppi41_disable_sched_rx(void) +{ + u16 numch = 7, blknum = usb_cppi41_info.dma_block; + + dma_sched_table[0] = 0x02810100; + dma_sched_table[1] = 0x830382; + + cppi41_dma_sched_tbl_init(blknum, usb_cppi41_info.q_mgr, + dma_sched_table, numch); + return 0; +} + +int cppi41_enable_sched_rx(void) +{ + u16 numch = 8, blknum = usb_cppi41_info.dma_block; + + dma_sched_table[0] = 0x81018000; + dma_sched_table[1] = 0x83038202; + + cppi41_dma_sched_tbl_init(blknum, usb_cppi41_info.q_mgr, + dma_sched_table, numch); + return 0; +} +#endif + +/* + * REVISIT (PM): we should be able to keep the PHY in low power mode most + * of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0 + * and, when in host mode, autosuspending idle root ports... PHYPLLON + * (overriding SUSPENDM?) then likely needs to stay off. + */ + +static inline void phy_on(void) +{ + u32 devconf2; + + /* + * Start the on-chip PHY and its PLL. + */ + devconf2 = omap_ctrl_readl(OMAP3517_CONTROL_DEVCONF2); + + devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN | + CONF2_OTGMODE | CONF2_REFFREQ | CONF2_PHY_GPIOMODE); + devconf2 |= CONF2_SESENDEN | CONF2_VBDTCTEN | CONF2_PHY_PLLON | + CONF2_REFFREQ_13MHZ | CONF2_DATPOL; + + omap_ctrl_writel(devconf2, OMAP3517_CONTROL_DEVCONF2); + + DBG(1, "Waiting for PHY clock good...\n"); + while (!(omap_ctrl_readl(OMAP3517_CONTROL_DEVCONF2) + & CONF2_PHYCLKGD)) + cpu_relax(); +} + +static inline void phy_off(void) +{ + u32 devconf2; + + /* + * Power down the on-chip PHY. + */ + devconf2 = omap_ctrl_readl(OMAP3517_CONTROL_DEVCONF2); + + devconf2 &= ~CONF2_PHY_PLLON; + devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN; + omap_ctrl_writel(devconf2, OMAP3517_CONTROL_DEVCONF2); +} + +/* + * Because we don't set CTRL.UINT, it's "important" to: + * - not read/write INTRUSB/INTRUSBE (except during + * initial setup, as a workaround); + * - use INTSET/INTCLR instead. + */ + +/** + * musb_platform_enable - enable interrupts + */ +void musb_platform_enable(struct musb *musb) +{ + void __iomem *reg_base = musb->ctrl_base; + u32 epmask, coremask; + + /* Workaround: setup IRQs through both register sets. */ + epmask = ((musb->epmask & AM3517_TX_EP_MASK) << USB_INTR_TX_SHIFT) | + ((musb->epmask & AM3517_RX_EP_MASK) << USB_INTR_RX_SHIFT); + coremask = (0x01ff << USB_INTR_USB_SHIFT); + + musb_writel(reg_base, EP_INTR_MASK_SET_REG, epmask); + musb_writel(reg_base, CORE_INTR_MASK_SET_REG, coremask); + + /* Force the DRVVBUS IRQ so we can start polling for ID change. */ + if (is_otg_enabled(musb)) + musb_writel(reg_base, CORE_INTR_SRC_SET_REG, + USB_INTR_DRVVBUS << USB_INTR_USB_SHIFT); +} + +/** + * musb_platform_disable - disable HDRC and flush interrupts + */ +void musb_platform_disable(struct musb *musb) +{ + void __iomem *reg_base = musb->ctrl_base; + + musb_writel(reg_base, CORE_INTR_MASK_CLEAR_REG, USB_INTR_USB_MASK); + musb_writel(reg_base, EP_INTR_MASK_CLEAR_REG, + AM3517_TX_INTR_MASK | AM3517_RX_INTR_MASK); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_writel(reg_base, USB_END_OF_INTR_REG, 0); +} + +/* REVISIT: it's not clear whether AM3517 can support full OTG. */ + +static int vbus_state = -1; + +#ifdef CONFIG_USB_MUSB_HDRC_HCD +#define portstate(stmt) stmt +#else +#define portstate(stmt) +#endif + +static void am3517_source_power(struct musb *musb, int is_on, int immediate) +{ + if (is_on) + is_on = 1; + + if (vbus_state == is_on) + return; + vbus_state = is_on; /* 0/1 vs "-1 == unknown/init" */ +} + +static void am3517_set_vbus(struct musb *musb, int is_on) +{ + WARN_ON(is_on && is_peripheral_active(musb)); + am3517_source_power(musb, is_on, 0); +} + +#define POLL_SECONDS 2 + +static struct timer_list otg_workaround; + +static void otg_timer(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + void __iomem *mregs = musb->mregs; + u8 devctl; + unsigned long flags; + + /* We poll because DaVinci's won't expose several OTG-critical + * status change events (from the transceiver) otherwise. + */ + devctl = musb_readb(mregs, MUSB_DEVCTL); + DBG(7, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv->state) { + case OTG_STATE_A_WAIT_BCON: + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv->state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + } else { + musb->xceiv->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + } + break; + case OTG_STATE_A_WAIT_VFALL: + /* + * Wait till VBUS falls below SessionEnd (~0.2 V); the 1.3 + * RTL seems to mis-handle session "start" otherwise (or in + * our case "recover"), in routine "VBUS was valid by the time + * VBUSERR got reported during enumeration" cases. + */ + if (devctl & MUSB_DEVCTL_VBUS) { + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + break; + } + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + musb_writel(musb->ctrl_base, CORE_INTR_SRC_SET_REG, + MUSB_INTR_VBUSERROR << USB_INTR_USB_SHIFT); + break; + case OTG_STATE_B_IDLE: + if (!is_peripheral_enabled(musb)) + break; + + /* + * There's no ID-changed IRQ, so we have no good way to tell + * when to switch to the A-Default state machine (by setting + * the DEVCTL.SESSION flag). + * + * Workaround: whenever we're in B_IDLE, try setting the + * session flag every few seconds. If it works, ID was + * grounded and we're now in the A-Default state machine. + * + * NOTE: setting the session flag is _supposed_ to trigger + * SRP but clearly it doesn't. + */ + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + else + musb->xceiv->state = OTG_STATE_A_IDLE; + break; + default: + break; + } + spin_unlock_irqrestore(&musb->lock, flags); +} + +void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +{ + static unsigned long last_timer; + + if (!is_otg_enabled(musb)) + return; + + if (timeout == 0) + timeout = jiffies + msecs_to_jiffies(3); + + /* Never idle if active, or when VBUS timeout is not set as host */ + if (musb->is_active || (musb->a_wait_bcon == 0 && + musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { + DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + del_timer(&otg_workaround); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { + DBG(4, "Longer idle timer already pending, ignoring...\n"); + return; + } + last_timer = timeout; + + DBG(4, "%s inactive, starting idle timer for %u ms\n", + otg_state_string(musb), jiffies_to_msecs(timeout - jiffies)); + mod_timer(&otg_workaround, timeout); +} + +static irqreturn_t am3517_interrupt(int irq, void *hci) +{ + struct musb *musb = hci; + void __iomem *reg_base = musb->ctrl_base; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + u32 pend1 = 0, pend2 = 0, tx, rx; + u32 epintr, usbintr, lvl_intr; + + spin_lock_irqsave(&musb->lock, flags); + + /* + * NOTE: AM3517 shadows the Mentor IRQs. Don't manage them through + * the Mentor registers (except for setup), use the TI ones and EOI. + */ + + /* + * CPPI 4.1 interrupts share the same IRQ and the EOI register but + * don't get reflected in the interrupt source/mask registers. + */ + if (is_cppi41_enabled()) { + /* + * Check for the interrupts from Tx/Rx completion queues; they + * are level-triggered and will stay asserted until the queues + * are emptied. We're using the queue pending register 0 as a + * substitute for the interrupt status register and reading it + * directly for speed. + */ + pend1 = musb_readl(reg_base, QMGR_PEND1_REG); + pend2 = musb_readl(reg_base, QMGR_PEND2_REG); + + /* AM3517 uses 63,64,65 and 66 queues as completion queue */ + if ((pend1 & (1 << 31)) || (pend2 & (7 << 0))) { + tx = (pend1 >> 31) | ((pend2 & 1) ? (1 << 1) : 0); + rx = (pend2 >> 1) & 0x3; + + DBG(4, "CPPI 4.1 IRQ: Tx %x, Rx %x\n", tx, rx); + cppi41_completion(musb, rx, tx); + ret = IRQ_HANDLED; + } + + /* TODO: check if this is required */ + /* handle the undocumented starvation interrupt bit:28 */ + /* if(pend0 & 0x10000000) + ret = IRQ_HANDLED; + */ + } + + /* Acknowledge and handle non-CPPI interrupts */ + /* Get endpoint interrupts */ + epintr = musb_readl(reg_base, EP_INTR_SRC_MASKED_REG); + + if (epintr) { + musb_writel(reg_base, EP_INTR_SRC_CLEAR_REG, epintr); + + musb->int_rx = + (epintr & AM3517_RX_INTR_MASK) >> USB_INTR_RX_SHIFT; + musb->int_tx = + (epintr & AM3517_TX_INTR_MASK) >> USB_INTR_TX_SHIFT; + } + + /* Get usb core interrupts */ + usbintr = musb_readl(reg_base, CORE_INTR_SRC_MASKED_REG); + if (!usbintr && !epintr) + goto eoi; + + if (usbintr) { + musb_writel(reg_base, CORE_INTR_SRC_CLEAR_REG, usbintr); + + musb->int_usb = + (usbintr & USB_INTR_USB_MASK) >> USB_INTR_USB_SHIFT; + /* musb->int_regs = regs; */ + } + /* + * DRVVBUS IRQs are the only proxy we have (a very poor one!) for + * AM3517's missing ID change IRQ. We need an ID change IRQ to + * switch appropriately between halves of the OTG state machine. + * Managing DEVCTL.SESSION per Mentor docs requires that we know its + * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. + * Also, DRVVBUS pulses for SRP (but not at 5V) ... + */ + if (usbintr & (USB_INTR_DRVVBUS << USB_INTR_USB_SHIFT)) { + int drvvbus = musb_readl(reg_base, USB_STAT_REG); + void __iomem *mregs = musb->mregs; + u8 devctl = musb_readb(mregs, MUSB_DEVCTL); + int err; + + err = is_host_enabled(musb) && (musb->int_usb & + MUSB_INTR_VBUSERROR); + if (err) { + /* + * The Mentor core doesn't debounce VBUS as needed + * to cope with device connect current spikes. This + * means it's not uncommon for bus-powered devices + * to get VBUS errors during enumeration. + * + * This is a workaround, but newer RTL from Mentor + * seems to allow a better one: "re"-starting sessions + * without waiting for VBUS to stop registering in + * devctl. + */ + musb->int_usb &= ~MUSB_INTR_VBUSERROR; + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + WARNING("VBUS error workaround (delay coming)\n"); + } else if (is_host_enabled(musb) && drvvbus) { + musb->is_active = 1; + MUSB_HST_MODE(musb); + musb->xceiv->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + portstate(musb->port1_status |= USB_PORT_STAT_POWER); + del_timer(&otg_workaround); + } else { + musb->is_active = 0; + MUSB_DEV_MODE(musb); + musb->xceiv->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; + portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); + } + + /* NOTE: this must complete power-on within 100 ms. */ + am3517_source_power(musb, drvvbus, 0); + DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + drvvbus ? "on" : "off", + otg_state_string(musb), + err ? " ERROR" : "", + devctl); + ret = IRQ_HANDLED; + } + + if (musb->int_tx || musb->int_rx || musb->int_usb) { + irqreturn_t mret; + + mret = musb_interrupt(musb); + if (mret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + /* musb->int_regs = NULL; */ + + eoi: + /* EOI needs to be written for the IRQ to be re-asserted. */ + if (ret == IRQ_HANDLED || epintr || usbintr) { + /* clear level interrupt */ + lvl_intr = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); + lvl_intr |= AM3517_USBOTG_INT_CLR; + omap_ctrl_writel(lvl_intr, OMAP3517_CONTROL_LVL_INTR_CLEAR); + /* write EOI */ + musb_writel(reg_base, USB_END_OF_INTR_REG, 0); + } + + /* Poll for ID change */ + if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE) + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + + spin_unlock_irqrestore(&musb->lock, flags); + + if (ret != IRQ_HANDLED) { + if (epintr || usbintr) + /* + * We sometimes get unhandled IRQs in the peripheral + * mode from EP0 and SOF... + */ + DBG(2, "Unhandled USB IRQ %08x-%08x\n", + epintr, usbintr); + else if (printk_ratelimit()) + /* + * We've seen series of spurious interrupts in the + * peripheral mode after USB reset and then after some + * time a real interrupt storm starting... + */ + DBG(2, "Spurious IRQ, CPPI 4.1 status %08x, %08x\n", + pend1, pend2); + } + return ret; +} + +int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +{ + /* TODO: implement this using CONF0 */ + WARNING("FIXME: %s not implemented\n", __func__); + return -EIO; +} + +int __init musb_platform_init(struct musb *musb) +{ + void __iomem *reg_base = musb->ctrl_base; + struct clk *otg_fck; + u32 rev, lvl_intr, sw_reset; + + g_musb = musb; + + usb_nop_xceiv_register(); + + musb->xceiv = otg_get_transceiver(); + if (!musb->xceiv) + return -ENODEV; + + /* mentor is at offset of 0x400 in am3517 */ + musb->mregs += USB_MENTOR_CORE_OFFSET; + + /* not required as clock is set in usb-musb.c file in arch */ + /* musb->clock = clk_get(NULL, "usbotg_ck"); */ + if (IS_ERR(musb->clock)) + return PTR_ERR(musb->clock); + + if (musb->set_clock) + musb->set_clock(musb->clock, 1); + else + clk_enable(musb->clock); + + DBG(2, "usbotg_ck=%lud\n", clk_get_rate(musb->clock)); + otg_fck = clk_get(NULL, "usbotg_phy_ck"); + clk_enable(otg_fck); + + DBG(2, "usbotg_phy_ck=%lud\n", clk_get_rate(otg_fck)); + /* Returns zero if e.g. not clocked */ + rev = musb_readl(reg_base, USB_REVISION_REG); + if (!rev) + return -ENODEV; + + if (is_host_enabled(musb)) + setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); + + musb->board_set_vbus = am3517_set_vbus; + am3517_source_power(musb, 0, 1); + +#if 0 /* follow recommended reset procedure */ + /* Reset the controller */ + musb_writel(reg_base, USB_CTRL_REG, USB_SOFT_RESET_MASK); + + /* wait till reset bit clears */ + while ((musb_readl(reg_base, USB_CTRL_REG) & 0x1)) + cpu_relax(); + + /* clock disable */ + clk_disable(musb->clock); + + /* Start the on-chip PHY and its PLL. */ + phy_on(); + + msleep(5); + + /* clock enable */ + clk_enable(musb->clock); + +#else + /* global reset */ + sw_reset = omap_ctrl_readl(OMAP3517_CONTROL_IP_SW_RESET); + + sw_reset |= AM3517_USBOTG_SW_RST; + omap_ctrl_writel(sw_reset, OMAP3517_CONTROL_IP_SW_RESET); + + sw_reset &= ~AM3517_USBOTG_SW_RST; + omap_ctrl_writel(sw_reset, OMAP3517_CONTROL_IP_SW_RESET); + + /* Reset the controller */ + musb_writel(reg_base, USB_CTRL_REG, USB_SOFT_RESET_MASK); + + /* Start the on-chip PHY and its PLL. */ + phy_on(); + + msleep(15); +#endif + +#ifdef CONFIG_USB_TI_CPPI41_DMA + cppi41_init(musb); +#endif + + /* NOTE: IRQs are in mixed mode, not bypass to pure MUSB */ + printk("AM3517 OTG revision %08x, PHY %03x, control %02x\n", + rev, omap_ctrl_readl(OMAP3517_CONTROL_DEVCONF2), + musb_readb(reg_base, USB_CTRL_REG)); + + musb->a_wait_bcon = A_WAIT_BCON_TIMEOUT; + musb->isr = am3517_interrupt; + /* update the HS EOF tiing */ + /* musb_writeb(musb->mregs, 0x7C, 0x40); */ + + /* clear level interrupt */ + lvl_intr = omap_ctrl_readl(OMAP3517_CONTROL_LVL_INTR_CLEAR); + lvl_intr |= AM3517_USBOTG_INT_CLR; + omap_ctrl_writel(lvl_intr, OMAP3517_CONTROL_LVL_INTR_CLEAR); + + return 0; +} + +int musb_platform_exit(struct musb *musb) +{ + if (is_host_enabled(musb)) + del_timer_sync(&otg_workaround); + + am3517_source_power(musb, 0 /* off */, 1); + + /* Delay to avoid problems with module reload... */ + if (is_host_enabled(musb) && musb->xceiv->default_a) { + int maxdelay = 30; + u8 devctl, warn = 0; + + /* + * If there's no peripheral connected, this can take a + * long time to fall... + */ + do { + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (!(devctl & MUSB_DEVCTL_VBUS)) + break; + if ((devctl & MUSB_DEVCTL_VBUS) != warn) { + warn = devctl & MUSB_DEVCTL_VBUS; + DBG(1, "VBUS %d\n", + warn >> MUSB_DEVCTL_VBUS_SHIFT); + } + msleep(1000); + maxdelay--; + } while (maxdelay > 0); + + /* In OTG mode, another host might be connected... */ + if (devctl & MUSB_DEVCTL_VBUS) + DBG(1, "VBUS off timeout (devctl %02x)\n", devctl); + } + + phy_off(); + + usb_nop_xceiv_unregister(); + +#ifdef CONFIG_USB_TI_CPPI41_DMA + cppi41_exit(); +#endif + return 0; +} + +#ifdef CONFIG_PM +void musb_platform_save_context(struct musb_context_registers + *musb_context) +{ + /* Save CPPI41 DMA related registers */ + phy_off(); +} + +void musb_platform_restore_context(struct musb_context_registers + *musb_context) +{ + /* Restore CPPI41 DMA related registers */ + phy_on(); +} +#endif diff --git a/drivers/usb/musb/cppi41.c b/drivers/usb/musb/cppi41.c new file mode 100644 index 00000000000..1ba7b49f195 --- /dev/null +++ b/drivers/usb/musb/cppi41.c @@ -0,0 +1,839 @@ +/* + * CPPI 4.1 support + * + * Copyright (C) 2008-2009 MontaVista Software, Inc. + * + * Based on the PAL CPPI 4.1 implementation + * Copyright (C) 1998-2006 Texas Instruments Incorporated + * + * This file contains the main implementation for CPPI 4.1 common peripherals, + * including the DMA Controllers and the Queue Managers. + * + * This program is free software; you can distribute 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 it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "cppi41.h" + +#undef CPPI41_DEBUG + +#ifdef CPPI41_DEBUG +#define DBG(format, args...) printk(format, ##args) +#else +#define DBG(format, args...) +#endif + +static struct { + void *virt_addr; + dma_addr_t phys_addr; +} linking_ram[CPPI41_NUM_QUEUE_MGR]; + +static u32 *allocated_queues[CPPI41_NUM_QUEUE_MGR]; + +/* First 32 packet descriptors are reserved for unallocated memory regions. */ +static u32 next_desc_index[CPPI41_NUM_QUEUE_MGR] = { 1 << 5 }; +static u8 next_mem_rgn[CPPI41_NUM_QUEUE_MGR]; + +static struct { + size_t rgn_size; + void *virt_addr; + dma_addr_t phys_addr; + struct cppi41_queue_obj queue_obj; + u8 mem_rgn; +} dma_teardown[CPPI41_NUM_DMA_BLOCK]; + +/******************** CPPI 4.1 Functions (External Interface) *****************/ + +int cppi41_queue_mgr_init(u8 q_mgr, dma_addr_t rgn0_base, u16 rgn0_size) +{ + void __iomem *q_mgr_regs; + void *ptr; + + if (q_mgr >= cppi41_num_queue_mgr) + return -EINVAL; + + q_mgr_regs = cppi41_queue_mgr[q_mgr].q_mgr_rgn_base; + ptr = dma_alloc_coherent(NULL, rgn0_size * 4, + &linking_ram[q_mgr].phys_addr, + GFP_KERNEL | GFP_DMA); + if (ptr == NULL) { + printk(KERN_ERR "ERROR: %s: Unable to allocate " + "linking RAM.\n", __func__); + return -ENOMEM; + } + linking_ram[q_mgr].virt_addr = ptr; + + __raw_writel(linking_ram[q_mgr].phys_addr, + q_mgr_regs + QMGR_LINKING_RAM_RGN0_BASE_REG); + DBG("Linking RAM region 0 base @ %p, value: %x\n", + q_mgr_regs + QMGR_LINKING_RAM_RGN0_BASE_REG, + __raw_readl(q_mgr_regs + QMGR_LINKING_RAM_RGN0_BASE_REG)); + + __raw_writel(rgn0_size, q_mgr_regs + QMGR_LINKING_RAM_RGN0_SIZE_REG); + DBG("Linking RAM region 0 size @ %p, value: %x\n", + q_mgr_regs + QMGR_LINKING_RAM_RGN0_SIZE_REG, + __raw_readl(q_mgr_regs + QMGR_LINKING_RAM_RGN0_SIZE_REG)); + + ptr = kzalloc(BITS_TO_LONGS(cppi41_queue_mgr[q_mgr].num_queue), + GFP_KERNEL); + if (ptr == NULL) { + printk(KERN_ERR "ERROR: %s: Unable to allocate queue bitmap.\n", + __func__); + dma_free_coherent(NULL, rgn0_size * 4, + linking_ram[q_mgr].virt_addr, + linking_ram[q_mgr].phys_addr); + return -ENOMEM; + } + allocated_queues[q_mgr] = ptr; + + return 0; +} +EXPORT_SYMBOL(cppi41_queue_mgr_init); + +int cppi41_dma_sched_tbl_init(u8 dma_num, u8 q_mgr, + u32 *sched_tbl, u8 tbl_size) +{ + struct cppi41_dma_block *dma_block; + int num_reg, k, i, val = 0; + + dma_block = (struct cppi41_dma_block *)&cppi41_dma_block[dma_num]; + + num_reg = (tbl_size + 3) / 4; + for (k = i = 0; i < num_reg; i++) { +#if 0 + for (val = j = 0; j < 4; j++, k++) { + val >>= 8; + if (k < tbl_size) + val |= sched_tbl[k] << 24; + } +#endif + val = sched_tbl[i]; + __raw_writel(val, dma_block->sched_table_base + + DMA_SCHED_TABLE_WORD_REG(i)); + DBG("DMA scheduler table @ %p, value written: %x\n", + dma_block->sched_table_base + DMA_SCHED_TABLE_WORD_REG(i), + val); + } + return 0; +} +EXPORT_SYMBOL(cppi41_dma_sched_tbl_init); + +int cppi41_dma_block_init(u8 dma_num, u8 q_mgr, u8 num_order, + u32 *sched_tbl, u8 tbl_size) +{ + const struct cppi41_dma_block *dma_block; + struct cppi41_teardown_desc *curr_td; + dma_addr_t td_addr; + unsigned num_desc, num_reg; + void *ptr; + int error, i; + u16 q_num; + u32 val; + + if (dma_num >= cppi41_num_dma_block || + q_mgr >= cppi41_num_queue_mgr || + !tbl_size || sched_tbl == NULL) + return -EINVAL; + + error = cppi41_queue_alloc(CPPI41_FREE_DESC_QUEUE | + CPPI41_UNASSIGNED_QUEUE, q_mgr, &q_num); + if (error) { + printk(KERN_ERR "ERROR: %s: Unable to allocate teardown " + "descriptor queue.\n", __func__); + return error; + } + DBG("Teardown descriptor queue %d in queue manager 0 " + "allocated\n", q_num); + + /* + * Tell the hardware about the Teardown descriptor + * queue manager and queue number. + */ + dma_block = &cppi41_dma_block[dma_num]; + __raw_writel((q_mgr << DMA_TD_DESC_QMGR_SHIFT) | + (q_num << DMA_TD_DESC_QNUM_SHIFT), + dma_block->global_ctrl_base + + DMA_TEARDOWN_FREE_DESC_CTRL_REG); + DBG("Teardown free descriptor control @ %p, value: %x\n", + dma_block->global_ctrl_base + DMA_TEARDOWN_FREE_DESC_CTRL_REG, + __raw_readl(dma_block->global_ctrl_base + + DMA_TEARDOWN_FREE_DESC_CTRL_REG)); + + num_desc = 1 << num_order; + dma_teardown[dma_num].rgn_size = num_desc * + sizeof(struct cppi41_teardown_desc); + + /* Pre-allocate teardown descriptors. */ + ptr = dma_alloc_coherent(NULL, dma_teardown[dma_num].rgn_size, + &dma_teardown[dma_num].phys_addr, + GFP_KERNEL | GFP_DMA); + if (ptr == NULL) { + printk(KERN_ERR "ERROR: %s: Unable to allocate teardown " + "descriptors.\n", __func__); + error = -ENOMEM; + goto free_queue; + } + dma_teardown[dma_num].virt_addr = ptr; + + error = cppi41_mem_rgn_alloc(q_mgr, dma_teardown[dma_num].phys_addr, 5, + num_order, &dma_teardown[dma_num].mem_rgn); + if (error) { + printk(KERN_ERR "ERROR: %s: Unable to allocate queue manager " + "memory region for teardown descriptors.\n", __func__); + goto free_mem; + } + + error = cppi41_queue_init(&dma_teardown[dma_num].queue_obj, 0, q_num); + if (error) { + printk(KERN_ERR "ERROR: %s: Unable to initialize teardown " + "free descriptor queue.\n", __func__); + goto free_rgn; + } + + /* + * Push all teardown descriptors to the free teardown queue + * for the CPPI 4.1 system. + */ + curr_td = dma_teardown[dma_num].virt_addr; + td_addr = dma_teardown[dma_num].phys_addr; + + for (i = 0; i < num_desc; i++) { + cppi41_queue_push(&dma_teardown[dma_num].queue_obj, td_addr, + sizeof(*curr_td), 0); + td_addr += sizeof(*curr_td); + } + + /* Initialize the DMA scheduler. */ + num_reg = (tbl_size + 3) / 4; + for (i = 0; i < num_reg; i++) { + val = sched_tbl[i]; + __raw_writel(val, dma_block->sched_table_base + + DMA_SCHED_TABLE_WORD_REG(i)); + DBG("DMA scheduler table @ %p, value written: %x\n", + dma_block->sched_table_base + DMA_SCHED_TABLE_WORD_REG(i), + val); + } + + __raw_writel((tbl_size - 1) << DMA_SCHED_LAST_ENTRY_SHIFT | + DMA_SCHED_ENABLE_MASK, + dma_block->sched_ctrl_base + DMA_SCHED_CTRL_REG); + DBG("DMA scheduler control @ %p, value: %x\n", + dma_block->sched_ctrl_base + DMA_SCHED_CTRL_REG, + __raw_readl(dma_block->sched_ctrl_base + DMA_SCHED_CTRL_REG)); + + return 0; + +free_rgn: + cppi41_mem_rgn_free(q_mgr, dma_teardown[dma_num].mem_rgn); +free_mem: + dma_free_coherent(NULL, dma_teardown[dma_num].rgn_size, + dma_teardown[dma_num].virt_addr, + dma_teardown[dma_num].phys_addr); +free_queue: + cppi41_queue_free(q_mgr, q_num); + return error; +} +EXPORT_SYMBOL(cppi41_dma_block_init); + +/* + * cppi41_mem_rgn_alloc - allocate a memory region within the queue manager + */ +int cppi41_mem_rgn_alloc(u8 q_mgr, dma_addr_t rgn_addr, u8 size_order, + u8 num_order, u8 *mem_rgn) +{ + void __iomem *desc_mem_regs; + u32 num_desc = 1 << num_order, index, ctrl; + int rgn; + + DBG("%s called with rgn_addr = %08x, size_order = %d, num_order = %d\n", + __func__, rgn_addr, size_order, num_order); + + if (q_mgr >= cppi41_num_queue_mgr || + size_order < 5 || size_order > 13 || + num_order < 5 || num_order > 12 || + (rgn_addr & ((1 << size_order) - 1))) + return -EINVAL; + + rgn = next_mem_rgn[q_mgr]; + index = next_desc_index[q_mgr]; + if (rgn >= CPPI41_MAX_MEM_RGN || index + num_desc > 0x4000) + return -ENOSPC; + + next_mem_rgn[q_mgr] = rgn + 1; + next_desc_index[q_mgr] = index + num_desc; + + desc_mem_regs = cppi41_queue_mgr[q_mgr].desc_mem_rgn_base; + + /* Write the base register */ + __raw_writel(rgn_addr, desc_mem_regs + QMGR_MEM_RGN_BASE_REG(rgn)); + DBG("Descriptor region base @ %p, value: %x\n", + desc_mem_regs + QMGR_MEM_RGN_BASE_REG(rgn), + __raw_readl(desc_mem_regs + QMGR_MEM_RGN_BASE_REG(rgn))); + + /* Write the control register */ + ctrl = ((index << QMGR_MEM_RGN_INDEX_SHIFT) & + QMGR_MEM_RGN_INDEX_MASK) | + (((size_order - 5) << QMGR_MEM_RGN_DESC_SIZE_SHIFT) & + QMGR_MEM_RGN_DESC_SIZE_MASK) | + (((num_order - 5) << QMGR_MEM_RGN_SIZE_SHIFT) & + QMGR_MEM_RGN_SIZE_MASK); + __raw_writel(ctrl, desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(rgn)); + DBG("Descriptor region control @ %p, value: %x\n", + desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(rgn), + __raw_readl(desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(rgn))); + + *mem_rgn = rgn; + return 0; +} +EXPORT_SYMBOL(cppi41_mem_rgn_alloc); + +/* + * cppi41_mem_rgn_free - free the memory region within the queue manager + */ +int cppi41_mem_rgn_free(u8 q_mgr, u8 mem_rgn) +{ + void __iomem *desc_mem_regs; + + DBG("%s called.\n", __func__); + + if (q_mgr >= cppi41_num_queue_mgr || mem_rgn >= next_mem_rgn[q_mgr]) + return -EINVAL; + + desc_mem_regs = cppi41_queue_mgr[q_mgr].desc_mem_rgn_base; + + if (__raw_readl(desc_mem_regs + QMGR_MEM_RGN_BASE_REG(mem_rgn)) == 0) + return -ENOENT; + + __raw_writel(0, desc_mem_regs + QMGR_MEM_RGN_BASE_REG(mem_rgn)); + __raw_writel(0, desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(mem_rgn)); + + return 0; +} +EXPORT_SYMBOL(cppi41_mem_rgn_free); + +/* + * cppi41_tx_ch_init - initialize a CPPI 4.1 Tx channel object + * + * Verify the channel info (range checking, etc.) and store the channel + * information within the object structure. + */ +int cppi41_tx_ch_init(struct cppi41_dma_ch_obj *tx_ch_obj, + u8 dma_num, u8 ch_num) +{ + if (dma_num >= cppi41_num_dma_block || + ch_num >= cppi41_dma_block[dma_num].num_tx_ch) + return -EINVAL; + + /* Populate the channel object structure */ + tx_ch_obj->base_addr = cppi41_dma_block[dma_num].ch_ctrl_stat_base + + DMA_CH_TX_GLOBAL_CFG_REG(ch_num); + tx_ch_obj->global_cfg = __raw_readl(tx_ch_obj->base_addr); + return 0; +} +EXPORT_SYMBOL(cppi41_tx_ch_init); + +/* + * cppi41_rx_ch_init - initialize a CPPI 4.1 Rx channel object + * + * Verify the channel info (range checking, etc.) and store the channel + * information within the object structure. + */ +int cppi41_rx_ch_init(struct cppi41_dma_ch_obj *rx_ch_obj, + u8 dma_num, u8 ch_num) +{ + if (dma_num >= cppi41_num_dma_block || + ch_num >= cppi41_dma_block[dma_num].num_rx_ch) + return -EINVAL; + + /* Populate the channel object structure */ + rx_ch_obj->base_addr = cppi41_dma_block[dma_num].ch_ctrl_stat_base + + DMA_CH_RX_GLOBAL_CFG_REG(ch_num); + rx_ch_obj->global_cfg = __raw_readl(rx_ch_obj->base_addr); + return 0; +} +EXPORT_SYMBOL(cppi41_rx_ch_init); + +/* + * We have to cache the last written Rx/Tx channel global configration register + * value due to its bits other than enable/teardown being write-only. Yet there + * is a caveat related to caching the enable bit: this bit may be automatically + * cleared as a result of teardown, so we can't trust its cached value! + * When modifying the write only register fields, we're making use of the fact + * that they read back as zeros, and not clearing them explicitly... + */ + +/* + * cppi41_dma_ch_default_queue - set CPPI 4.1 channel default completion queue + */ +void cppi41_dma_ch_default_queue(struct cppi41_dma_ch_obj *dma_ch_obj, + u8 q_mgr, u16 q_num) +{ + u32 val = dma_ch_obj->global_cfg; + + /* Clear the fields to be modified. */ + val &= ~(DMA_CH_TX_DEFAULT_QMGR_MASK | DMA_CH_TX_DEFAULT_QNUM_MASK | + DMA_CH_TX_ENABLE_MASK); + + /* Set the default completion queue. */ + val |= ((q_mgr << DMA_CH_TX_DEFAULT_QMGR_SHIFT) & + DMA_CH_TX_DEFAULT_QMGR_MASK) | + ((q_num << DMA_CH_TX_DEFAULT_QNUM_SHIFT) & + DMA_CH_TX_DEFAULT_QNUM_MASK); + + /* Get the current state of the enable bit. */ + dma_ch_obj->global_cfg = val |= __raw_readl(dma_ch_obj->base_addr); + __raw_writel(val, dma_ch_obj->base_addr); + DBG("Channel global configuration @ %p, value written: %x, " + "value read: %x\n", dma_ch_obj->base_addr, val, + __raw_readl(dma_ch_obj->base_addr)); + +} +EXPORT_SYMBOL(cppi41_dma_ch_default_queue); + +/* + * cppi41_rx_ch_configure - configure CPPI 4.1 Rx channel + */ +void cppi41_rx_ch_configure(struct cppi41_dma_ch_obj *rx_ch_obj, + struct cppi41_rx_ch_cfg *cfg) +{ + void __iomem *base = rx_ch_obj->base_addr; + u32 val = __raw_readl(rx_ch_obj->base_addr); + + val |= ((cfg->sop_offset << DMA_CH_RX_SOP_OFFSET_SHIFT) & + DMA_CH_RX_SOP_OFFSET_MASK) | + ((cfg->default_desc_type << DMA_CH_RX_DEFAULT_DESC_TYPE_SHIFT) & + DMA_CH_RX_DEFAULT_DESC_TYPE_MASK) | + ((cfg->retry_starved << DMA_CH_RX_ERROR_HANDLING_SHIFT) & + DMA_CH_RX_ERROR_HANDLING_MASK) | + ((cfg->rx_queue.q_mgr << DMA_CH_RX_DEFAULT_RQ_QMGR_SHIFT) & + DMA_CH_RX_DEFAULT_RQ_QMGR_MASK) | + ((cfg->rx_queue.q_num << DMA_CH_RX_DEFAULT_RQ_QNUM_SHIFT) & + DMA_CH_RX_DEFAULT_RQ_QNUM_MASK); + + rx_ch_obj->global_cfg = val; + __raw_writel(val, base); + DBG("Rx channel global configuration @ %p, value written: %x, " + "value read: %x\n", base, val, __raw_readl(base)); + + base -= DMA_CH_RX_GLOBAL_CFG_REG(0); + + /* + * Set up the packet configuration register + * based on the descriptor type... + */ + switch (cfg->default_desc_type) { + case DMA_CH_RX_DEFAULT_DESC_EMBED: + val = ((cfg->cfg.embed_pkt.fd_queue.q_mgr << + DMA_CH_RX_EMBED_FDQ_QMGR_SHIFT) & + DMA_CH_RX_EMBED_FDQ_QMGR_MASK) | + ((cfg->cfg.embed_pkt.fd_queue.q_num << + DMA_CH_RX_EMBED_FDQ_QNUM_SHIFT) & + DMA_CH_RX_EMBED_FDQ_QNUM_MASK) | + ((cfg->cfg.embed_pkt.num_buf_slot << + DMA_CH_RX_EMBED_NUM_SLOT_SHIFT) & + DMA_CH_RX_EMBED_NUM_SLOT_MASK) | + ((cfg->cfg.embed_pkt.sop_slot_num << + DMA_CH_RX_EMBED_SOP_SLOT_SHIFT) & + DMA_CH_RX_EMBED_SOP_SLOT_MASK); + + __raw_writel(val, base + DMA_CH_RX_EMBED_PKT_CFG_REG_B(0)); + DBG("Rx channel embedded packet configuration B @ %p, " + "value written: %x\n", + base + DMA_CH_RX_EMBED_PKT_CFG_REG_B(0), val); + + val = ((cfg->cfg.embed_pkt.free_buf_pool[0].b_pool << + DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(0)) & + DMA_CH_RX_EMBED_FBP_PNUM_MASK(0)) | + ((cfg->cfg.embed_pkt.free_buf_pool[0].b_mgr << + DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(0)) & + DMA_CH_RX_EMBED_FBP_BMGR_MASK(0)) | + ((cfg->cfg.embed_pkt.free_buf_pool[1].b_pool << + DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(1)) & + DMA_CH_RX_EMBED_FBP_PNUM_MASK(1)) | + ((cfg->cfg.embed_pkt.free_buf_pool[1].b_mgr << + DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(1)) & + DMA_CH_RX_EMBED_FBP_BMGR_MASK(1)) | + ((cfg->cfg.embed_pkt.free_buf_pool[2].b_pool << + DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(2)) & + DMA_CH_RX_EMBED_FBP_PNUM_MASK(2)) | + ((cfg->cfg.embed_pkt.free_buf_pool[2].b_mgr << + DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(2)) & + DMA_CH_RX_EMBED_FBP_BMGR_MASK(2)) | + ((cfg->cfg.embed_pkt.free_buf_pool[3].b_pool << + DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(3)) & + DMA_CH_RX_EMBED_FBP_PNUM_MASK(3)) | + ((cfg->cfg.embed_pkt.free_buf_pool[3].b_mgr << + DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(3)) & + DMA_CH_RX_EMBED_FBP_BMGR_MASK(3)); + + __raw_writel(val, base + DMA_CH_RX_EMBED_PKT_CFG_REG_A(0)); + DBG("Rx channel embedded packet configuration A @ %p, " + "value written: %x\n", + base + DMA_CH_RX_EMBED_PKT_CFG_REG_A(0), val); + break; + case DMA_CH_RX_DEFAULT_DESC_HOST: + val = ((cfg->cfg.host_pkt.fdb_queue[0].q_num << + DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(0)) & + DMA_CH_RX_HOST_FDQ_QNUM_MASK(0)) | + ((cfg->cfg.host_pkt.fdb_queue[0].q_mgr << + DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(0)) & + DMA_CH_RX_HOST_FDQ_QMGR_MASK(0)) | + ((cfg->cfg.host_pkt.fdb_queue[1].q_num << + DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(1)) & + DMA_CH_RX_HOST_FDQ_QNUM_MASK(1)) | + ((cfg->cfg.host_pkt.fdb_queue[1].q_mgr << + DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(1)) & + DMA_CH_RX_HOST_FDQ_QMGR_MASK(1)); + + __raw_writel(val, base + DMA_CH_RX_HOST_PKT_CFG_REG_A(0)); + DBG("Rx channel host packet configuration A @ %p, " + "value written: %x\n", + base + DMA_CH_RX_HOST_PKT_CFG_REG_A(0), val); + + val = ((cfg->cfg.host_pkt.fdb_queue[2].q_num << + DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(2)) & + DMA_CH_RX_HOST_FDQ_QNUM_MASK(2)) | + ((cfg->cfg.host_pkt.fdb_queue[2].q_mgr << + DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(2)) & + DMA_CH_RX_HOST_FDQ_QMGR_MASK(2)) | + ((cfg->cfg.host_pkt.fdb_queue[3].q_num << + DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(3)) & + DMA_CH_RX_HOST_FDQ_QNUM_MASK(3)) | + ((cfg->cfg.host_pkt.fdb_queue[3].q_mgr << + DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(3)) & + DMA_CH_RX_HOST_FDQ_QMGR_MASK(3)); + + __raw_writel(val, base + DMA_CH_RX_HOST_PKT_CFG_REG_B(0)); + DBG("Rx channel host packet configuration B @ %p, " + "value written: %x\n", + base + DMA_CH_RX_HOST_PKT_CFG_REG_B(0), val); + break; + case DMA_CH_RX_DEFAULT_DESC_MONO: + val = ((cfg->cfg.mono_pkt.fd_queue.q_num << + DMA_CH_RX_MONO_FDQ_QNUM_SHIFT) & + DMA_CH_RX_MONO_FDQ_QNUM_MASK) | + ((cfg->cfg.mono_pkt.fd_queue.q_mgr << + DMA_CH_RX_MONO_FDQ_QMGR_SHIFT) & + DMA_CH_RX_MONO_FDQ_QMGR_MASK) | + ((cfg->cfg.mono_pkt.sop_offset << + DMA_CH_RX_MONO_SOP_OFFSET_SHIFT) & + DMA_CH_RX_MONO_SOP_OFFSET_MASK); + + __raw_writel(val, base + DMA_CH_RX_MONO_PKT_CFG_REG(0)); + DBG("Rx channel monolithic packet configuration @ %p, " + "value written: %x\n", + base + DMA_CH_RX_MONO_PKT_CFG_REG(0), val); + break; + } +} +EXPORT_SYMBOL(cppi41_rx_ch_configure); + +/* + * cppi41_dma_ch_teardown - teardown a given Tx/Rx channel + */ +void cppi41_dma_ch_teardown(struct cppi41_dma_ch_obj *dma_ch_obj) +{ + u32 val = __raw_readl(dma_ch_obj->base_addr); + + /* Initiate channel teardown. */ + val |= dma_ch_obj->global_cfg & ~DMA_CH_TX_ENABLE_MASK; + dma_ch_obj->global_cfg = val |= DMA_CH_TX_TEARDOWN_MASK; + __raw_writel(val, dma_ch_obj->base_addr); + DBG("Tear down channel @ %p, value written: %x, value read: %x\n", + dma_ch_obj->base_addr, val, __raw_readl(dma_ch_obj->base_addr)); +} +EXPORT_SYMBOL(cppi41_dma_ch_teardown); + +/* + * cppi41_dma_ch_enable - enable Tx/Rx DMA channel in hardware + * + * Makes the channel ready for data transmission/reception. + */ +void cppi41_dma_ch_enable(struct cppi41_dma_ch_obj *dma_ch_obj) +{ + u32 val = dma_ch_obj->global_cfg | DMA_CH_TX_ENABLE_MASK; + + /* Teardown bit remains set after completion, so clear it now... */ + dma_ch_obj->global_cfg = val &= ~DMA_CH_TX_TEARDOWN_MASK; + __raw_writel(val, dma_ch_obj->base_addr); + DBG("Enable channel @ %p, value written: %x, value read: %x\n", + dma_ch_obj->base_addr, val, __raw_readl(dma_ch_obj->base_addr)); +} +EXPORT_SYMBOL(cppi41_dma_ch_enable); + +/* + * cppi41_dma_ch_disable - disable Tx/Rx DMA channel in hardware + */ +void cppi41_dma_ch_disable(struct cppi41_dma_ch_obj *dma_ch_obj) +{ + dma_ch_obj->global_cfg &= ~DMA_CH_TX_ENABLE_MASK; + __raw_writel(dma_ch_obj->global_cfg, dma_ch_obj->base_addr); + DBG("Disable channel @ %p, value written: %x, value read: %x\n", + dma_ch_obj->base_addr, dma_ch_obj->global_cfg, + __raw_readl(dma_ch_obj->base_addr)); +} +EXPORT_SYMBOL(cppi41_dma_ch_disable); + +void cppi41_free_teardown_queue(int dma_num) +{ + unsigned long td_addr; + + while ((td_addr = + cppi41_queue_pop(&dma_teardown[dma_num].queue_obj)) != 0) + DBG("pop tdDesc(%p) from tdQueue\n", td_addr); +} +EXPORT_SYMBOL(cppi41_free_teardown_queue); + +void cppi41_exit(void) +{ + int i; + for (i = 0; i < CPPI41_NUM_QUEUE_MGR; i++) { + if (linking_ram[i].virt_addr != NULL) + dma_free_coherent(NULL, 0x10000, + linking_ram[i].virt_addr, + linking_ram[i].phys_addr); + if (allocated_queues[i] != NULL) + kfree(allocated_queues[i]); + } + for (i = 0; i < CPPI41_NUM_DMA_BLOCK; i++) + if (dma_teardown[i].virt_addr != NULL) { + + cppi41_mem_rgn_free(0, dma_teardown[i].mem_rgn); + dma_free_coherent(NULL, dma_teardown[i].rgn_size, + dma_teardown[i].virt_addr, + dma_teardown[i].phys_addr); + } +} +EXPORT_SYMBOL(cppi41_exit); + +/** + * alloc_queue - allocate a queue in the given range + * @allocated: pointer to the bitmap of the allocated queues + * @excluded: pointer to the bitmap of the queues excluded from allocation + * (optional) + * @start: starting queue number + * @count: number of queues available + * + * Returns queue number on success, -ENOSPC otherwise. + */ +static int alloc_queue(u32 *allocated, const u32 *excluded, unsigned start, + unsigned count) +{ + u32 bit, mask = 0; + int index = -1; + + /* + * We're starting the loop as if we've just wrapped around 32 bits + * in order to save on preloading the bitmasks. + */ + for (bit = 0; count--; start++, bit <<= 1) { + /* Have we just wrapped around 32 bits? */ + if (!bit) { + /* Start over with the next bitmask word */ + bit = 1; + index++; + /* Have we just entered the loop? */ + if (!index) { + /* Calculate the starting values */ + bit <<= start & 0x1f; + index = start >> 5; + } + /* + * Load the next word of the allocated bitmask OR'ing + * it with the excluded bitmask if it's been passed. + */ + mask = allocated[index]; + if (excluded != NULL) + mask |= excluded[index]; + } + /* + * If the bit in the combined bitmask is zero, + * we've just found a free queue. + */ + if (!(mask & bit)) { + allocated[index] |= bit; + return start; + } + } + return -ENOSPC; +} + +/* + * cppi41_queue_alloc - allocate a queue of a given type in the queue manager + */ +int cppi41_queue_alloc(u8 type, u8 q_mgr, u16 *q_num) +{ + int res = -ENOSPC; + + if (q_mgr >= cppi41_num_queue_mgr) + return -EINVAL; + + /* Mask out the unsupported queue types */ + type &= cppi41_queue_mgr[q_mgr].queue_types; + /* First see if a free descriptor queue was requested... */ + if (type & CPPI41_FREE_DESC_QUEUE) + res = alloc_queue(allocated_queues[q_mgr], NULL, + cppi41_queue_mgr[q_mgr].base_fdq_num, 16); + + /* Then see if a free descriptor/buffer queue was requested... */ + if (res < 0 && (type & CPPI41_FREE_DESC_BUF_QUEUE)) + res = alloc_queue(allocated_queues[q_mgr], NULL, + cppi41_queue_mgr[q_mgr].base_fdbq_num, 16); + + /* Last see if an unassigned queue was requested... */ + if (res < 0 && (type & CPPI41_UNASSIGNED_QUEUE)) + res = alloc_queue(allocated_queues[q_mgr], + cppi41_queue_mgr[q_mgr].assigned, 0, + cppi41_queue_mgr[q_mgr].num_queue); + + /* See if any queue was allocated... */ + if (res < 0) + return res; + + /* Return the queue allocated */ + *q_num = res; + return 0; +} +EXPORT_SYMBOL(cppi41_queue_alloc); + +/* + * cppi41_queue_free - free the given queue in the queue manager + */ +int cppi41_queue_free(u8 q_mgr, u16 q_num) +{ + int index = q_num >> 5, bit = 1 << (q_num & 0x1f); + + if (q_mgr >= cppi41_num_queue_mgr || + q_num >= cppi41_queue_mgr[q_mgr].num_queue || + !(allocated_queues[q_mgr][index] & bit)) + return -EINVAL; + + allocated_queues[q_mgr][index] &= ~bit; + return 0; +} +EXPORT_SYMBOL(cppi41_queue_free); + +/* + * cppi41_queue_init - initialize a CPPI 4.1 queue object + */ +int cppi41_queue_init(struct cppi41_queue_obj *queue_obj, u8 q_mgr, u16 q_num) +{ + if (q_mgr >= cppi41_num_queue_mgr || + q_num >= cppi41_queue_mgr[q_mgr].num_queue) + return -EINVAL; + + queue_obj->base_addr = cppi41_queue_mgr[q_mgr].q_mgmt_rgn_base + + QMGR_QUEUE_STATUS_REG_A(q_num); + + return 0; +} +EXPORT_SYMBOL(cppi41_queue_init); + +/* + * cppi41_queue_push - push a descriptor into the given queue + */ +void cppi41_queue_push(const struct cppi41_queue_obj *queue_obj, u32 desc_addr, + u32 desc_size, u32 pkt_size) +{ + u32 val; + + /* + * Write to the tail of the queue. + * TODO: Can't think of a reason why a queue to head may be required. + * If it is, the API may have to be extended. + */ +#if 0 + /* + * Also, can't understand why packet size is required to queue up a + * descriptor. The spec says packet size *must* be written prior to + * the packet write operation. + */ + if (pkt_size) + val = (pkt_size << QMGR_QUEUE_PKT_SIZE_SHIFT) & + QMGR_QUEUE_PKT_SIZE_MASK; + __raw_writel(val, queue_obj->base_addr + QMGR_QUEUE_REG_C(0)); +#endif + + val = (((desc_size - 24) >> (2 - QMGR_QUEUE_DESC_SIZE_SHIFT)) & + QMGR_QUEUE_DESC_SIZE_MASK) | + (desc_addr & QMGR_QUEUE_DESC_PTR_MASK); + + DBG("Pushing value %x to queue @ %p\n", val, queue_obj->base_addr); + + __raw_writel(val, queue_obj->base_addr + QMGR_QUEUE_REG_D(0)); +} +EXPORT_SYMBOL(cppi41_queue_push); + +/* + * cppi41_queue_pop - pop a descriptor from a given queue + */ +unsigned long cppi41_queue_pop(const struct cppi41_queue_obj *queue_obj) +{ + u32 val = __raw_readl(queue_obj->base_addr + QMGR_QUEUE_REG_D(0)); + + DBG("Popping value %x from queue @ %p\n", val, queue_obj->base_addr); + + return val & QMGR_QUEUE_DESC_PTR_MASK; +} +EXPORT_SYMBOL(cppi41_queue_pop); + +/* + * cppi41_get_teardown_info - extract information from a teardown descriptor + */ +int cppi41_get_teardown_info(unsigned long addr, u32 *info) +{ + struct cppi41_teardown_desc *desc; + int dma_num; + + for (dma_num = 0; dma_num < cppi41_num_dma_block; dma_num++) + if (addr >= dma_teardown[dma_num].phys_addr && + addr < dma_teardown[dma_num].phys_addr + + dma_teardown[dma_num].rgn_size) + break; + + if (dma_num == cppi41_num_dma_block) + return -EINVAL; + + desc = addr - dma_teardown[dma_num].phys_addr + + dma_teardown[dma_num].virt_addr; + + if ((desc->teardown_info & CPPI41_DESC_TYPE_MASK) != + (CPPI41_DESC_TYPE_TEARDOWN << CPPI41_DESC_TYPE_SHIFT)) + return -EINVAL; + + *info = desc->teardown_info; +#if 1 + /* Hardware is not giving the current DMA number as of now. :-/ */ + *info |= (dma_num << CPPI41_TEARDOWN_DMA_NUM_SHIFT) & + CPPI41_TEARDOWN_DMA_NUM_MASK; +#else + dma_num = (desc->teardown_info & CPPI41_TEARDOWN_DMA_NUM_MASK) >> + CPPI41_TEARDOWN_DMA_NUM_SHIFT; +#endif + + cppi41_queue_push(&dma_teardown[dma_num].queue_obj, addr, + sizeof(struct cppi41_teardown_desc), 0); + + return 0; +} +EXPORT_SYMBOL(cppi41_get_teardown_info); + +MODULE_DESCRIPTION("TI CPPI 4.1 support"); +MODULE_AUTHOR("MontaVista Software"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/musb/cppi41.h b/drivers/usb/musb/cppi41.h new file mode 100644 index 00000000000..86623dda516 --- /dev/null +++ b/drivers/usb/musb/cppi41.h @@ -0,0 +1,726 @@ +/* + * CPPI 4.1 definitions + * + * Copyright (c) 2008-2009, MontaVista Software, Inc. + * + * This program is free software; you can distribute 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 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 + +/* + * Queue Manager - Control Registers Region + */ +#define QMGR_REVISION_REG 0x00 /* Major and minor versions */ + /* of the module */ +#define QMGR_QUEUE_DIVERSION_REG 0x08 /* Queue Diversion register */ +#define QMGR_FREE_DESC_BUF_STARVED_REG(n) (0x20 + ((n) << 2)) /* Free Desc./ */ + /* Buffer Starvation Count */ +#define QMGR_FREE_DESC_STARVED_REG(n) (0x30 + ((n) << 2)) /* Free Desc. */ + /* Starvation Count */ +#define QMGR_LINKING_RAM_RGN0_BASE_REG 0x80 /* Linking RAM Region 0 Base */ + /* Address */ +#define QMGR_LINKING_RAM_RGN0_SIZE_REG 0x84 /* Linking RAM Region 0 Size */ +#define QMGR_LINKING_RAM_RGN1_BASE_REG 0x88 /* Linking RAM Region 1 Base */ + /* Address */ +#define QMGR_QUEUE_PENDING_REG(n) (0x90 + ((n) << 2)) /* Pending status */ + /* for all queues */ + +/* + * Queue Manager - Memory Region Registers + */ +#define QMGR_MEM_RGN_BASE_REG(r) (0x00 + ((r) << 4)) +#define QMGR_MEM_RGN_CTRL_REG(r) (0x04 + ((r) << 4)) + +/* Memory Region R Control Register bits */ +#define QMGR_MEM_RGN_INDEX_SHIFT 16 +#define QMGR_MEM_RGN_INDEX_MASK (0x3fff << QMGR_MEM_RGN_INDEX_SHIFT) +#define QMGR_MEM_RGN_DESC_SIZE_SHIFT 8 +#define QMGR_MEM_RGN_DESC_SIZE_MASK (0xf << QMGR_MEM_RGN_DESC_SIZE_SHIFT) +#define QMGR_MEM_RGN_SIZE_SHIFT 0 +#define QMGR_MEM_RGN_SIZE_MASK (7 << QMGR_MEM_RGN_SIZE_SHIFT) + +/* + * Queue Manager - Queues Region + */ +#define QMGR_QUEUE_REG_A(n) (0x00 + ((n) << 4)) +#define QMGR_QUEUE_REG_B(n) (0x04 + ((n) << 4)) +#define QMGR_QUEUE_REG_C(n) (0x08 + ((n) << 4)) +#define QMGR_QUEUE_REG_D(n) (0x0C + ((n) << 4)) + +/* Queue N Register C bits */ +#define QMGR_QUEUE_HEAD_TAIL_SHIFT 31 +#define QMGR_QUEUE_HEAD_TAIL_MASK (1 << QMGR_QUEUE_HEAD_TAIL_SHIFT) +#define QMGR_QUEUE_PKT_SIZE_SHIFT 0 +#define QMGR_QUEUE_PKT_SIZE_MASK (0x3fff << QMGR_QUEUE_PKT_SIZE_SHIFT) +/* Queue N Register D bits */ +#define QMGR_QUEUE_DESC_PTR_SHIFT 5 +#define QMGR_QUEUE_DESC_PTR_MASK (0x7ffffff << QMGR_QUEUE_DESC_PTR_SHIFT) +#define QMGR_QUEUE_DESC_SIZE_SHIFT 0 +#define QMGR_QUEUE_DESC_SIZE_MASK (0x1f << QMGR_QUEUE_DESC_SIZE_SHIFT) + +/* + * Queue Manager - Queue Status Region + */ +#define QMGR_QUEUE_STATUS_REG_A(n) (0x00 + ((n) << 4)) +#define QMGR_QUEUE_STATUS_REG_B(n) (0x04 + ((n) << 4)) +#define QMGR_QUEUE_STATUS_REG_C(n) (0x08 + ((n) << 4)) + +/* + * DMA Controller - Global Control Registers Region + */ +#define DMA_REVISION_REG 0x00 /* Major and minor versions */ + /* of the module */ +#define DMA_TEARDOWN_FREE_DESC_CTRL_REG 0x04 /* Queue manager and queue */ + /* number for Teardown free */ + /* descriptor queue */ +#define DMA_EMULATION_CTRL_REG 0x08 /* Emulation control register */ + +/* Teardown Free Descriptor Queue Control Register bits */ +#define DMA_TD_DESC_QMGR_SHIFT 12 +#define DMA_TD_DESC_QMGR_MASK (3 << DMA_TD_DESC_QMGR_SHIFT) +#define DMA_TD_DESC_QNUM_SHIFT 0 +#define DMA_TD_DESC_QNUM_MASK (0xfff << DMA_TD_DESC_QNUM_SHIFT) + +/* + * DMA Controller - Channel Control / Status Registers Region + */ +#define DMA_CH_TX_GLOBAL_CFG_REG(n) (0x00 + ((n) << 5)) +#define DMA_CH_RX_GLOBAL_CFG_REG(n) (0x08 + ((n) << 5)) +#define DMA_CH_RX_HOST_PKT_CFG_REG_A(n) (0x0C + ((n) << 5)) +#define DMA_CH_RX_HOST_PKT_CFG_REG_B(n) (0x10 + ((n) << 5)) +#define DMA_CH_RX_EMBED_PKT_CFG_REG_A(n) (0x14 + ((n) << 5)) +#define DMA_CH_RX_EMBED_PKT_CFG_REG_B(n) (0x18 + ((n) << 5)) +#define DMA_CH_RX_MONO_PKT_CFG_REG(n) (0x1C + ((n) << 5)) + +/* Tx Channel N Global Configuration Register bits */ +#define DMA_CH_TX_ENABLE_SHIFT 31 +#define DMA_CH_TX_ENABLE_MASK (1 << DMA_CH_TX_ENABLE_SHIFT) +#define DMA_CH_TX_TEARDOWN_SHIFT 30 +#define DMA_CH_TX_TEARDOWN_MASK (1 << DMA_CH_TX_TEARDOWN_SHIFT) +#define DMA_CH_TX_DEFAULT_QMGR_SHIFT 12 +#define DMA_CH_TX_DEFAULT_QMGR_MASK (3 << DMA_CH_TX_DEFAULT_QMGR_SHIFT) +#define DMA_CH_TX_DEFAULT_QNUM_SHIFT 0 +#define DMA_CH_TX_DEFAULT_QNUM_MASK (0xfff << DMA_CH_TX_DEFAULT_QNUM_SHIFT) + +/* Rx Channel N Global Configuration Register bits */ +#define DMA_CH_RX_ENABLE_SHIFT 31 +#define DMA_CH_RX_ENABLE_MASK (1 << DMA_CH_RX_ENABLE_SHIFT) +#define DMA_CH_RX_TEARDOWN_SHIFT 30 +#define DMA_CH_RX_TEARDOWN_MASK (1 << DMA_CH_RX_TEARDOWN_SHIFT) +#define DMA_CH_RX_ERROR_HANDLING_SHIFT 24 +#define DMA_CH_RX_ERROR_HANDLING_MASK (1 << DMA_CH_RX_ERROR_HANDLING_SHIFT) +#define DMA_CH_RX_SOP_OFFSET_SHIFT 16 +#define DMA_CH_RX_SOP_OFFSET_MASK (0xff << DMA_CH_RX_SOP_OFFSET_SHIFT) +#define DMA_CH_RX_DEFAULT_DESC_TYPE_SHIFT 14 +#define DMA_CH_RX_DEFAULT_DESC_TYPE_MASK (3 << \ + DMA_CH_RX_DEFAULT_DESC_TYPE_SHIFT) +#define DMA_CH_RX_DEFAULT_DESC_EMBED 0 +#define DMA_CH_RX_DEFAULT_DESC_HOST 1 +#define DMA_CH_RX_DEFAULT_DESC_MONO 2 +#define DMA_CH_RX_DEFAULT_RQ_QMGR_SHIFT 12 +#define DMA_CH_RX_DEFAULT_RQ_QMGR_MASK (3 << DMA_CH_RX_DEFAULT_RQ_QMGR_SHIFT) +#define DMA_CH_RX_DEFAULT_RQ_QNUM_SHIFT 0 +#define DMA_CH_RX_DEFAULT_RQ_QNUM_MASK (0xfff << \ + DMA_CH_RX_DEFAULT_RQ_QNUM_SHIFT) + +/* Rx Channel N Host Packet Configuration Register A/B bits */ +#define DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(n) (12 + 16 * ((n) & 1)) +#define DMA_CH_RX_HOST_FDQ_QMGR_MASK(n) (3 << DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(n)) +#define DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(n) (0 + 16 * ((n) & 1)) +#define DMA_CH_RX_HOST_FDQ_QNUM_MASK(n) (0xfff << \ + DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(n)) + +/* Rx Channel N Embedded Packet Configuration Register A bits */ +#define DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(n) (6 + 8 * (n)) +#define DMA_CH_RX_EMBED_FBP_BMGR_MASK(n) (3 << \ + DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(n)) +#define DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(n) (0 + 8 * (n)) +#define DMA_CH_RX_EMBED_FBP_PNUM_MASK(n) (0x1f << \ + DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(n)) + +/* Rx Channel N Embedded Packet Configuration Register B bits */ +#define DMA_CH_RX_EMBED_NUM_SLOT_SHIFT 24 +#define DMA_CH_RX_EMBED_NUM_SLOT_MASK (7 << DMA_CH_RX_EMBED_NUM_SLOT_SHIFT) +#define DMA_CH_RX_EMBED_SOP_SLOT_SHIFT 16 +#define DMA_CH_RX_EMBED_SOP_SLOT_MASK (7 << DMA_CH_RX_EMBED_SOP_SLOT_SHIFT) +#define DMA_CH_RX_EMBED_FDQ_QMGR_SHIFT 12 +#define DMA_CH_RX_EMBED_FDQ_QMGR_MASK (3 << DMA_CH_RX_EMBED_FDQ_QMGR_SHIFT) +#define DMA_CH_RX_EMBED_FDQ_QNUM_SHIFT 0 +#define DMA_CH_RX_EMBED_FDQ_QNUM_MASK (0xfff << \ + DMA_CH_RX_EMBED_FDQ_QNUM_SHIFT) + +/* Rx Channel N Monolithic Packet Configuration Register bits */ +#define DMA_CH_RX_MONO_SOP_OFFSET_SHIFT 16 +#define DMA_CH_RX_MONO_SOP_OFFSET_MASK (0xff << \ + DMA_CH_RX_MONO_SOP_OFFSET_SHIFT) +#define DMA_CH_RX_MONO_FDQ_QMGR_SHIFT 12 +#define DMA_CH_RX_MONO_FDQ_QMGR_MASK (3 << DMA_CH_RX_MONO_FDQ_QMGR_SHIFT) +#define DMA_CH_RX_MONO_FDQ_QNUM_SHIFT 0 +#define DMA_CH_RX_MONO_FDQ_QNUM_MASK (0xfff << DMA_CH_RX_MONO_FDQ_QNUM_SHIFT) + +/* + * DMA Scheduler - Control Region + */ +#define DMA_SCHED_CTRL_REG 0x00 + +/* DMA Scheduler Control Register bits */ +#define DMA_SCHED_ENABLE_SHIFT 31 +#define DMA_SCHED_ENABLE_MASK (1 << DMA_SCHED_ENABLE_SHIFT) +#define DMA_SCHED_LAST_ENTRY_SHIFT 0 +#define DMA_SCHED_LAST_ENTRY_MASK (0xff << DMA_SCHED_LAST_ENTRY_SHIFT) + +/* + * DMA Scheduler - Table Region + */ +#define DMA_SCHED_TABLE_WORD_REG(n) ((n) << 2) + +/* + * CPPI 4.1 Host Packet Descriptor + */ +struct cppi41_host_pkt_desc { + u32 desc_info; /* Descriptor type, protocol specific word */ + /* count, packet length */ + u32 tag_info; /* Source tag (31:16), destination tag (15:0) */ + u32 pkt_info; /* Packet error state, type, protocol flags, */ + /* return info, descriptor location */ + u32 buf_len; /* Number of valid data bytes in the buffer */ + u32 buf_ptr; /* Pointer to the buffer associated with */ + /* this descriptor */ + u32 next_desc_ptr; /* Pointer to the next buffer descriptor */ + u32 orig_buf_len; /* Original buffer length */ + u32 orig_buf_ptr; /* Original buffer pointer */ + u32 stk_comms_info[2]; /* Network stack private communications info */ +}; + +/* + * CPPI 4.1 Host Buffer Descriptor + */ +struct cppi41_host_buf_desc { + u32 reserved[2]; + u32 buf_recl_info; /* Return info, descriptor location */ + u32 buf_len; /* Number of valid data bytes in the buffer */ + u32 buf_ptr; /* Pointer to the buffer associated with */ + /* this descriptor */ + u32 next_desc_ptr; /* Pointer to the next buffer descriptor */ + u32 orig_buf_len; /* Original buffer length */ + u32 orig_buf_ptr; /* Original buffer pointer */ +}; + +#define CPPI41_DESC_TYPE_SHIFT 27 +#define CPPI41_DESC_TYPE_MASK (0x1f << CPPI41_DESC_TYPE_SHIFT) +#define CPPI41_DESC_TYPE_HOST 16 +#define CPPI41_DESC_TYPE_MONOLITHIC 18 +#define CPPI41_DESC_TYPE_TEARDOWN 19 +#define CPPI41_PROT_VALID_WORD_CNT_SHIFT 22 +#define CPPI41_PROT_VALID_WORD_CNT_MASK (0x1f << CPPI41_PROT_WORD_CNT_SHIFT) +#define CPPI41_PKT_LEN_SHIFT 0 +#define CPPI41_PKT_LEN_MASK (0x1fffff << CPPI41_PKT_LEN_SHIFT) + +#define CPPI41_PKT_ERROR_SHIFT 31 +#define CPPI41_PKT_ERROR_MASK (1 << CPPI41_PKT_ERROR_SHIFT) +#define CPPI41_PKT_TYPE_SHIFT 26 +#define CPPI41_PKT_TYPE_MASK (0x1f << CPPI41_PKT_TYPE_SHIFT) +#define CPPI41_PKT_TYPE_ATM_AAL5 0 +#define CPPI41_PKT_TYPE_ATM_NULL_AAL 1 +#define CPPI41_PKT_TYPE_ATM_OAM 2 +#define CPPI41_PKT_TYPE_ATM_TRANSPARENT 3 +#define CPPI41_PKT_TYPE_EFM 4 +#define CPPI41_PKT_TYPE_USB 5 +#define CPPI41_PKT_TYPE_GENERIC 6 +#define CPPI41_PKT_TYPE_ETHERNET 7 +#define CPPI41_RETURN_POLICY_SHIFT 15 +#define CPPI41_RETURN_POLICY_MASK (1 << CPPI41_RETURN_POLICY_SHIFT) +#define CPPI41_RETURN_LINKED 0 +#define CPPI41_RETURN_UNLINKED 1 +#define CPPI41_ONCHIP_SHIFT 14 +#define CPPI41_ONCHIP_MASK (1 << CPPI41_ONCHIP_SHIFT) +#define CPPI41_RETURN_QMGR_SHIFT 12 +#define CPPI41_RETURN_QMGR_MASK (3 << CPPI41_RETURN_QMGR_SHIFT) +#define CPPI41_RETURN_QNUM_SHIFT 0 +#define CPPI41_RETURN_QNUM_MASK (0xfff << CPPI41_RETURN_QNUM_SHIFT) + +#define CPPI41_SRC_TAG_PORT_NUM_SHIFT 27 +#define CPPI41_SRC_TAG_PORT_NUM_MASK (0x1f << CPPI41_SRC_TAG_PORT_NUM_SHIFT) +#define CPPI41_SRC_TAG_CH_NUM_SHIFT 21 +#define CPPI41_SRC_TAG_CH_NUM_MASK (0x3f << CPPI41_SRC_TAG_CH_NUM_SHIFT) +#define CPPI41_SRC_TAG_SUB_CH_NUM_SHIFT 16 +#define CPPI41_SRC_TAG_SUB_CH_NUM_MASK (0x1f << \ + CPPI41_SRC_TAG_SUB_CH_NUM_SHIFT) +#define CPPI41_DEST_TAG_SHIFT 0 +#define CPPI41_DEST_TAG_MASK (0xffff << CPPI41_DEST_TAG_SHIFT) + +/* + * CPPI 4.1 Teardown Descriptor + */ +struct cppi41_teardown_desc { + u32 teardown_info; /* Teardown information */ + u32 reserved[7]; /* 28 byte padding */ +}; + +#define CPPI41_TEARDOWN_TX_RX_SHIFT 16 +#define CPPI41_TEARDOWN_TX_RX_MASK (1 << CPPI41_TEARDOWN_TX_RX_SHIFT) +#define CPPI41_TEARDOWN_DMA_NUM_SHIFT 10 +#define CPPI41_TEARDOWN_DMA_NUM_MASK (0x3f << CPPI41_TEARDOWN_DMA_NUM_SHIFT) +#define CPPI41_TEARDOWN_CHAN_NUM_SHIFT 0 +#define CPPI41_TEARDOWN_CHAN_NUM_MASK (0x3f << CPPI41_TEARDOWN_CHAN_NUM_SHIFT) + +#define CPPI41_MAX_MEM_RGN 16 + +/* CPPI 4.1 configuration for AM3517 */ +#define CPPI41_NUM_QUEUE_MGR 1 /* 4 max */ +#define CPPI41_NUM_DMA_BLOCK 1 /* 64 max */ +#define cppi41_num_queue_mgr CPPI41_NUM_QUEUE_MGR +#define cppi41_num_dma_block CPPI41_NUM_DMA_BLOCK + +/** + * struct cppi41_queue - Queue Tuple + * + * The basic queue tuple in CPPI 4.1 used across all data structures + * where a definition of a queue is required. + */ +struct cppi41_queue { + u8 q_mgr; /* The queue manager number */ + u16 q_num; /* The queue number */ +}; + +/** + * struct cppi41_buf_pool - Buffer Pool Tuple + * + * The basic buffer pool tuple in CPPI 4.1 used across all data structures + * where a definition of a buffer pool is required. + */ +struct cppi41_buf_pool { + u8 b_mgr; /* The buffer manager number */ + u16 b_pool; /* The buffer pool number */ +}; + +/** + * struct cppi41_queue_mgr - Queue Manager information + * + * Contains the information about the queue manager which should be copied from + * the hardware spec as is. + */ +struct cppi41_queue_mgr { + void __iomem *q_mgr_rgn_base; /* Base address of the Control region. */ + void __iomem *desc_mem_rgn_base; /* Base address of the descriptor */ + /* memory region. */ + void __iomem *q_mgmt_rgn_base; /* Base address of the queues region. */ + void __iomem *q_stat_rgn_base; /* Base address of the queue status */ + /* region. */ + u16 num_queue; /* Number of the queues supported. */ + u8 queue_types; /* Bitmask of the supported queue types. */ + u16 base_fdq_num; /* The base free descriptor queue number. */ + /* If present, there's always 16 such queues. */ + u16 base_fdbq_num; /* The base free descriptor/buffer queue */ + /* number. If present, there's always 16 */ + /* such queues. */ + const u32 *assigned; /* Pointer to the bitmask of the pre-assigned */ + /* queues. */ +}; + +/* Queue type flags */ +#define CPPI41_FREE_DESC_QUEUE 0x01 +#define CPPI41_FREE_DESC_BUF_QUEUE 0x02 +#define CPPI41_UNASSIGNED_QUEUE 0x04 + +/** + * struct cppi41_embed_pkt_cfg - Rx Channel Embedded packet configuration + * + * An instance of this structure forms part of the Rx channel information + * structure. + */ +struct cppi41_embed_pkt_cfg { + struct cppi41_queue fd_queue; /* Free Descriptor queue.*/ + u8 num_buf_slot; /* Number of buffer slots in the descriptor */ + u8 sop_slot_num; /* SOP buffer slot number. */ + struct cppi41_buf_pool free_buf_pool[4]; /* Free Buffer pool. Element */ + /* 0 used for the 1st Rx buffer, etc. */ +}; + +/** + * struct cppi41_host_pkt_cfg - Rx Channel Host Packet Configuration + * + * An instance of this structure forms part of the Rx channel information + * structure. + */ +struct cppi41_host_pkt_cfg { + struct cppi41_queue fdb_queue[4]; /* Free Desc/Buffer queue. Element */ + /* 0 used for 1st Rx buffer, etc. */ +}; + +/** + * struct cppi41_mono_pkt_cfg - Rx Channel Monolithic Packet Configuration + * + * An instance of this structure forms part of the Rx channel information + * structure. + */ +struct cppi41_mono_pkt_cfg { + struct cppi41_queue fd_queue; /* Free descriptor queue */ + u8 sop_offset; /* Number of bytes to skip before writing */ + /* payload */ +}; + +enum cppi41_rx_desc_type { + cppi41_rx_embed_desc, + cppi41_rx_host_desc, + cppi41_rx_mono_desc, +}; + +/** + * struct cppi41_rx_ch_cfg - Rx Channel Configuration + * + * Must be allocated and filled by the caller of cppi41_rx_ch_configure(). + * + * The same channel can be configured to receive different descripor type + * packets (not simaltaneously). When the Rx packets on a port need to be sent + * to the SR, the channels default descriptor type is set to Embedded and the + * Rx completion queue is set to the queue which CPU polls for input packets. + * When in SR bypass mode, the same channel's default descriptor type will be + * set to Host and the Rx completion queue set to one of the queues which host + * can get interrupted on (via the Queuing proxy/accumulator). In this example, + * the embedded mode configuration fetches free descriptor from the Free + * descriptor queue (as defined by struct cppi41_embed_pkt_cfg) and host + * mode configuration fetches free descriptors/buffers from the free descriptor/ + * buffer queue (as defined by struct cppi41_host_pkt_cfg). + * + * NOTE: There seems to be no separate configuration for teardown completion + * descriptor. The assumption is rxQueue tuple is used for this purpose as well. + */ +struct cppi41_rx_ch_cfg { + enum cppi41_rx_desc_type default_desc_type; /* Describes which queue */ + /* configuration is used for the free */ + /* descriptors and/or buffers */ + u8 sop_offset; /* Number of bytes to skip in SOP buffer */ + /* before writing payload */ + u8 retry_starved; /* 0 = Drop packet on descriptor/buffer */ + /* starvartion, 1 = DMA retries FIFO block */ + /* transfer at a later time */ + struct cppi41_queue rx_queue; /* Rx complete packets queue */ + union { + struct cppi41_host_pkt_cfg host_pkt; /* Host packet */ + /* configuration. This defines where channel */ + /* picks free descriptors from. */ + struct cppi41_embed_pkt_cfg embed_pkt; /* Embedded packet */ + /* configuration. This defines where channel */ + /* picks free descriptors/buffers from. */ + /* from. */ + struct cppi41_mono_pkt_cfg mono_pkt; /* Monolithic packet */ + /* configuration. This defines where channel */ + /* picks free descriptors from. */ + } cfg; /* Union of packet configuration structures */ + /* to be filled in depending on the */ + /* defDescType field. */ +}; + +/** + * struct cppi41_tx_ch - Tx channel information + * + * NOTE: The queues that feed into the Tx channel are fixed at SoC design time. + */ +struct cppi41_tx_ch { + u8 port_num; /* Port number. */ + u8 ch_num; /* Channel number within port. */ + u8 sub_ch_num; /* Sub-channel number within channel. */ + u8 num_tx_queue; /* Number of queues from which the channel */ + /* can feed. */ + struct cppi41_queue tx_queue[4]; /* List of queues from which the */ + /* channel can feed. */ +}; + +/** + * struct cppi41_dma_block - CPPI 4.1 DMA configuration + * + * Configuration information for CPPI DMA functionality. Includes the Global + * configuration, Channel configuration, and the Scheduler configuration. + */ +struct cppi41_dma_block { + void __iomem *global_ctrl_base; /* Base address of the Global Control */ + /* registers. */ + void __iomem *ch_ctrl_stat_base; /* Base address of the Channel */ + /* Control/Status registers. */ + void __iomem *sched_ctrl_base; /* Base address of the Scheduler */ + /* Control register. */ + void __iomem *sched_table_base; /* Base address of the Scheduler */ + /* Table registers. */ + u8 num_tx_ch; /* Number of the Tx channels. */ + u8 num_rx_ch; /* Number of the Rx channels. */ + const struct cppi41_tx_ch *tx_ch_info; +}; + +extern struct cppi41_queue_mgr cppi41_queue_mgr[]; +extern struct cppi41_dma_block cppi41_dma_block[]; + +/** + * struct cppi41_dma_ch_obj - CPPI 4.1 DMA Channel object + */ +struct cppi41_dma_ch_obj { + void __iomem *base_addr; /* The address of the channel global */ + /* configuration register */ + u32 global_cfg; /* Tx/Rx global configuration backed-up value */ +}; + +/** + * struct cppi41_queue_obj - CPPI 4.1 queue object + */ +struct cppi41_queue_obj { + void __iomem *base_addr; /* The base address of the queue management */ + /* registers */ +}; + +/** + * cppi41_queue_mgr_init - CPPI 4.1 queue manager initialization. + * @q_mgr: the queue manager to initialize + * @rgn0_base: linking RAM region 0 physical address + * @rgn0_size: linking RAM region 0 size in 32-bit words (0 to 0x3fff) + * + * Returns 0 on success, error otherwise. + */ +int cppi41_queue_mgr_init(u8 q_mgr, dma_addr_t rgn0_base, u16 rgn0_size); + +/* + * CPPI 4.1 Queue Manager Memory Region Allocation and De-allocation APIs. + */ + +/** + * cppi41_mem_rgn_alloc - CPPI 4.1 queue manager memory region allocation. + * @q_mgr: the queue manager whose memory region to allocate + * @rgn_addr: physical address of the memory region + * @size_order: descriptor size as a power of two (between 5 and 13) + * @num_order: number of descriptors as a power of two (between 5 and 12) + * @mem_rgn: pointer to the index of the memory region allocated + * + * This function allocates a memory region within the queue manager + * consisiting of the descriptors of paricular size and number. + * + * Returns 0 on success, error otherwise. + */ +int cppi41_mem_rgn_alloc(u8 q_mgr, dma_addr_t rgn_addr, u8 size_order, + u8 num_order, u8 *mem_rgn); + +/** + * cppi41_mem_rgn_free - CPPI 4.1 queue manager memory region de-allocation. + * @q_mgr: the queue manager whose memory region was allocated + * @mem_rgn: index of the memory region + * + * This function frees the memory region allocated by cppi41_mem_rgn_alloc(). + * + * Returns 0 on success, -EINVAL otherwise. + */ +int cppi41_mem_rgn_free(u8 q_mgr, u8 mem_rgn); + +/** + * cppi41_dma_block_init - CPPI 4.1 DMA block initialization. + * @dma_num: number of the DMA block + * @q_mgr: the queue manager in which to allocate the free teardown + * descriptor queue + * @num_order: number of teardown descriptors as a power of two (at least 5) + * @sched_tbl: the DMA scheduler table + * @tbl_size: number of entries in the DMA scheduler table + * + * This function frees the memory region allocated by cppi41_mem_rgn_alloc(). + * + * Returns 0 on success, error otherwise. + */ +int cppi41_dma_block_init(u8 dma_num, u8 q_mgr, u8 num_order, + u32 *sched_tbl, u8 tbl_size); + +/* + * CPPI 4.1 DMA Channel Management APIs + */ + +/** + * cppi41_tx_ch_init - initialize CPPI 4.1 transmit channel object + * @tx_ch_obj: pointer to Tx channel object + * @dma_num: DMA block to which this channel belongs + * @ch_num: DMA channel number + * + * Returns 0 if valid Tx channel, -EINVAL otherwise. + */ +int cppi41_tx_ch_init(struct cppi41_dma_ch_obj *tx_ch_obj, + u8 dma_num, u8 ch_num); + +/** + * cppi41_rx_ch_init - initialize CPPI 4.1 receive channel object + * @rx_ch_obj: pointer to Rx channel object + * @dma_num: DMA block to which this channel belongs + * @ch_num: DMA channel number + * + * Returns 0 if valid Rx channel, -EINVAL otherwise. + */ +int cppi41_rx_ch_init(struct cppi41_dma_ch_obj *rx_ch_obj, + u8 dma_num, u8 ch_num); + +/** + * cppi41_dma_ch_default_queue - set CPPI 4.1 channel default completion queue + * @dma_ch_obj: pointer to DMA channel object + * @q_mgr: default queue manager + * @q_num: default queue number + * + * This function configures the specified channel. The caller is required to + * provide the default queue onto which the teardown descriptors will be queued. + */ +void cppi41_dma_ch_default_queue(struct cppi41_dma_ch_obj *dma_ch_obj, + u8 q_mgr, u16 q_num); + +/** + * cppi41_rx_ch_configure - configure CPPI 4.1 receive channel + * @rx_ch_obj: pointer to Rx channel object + * @cfg: pointer to Rx channel configuration + * + * This function configures and opens the specified Rx channel. The caller + * is required to provide channel configuration information by initializing + * a struct cppi41_rx_ch_cfg. + */ +void cppi41_rx_ch_configure(struct cppi41_dma_ch_obj *rx_ch_obj, + struct cppi41_rx_ch_cfg *cfg); + +/** + * cppi41_dma_ch_enable - enable CPPI 4.1 Tx/Rx DMA channel + * @dma_ch_obj: pointer to DMA channel object + * + * This function enables a specified Tx channel. The caller is required to + * provide a reference to a channel object initialized by an earlier call of + * the cppi41_dma_ch_init() function. After the successful completion of this + * function, the Tx DMA channel will be active and ready for data transmission. + */ +void cppi41_dma_ch_enable(struct cppi41_dma_ch_obj *dma_ch_obj); + +/** + * cppi41_dma_ch_disable - disable CPPI 4.1 Tx/Rx DMA channel + * @dma_ch_obj: pointer to DMA channel object + * + * This function disables a specific Tx channel. The caller is required to + * provide a reference to a channel object initialized by an earlier call of + * the cppi41_dma_ch_init() function. After the successful completion of this + * function, the Tx DMA channel will be deactived. + */ +void cppi41_dma_ch_disable(struct cppi41_dma_ch_obj *dma_ch_obj); + +/** + * cppi41_dma_ch_teardown - tear down CPPI 4.1 transmit channel + * @dma_ch_obj: pointer DMA channel object + * + * This function triggers the teardown of the given DMA channel. + * + * ATTENTION: Channel disable should not be called before the teardown is + * completed as a disable will stop the DMA scheduling on the channel resulting + * in the teardown complete event not being registered at all. + * + * NOTE: A successful channel teardown event is reported via queueing of a + * teardown descriptor. + * + * This function just sets up for the teardown of the channel and returns. The + * caller must detect the channel teardown event to assume that the channel is + * disabled. + * + * See cppi41_get_teardown_info() for the teardown completion processing. + */ +void cppi41_dma_ch_teardown(struct cppi41_dma_ch_obj *dma_ch_obj); + +/* + * CPPI 4.1 Queue Allocation and De-allocation APIs. + */ + +/** + * cppi41_queue_alloc - allocate CPPI 4.1 queue + * @type: queue type bitmask + * @q_mgr: queue manager + * @q_num: pointer to the queue number + * + * Returns 0 if queue allocated, error otherwise. + */ +int cppi41_queue_alloc(u8 type, u8 q_mgr, u16 *q_num); + +/** + * cppi41_queue_free - de-allocate CPPI 4.1 queue + * @q_mgr: queue manager + * @q_num: queue number + * + * Returns 0 on success, -EINVAL otherwise. + */ +int cppi41_queue_free(u8 q_mgr, u16 q_num); + +/* + * CPPI 4.1 Queue Management APIs + */ + +/** + * cppi41_queue_init - initialize CPPI 4.1 queue object + * @queue_obj: pointer to the queue object + * @q_mgr: queue manager + * @q_num: queue number + * + * Returns 0 if valid queue, -EINVAL otherwise. + */ +int cppi41_queue_init(struct cppi41_queue_obj *queue_obj, u8 q_mgr, u16 q_num); + +/** + * cppi41_queue_push - push to CPPI 4.1 queue + * @queue_obj: pointer to the queue object + * @desc_addr: descriptor physical address + * @desc_size: descriptor size + * @pkt_size: packet size + * + * This function is called to queue a descriptor onto a queue. + * NOTE: pSize parameter is optional. Pass 0 in case not required. + */ +void cppi41_queue_push(const struct cppi41_queue_obj *queue_obj, u32 desc_addr, + u32 desc_size, u32 pkt_size); + +/** + * cppi41_queue_pop - pop from CPPI 4.1 queue + * @queue_obj: pointer to the queue object + * + * This function is called to pop a single descriptor from the queue. + * + * Returns a packet descriptor's physical address. + */ +unsigned long cppi41_queue_pop(const struct cppi41_queue_obj *queue_obj); + +/* + * CPPI 4.1 Miscellaneous APIs + */ + +/** + * cppi41_get_teardown_info - CPPI 4.1 teardown completion processing function + * + * @addr: physical address of teardown descriptor + * @info: pointer to the teardown information word + * + * This function is called to complete the teardown processing on a channel + * and provides teardown information from the teardown descriptor passed to it. + * It also recycles the teardown descriptor back to the teardown descriptor + * queue. + * + * Returns 0 if valid descriptor, -EINVAL otherwise. + */ +int cppi41_get_teardown_info(unsigned long addr, u32 *info); + +/** + * cppi41_exit - delete the instance created via cppi41_init() + */ +void cppi41_exit(void); + +/** + * cppi41_dma_sched_tbl_init + */ +int cppi41_dma_sched_tbl_init(u8 dma_num, u8 q_mgr, + u32 *sched_tbl, u8 tbl_size); + +/** + * cppi41_free_teardown_queue + */ +void cppi41_free_teardown_queue(int dma_num); diff --git a/drivers/usb/musb/cppi41_dma.c b/drivers/usb/musb/cppi41_dma.c new file mode 100644 index 00000000000..98e49f28f0c --- /dev/null +++ b/drivers/usb/musb/cppi41_dma.c @@ -0,0 +1,1314 @@ +/* + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (c) 2008, MontaVista Software, Inc. + * + * This file implements a DMA interface using TI's CPPI 4.1 DMA. + * + * This program is free software; you can distribute 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 it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ + +#include +#include + +#include "cppi41.h" + +#include "musb_core.h" +#include "musb_dma.h" +#include "cppi41_dma.h" + +/* Configuration */ +#define USB_CPPI41_DESC_SIZE_SHIFT 6 +#define USB_CPPI41_DESC_ALIGN (1 << USB_CPPI41_DESC_SIZE_SHIFT) +#define USB_CPPI41_CH_NUM_PD 64 /* 4K bulk data at full speed */ +#define USB_CPPI41_MAX_PD (USB_CPPI41_CH_NUM_PD * USB_CPPI41_NUM_CH) + +#undef DEBUG_CPPI_TD +#undef USBDRV_DEBUG + +#ifdef USBDRV_DEBUG +#define dprintk(x, ...) printk(x, ## __VA_ARGS__) +#else +#define dprintk(x, ...) +#endif + +/* + * Data structure definitions + */ + +/* + * USB Packet Descriptor + */ +struct usb_pkt_desc; + +struct usb_pkt_desc { + /* Hardware descriptor fields from this point */ + struct cppi41_host_pkt_desc hw_desc; + /* Protocol specific data */ + dma_addr_t dma_addr; + struct usb_pkt_desc *next_pd_ptr; + u8 ch_num; + u8 ep_num; + u8 eop; +}; + +/** + * struct cppi41_channel - DMA Channel Control Structure + * + * Using the same for Tx/Rx. + */ +struct cppi41_channel { + struct dma_channel channel; + + struct cppi41_dma_ch_obj dma_ch_obj; /* DMA channel object */ + struct cppi41_queue src_queue; /* Tx queue or Rx free descriptor/ */ + /* buffer queue */ + struct cppi41_queue_obj queue_obj; /* Tx queue object or Rx free */ + /* descriptor/buffer queue object */ + + u32 tag_info; /* Tx PD Tag Information field */ + + /* Which direction of which endpoint? */ + struct musb_hw_ep *end_pt; + u8 transmit; + u8 ch_num; /* Channel number of Tx/Rx 0..3 */ + + /* DMA mode: "transparent", RNDIS, CDC, or Generic RNDIS */ + u8 dma_mode; + u8 autoreq; + + /* Book keeping for the current transfer request */ + dma_addr_t start_addr; + u32 length; + u32 curr_offset; + u16 pkt_size; + u8 transfer_mode; + u8 zlp_queued; +}; + +/** + * struct cppi41 - CPPI 4.1 DMA Controller Object + * + * Encapsulates all book keeping and data structures pertaining to + * the CPPI 1.4 DMA controller. + */ +struct cppi41 { + struct dma_controller controller; + struct musb *musb; + + struct cppi41_channel tx_cppi_ch[USB_CPPI41_NUM_CH]; + struct cppi41_channel rx_cppi_ch[USB_CPPI41_NUM_CH]; + + struct usb_pkt_desc *pd_pool_head; /* Free PD pool head */ + dma_addr_t pd_mem_phys; /* PD memory physical address */ + void *pd_mem; /* PD memory pointer */ + u8 pd_mem_rgn; /* PD memory region number */ + + u16 teardownQNum; /* Teardown completion queue number */ + struct cppi41_queue_obj queue_obj; /* Teardown completion queue */ + /* object */ + u32 pkt_info; /* Tx PD Packet Information field */ +}; + +#ifdef DEBUG_CPPI_TD +static void print_pd_list(struct usb_pkt_desc *pd_pool_head) +{ + struct usb_pkt_desc *curr_pd = pd_pool_head; + int cnt = 0; + + while (curr_pd != NULL) { + if (cnt % 8 == 0) + dprintk("\n%02x ", cnt); + cnt++; + dprintk(" %p", curr_pd); + curr_pd = curr_pd->next_pd_ptr; + } + dprintk("\n"); +} +#endif + +static struct usb_pkt_desc *usb_get_free_pd(struct cppi41 *cppi) +{ + struct usb_pkt_desc *free_pd = cppi->pd_pool_head; + + if (free_pd != NULL) { + cppi->pd_pool_head = free_pd->next_pd_ptr; + free_pd->next_pd_ptr = NULL; + } + return free_pd; +} + +static void usb_put_free_pd(struct cppi41 *cppi, struct usb_pkt_desc *free_pd) +{ + free_pd->next_pd_ptr = cppi->pd_pool_head; + cppi->pd_pool_head = free_pd; +} + +/** + * cppi41_controller_start - start DMA controller + * @controller: the controller + * + * This function initializes the CPPI 4.1 Tx/Rx channels. + */ +static int __init cppi41_controller_start(struct dma_controller *controller) +{ + struct cppi41 *cppi; + struct cppi41_channel *cppi_ch; + void __iomem *reg_base; + struct usb_pkt_desc *curr_pd; + unsigned long pd_addr; + int i; + + cppi = container_of(controller, struct cppi41, controller); + + /* + * TODO: We may need to check USB_CPPI41_MAX_PD here since CPPI 4.1 + * requires the descriptor count to be a multiple of 2 ^ 5 (i.e. 32). + * Similarly, the descriptor size should also be a multiple of 32. + */ + + /* + * Allocate free packet descriptor pool for all Tx/Rx endpoints -- + * dma_alloc_coherent() will return a page aligned address, so our + * alignment requirement will be honored. + */ + cppi->pd_mem = dma_alloc_coherent(cppi->musb->controller, + USB_CPPI41_MAX_PD * + USB_CPPI41_DESC_ALIGN, + &cppi->pd_mem_phys, + GFP_KERNEL | GFP_DMA); + if (cppi->pd_mem == NULL) { + DBG(1, "ERROR: packet descriptor memory allocation failed\n"); + return 0; + } + if (cppi41_mem_rgn_alloc(usb_cppi41_info.q_mgr, cppi->pd_mem_phys, + USB_CPPI41_DESC_SIZE_SHIFT, + get_count_order(USB_CPPI41_MAX_PD), + &cppi->pd_mem_rgn)) { + DBG(1, "ERROR: queue manager memory region allocation " + "failed\n"); + goto free_pds; + } + + /* Allocate the teardown completion queue */ + if (cppi41_queue_alloc(CPPI41_UNASSIGNED_QUEUE, + 0, &cppi->teardownQNum)) { + DBG(1, "ERROR: teardown completion queue allocation failed\n"); + goto free_mem_rgn; + } + DBG(4, "Allocated teardown completion queue %d in queue manager 0\n", + cppi->teardownQNum); + + if (cppi41_queue_init(&cppi->queue_obj, 0, cppi->teardownQNum)) { + DBG(1, "ERROR: teardown completion queue initialization " + "failed\n"); + goto free_queue; + } + + /* + * "Slice" PDs one-by-one from the big chunk and + * add them to the free pool. + */ + curr_pd = (struct usb_pkt_desc *)cppi->pd_mem; + pd_addr = cppi->pd_mem_phys; + for (i = 0; i < USB_CPPI41_MAX_PD; i++) { + curr_pd->dma_addr = pd_addr; + + usb_put_free_pd(cppi, curr_pd); + curr_pd = (struct usb_pkt_desc *)((char *)curr_pd + + USB_CPPI41_DESC_ALIGN); + pd_addr += USB_CPPI41_DESC_ALIGN; + } + + /* Configure the Tx channels */ + for (i = 0, cppi_ch = cppi->tx_cppi_ch; + i < ARRAY_SIZE(cppi->tx_cppi_ch); ++i, ++cppi_ch) { + const struct cppi41_tx_ch *tx_info; + + memset(cppi_ch, 0, sizeof(struct cppi41_channel)); + cppi_ch->transmit = 1; + cppi_ch->ch_num = i; + cppi_ch->channel.private_data = cppi; + + /* + * Extract the CPPI 4.1 DMA Tx channel configuration and + * construct/store the Tx PD tag info field for later use... + */ + tx_info = cppi41_dma_block[usb_cppi41_info.dma_block].tx_ch_info + + usb_cppi41_info.ep_dma_ch[i]; + cppi_ch->src_queue = tx_info->tx_queue[0]; + cppi_ch->tag_info = (tx_info->port_num << + CPPI41_SRC_TAG_PORT_NUM_SHIFT) | + (tx_info->ch_num << + CPPI41_SRC_TAG_CH_NUM_SHIFT) | + (tx_info->sub_ch_num << + CPPI41_SRC_TAG_SUB_CH_NUM_SHIFT); + } + + /* Configure the Rx channels */ + for (i = 0, cppi_ch = cppi->rx_cppi_ch; + i < ARRAY_SIZE(cppi->rx_cppi_ch); ++i, ++cppi_ch) { + memset(cppi_ch, 0, sizeof(struct cppi41_channel)); + cppi_ch->ch_num = i; + cppi_ch->channel.private_data = cppi; + } + + /* Construct/store Tx PD packet info field for later use */ + cppi->pkt_info = (CPPI41_PKT_TYPE_USB << CPPI41_PKT_TYPE_SHIFT) | + (CPPI41_RETURN_LINKED << CPPI41_RETURN_POLICY_SHIFT) | + (usb_cppi41_info.q_mgr << CPPI41_RETURN_QMGR_SHIFT) | + (usb_cppi41_info.tx_comp_q[0] << + CPPI41_RETURN_QNUM_SHIFT); + + /* Do necessary configuartion in hardware to get started */ + reg_base = cppi->musb->ctrl_base; + + /* Disable auto request mode */ + musb_writel(reg_base, USB_AUTOREQ_REG, 0); + + /* Disable the CDC/RNDIS modes */ + musb_writel(reg_base, USB_TX_MODE_REG, 0); + musb_writel(reg_base, USB_RX_MODE_REG, 0); + + return 1; + + free_queue: + if (cppi41_queue_free(0, cppi->teardownQNum)) + DBG(1, "ERROR: failed to free teardown completion queue\n"); + + free_mem_rgn: + if (cppi41_mem_rgn_free(usb_cppi41_info.q_mgr, cppi->pd_mem_rgn)) + DBG(1, "ERROR: failed to free queue manager memory region\n"); + + free_pds: + dma_free_coherent(cppi->musb->controller, + USB_CPPI41_MAX_PD * USB_CPPI41_DESC_ALIGN, + cppi->pd_mem, cppi->pd_mem_phys); + + return 0; +} + +/** + * cppi41_controller_stop - stop DMA controller + * @controller: the controller + * + * De-initialize the DMA Controller as necessary. + */ +static int cppi41_controller_stop(struct dma_controller *controller) +{ + struct cppi41 *cppi; + void __iomem *reg_base; + + cppi = container_of(controller, struct cppi41, controller); + + /* + * pop all the teardwon descriptor queued to tdQueue + */ + cppi41_free_teardown_queue(0); + + /* Free the teardown completion queue */ + if (cppi41_queue_free(usb_cppi41_info.q_mgr, cppi->teardownQNum)) + DBG(1, "ERROR: failed to free teardown completion queue\n"); + + /* + * Free the packet descriptor region allocated + * for all Tx/Rx channels. + */ + if (cppi41_mem_rgn_free(usb_cppi41_info.q_mgr, cppi->pd_mem_rgn)) + DBG(1, "ERROR: failed to free queue manager memory region\n"); + + dma_free_coherent(cppi->musb->controller, + USB_CPPI41_MAX_PD * USB_CPPI41_DESC_ALIGN, + cppi->pd_mem, cppi->pd_mem_phys); + + reg_base = cppi->musb->ctrl_base; + + /* Disable auto request mode */ + musb_writel(reg_base, USB_AUTOREQ_REG, 0); + + /* Disable the CDC/RNDIS modes */ + musb_writel(reg_base, USB_TX_MODE_REG, 0); + musb_writel(reg_base, USB_RX_MODE_REG, 0); + + return 1; +} + +/** + * cppi41_channel_alloc - allocate a CPPI channel for DMA. + * @controller: the controller + * @ep: the endpoint + * @is_tx: 1 for Tx channel, 0 for Rx channel + * + * With CPPI, channels are bound to each transfer direction of a non-control + * endpoint, so allocating (and deallocating) is mostly a way to notice bad + * housekeeping on the software side. We assume the IRQs are always active. + */ +static struct dma_channel *cppi41_channel_alloc(struct dma_controller + *controller, + struct musb_hw_ep *ep, u8 is_tx) +{ + struct cppi41 *cppi; + struct cppi41_channel *cppi_ch; + u32 ch_num, ep_num = ep->epnum; + + cppi = container_of(controller, struct cppi41, controller); + + /* Remember, ep_num: 1 .. Max_EP, and CPPI ch_num: 0 .. Max_EP - 1 */ + ch_num = ep_num - 1; + + if (ep_num > USB_CPPI41_NUM_CH) { + DBG(1, "No %cx DMA channel for EP%d\n", + is_tx ? 'T' : 'R', ep_num); + return NULL; + } + + cppi_ch = (is_tx ? cppi->tx_cppi_ch : cppi->rx_cppi_ch) + ch_num; + + /* As of now, just return the corresponding CPPI 4.1 channel handle */ + if (is_tx) { + /* Initialize the CPPI 4.1 Tx DMA channel */ + if (cppi41_tx_ch_init(&cppi_ch->dma_ch_obj, + usb_cppi41_info.dma_block, + usb_cppi41_info.ep_dma_ch[ch_num])) { + DBG(1, "ERROR: cppi41_tx_ch_init failed for " + "channel %d\n", ch_num); + return NULL; + } + /* + * Teardown descriptors will be pushed to the dedicated + * completion queue. + */ + cppi41_dma_ch_default_queue(&cppi_ch->dma_ch_obj, + 0, cppi->teardownQNum); + } else { + struct cppi41_rx_ch_cfg rx_cfg; + u8 q_mgr = usb_cppi41_info.q_mgr; + int i; + + /* Initialize the CPPI 4.1 Rx DMA channel */ + if (cppi41_rx_ch_init(&cppi_ch->dma_ch_obj, + usb_cppi41_info.dma_block, + usb_cppi41_info.ep_dma_ch[ch_num])) { + DBG(1, "ERROR: cppi41_rx_ch_init failed\n"); + return NULL; + } + + if (cppi41_queue_alloc(CPPI41_FREE_DESC_BUF_QUEUE | + CPPI41_UNASSIGNED_QUEUE, + q_mgr, &cppi_ch->src_queue.q_num)) { + DBG(1, "ERROR: cppi41_queue_alloc failed for " + "free descriptor/buffer queue\n"); + return NULL; + } + DBG(4, "Allocated free descriptor/buffer queue %d in " + "queue manager %d\n", cppi_ch->src_queue.q_num, q_mgr); + + rx_cfg.default_desc_type = cppi41_rx_host_desc; + rx_cfg.sop_offset = 0; + rx_cfg.retry_starved = 1; + rx_cfg.rx_queue.q_mgr = cppi_ch->src_queue.q_mgr = q_mgr; + rx_cfg.rx_queue.q_num = usb_cppi41_info.rx_comp_q[0]; + for (i = 0; i < 4; i++) + rx_cfg.cfg.host_pkt.fdb_queue[i] = cppi_ch->src_queue; + cppi41_rx_ch_configure(&cppi_ch->dma_ch_obj, &rx_cfg); + } + + /* Initialize the CPPI 4.1 DMA source queue */ + if (cppi41_queue_init(&cppi_ch->queue_obj, cppi_ch->src_queue.q_mgr, + cppi_ch->src_queue.q_num)) { + DBG(1, "ERROR: cppi41_queue_init failed for %s queue", + is_tx ? "Tx" : "Rx free descriptor/buffer"); + if (is_tx == 0 && + cppi41_queue_free(cppi_ch->src_queue.q_mgr, + cppi_ch->src_queue.q_num)) + DBG(1, "ERROR: failed to free Rx descriptor/buffer " + "queue\n"); + return NULL; + } + + /* Enable the DMA channel */ + cppi41_dma_ch_enable(&cppi_ch->dma_ch_obj); + + if (cppi_ch->end_pt) + DBG(1, "Re-allocating DMA %cx channel %d (%p)\n", + is_tx ? 'T' : 'R', ch_num, cppi_ch); + + cppi_ch->end_pt = ep; + cppi_ch->ch_num = ch_num; + cppi_ch->channel.status = MUSB_DMA_STATUS_FREE; + + DBG(4, "Allocated DMA %cx channel %d for EP%d\n", is_tx ? 'T' : 'R', + ch_num, ep_num); + + return &cppi_ch->channel; +} + +/** + * cppi41_channel_release - release a CPPI DMA channel + * @channel: the channel + */ +static void cppi41_channel_release(struct dma_channel *channel) +{ + struct cppi41_channel *cppi_ch; + + /* REVISIT: for paranoia, check state and abort if needed... */ + cppi_ch = container_of(channel, struct cppi41_channel, channel); + if (cppi_ch->end_pt == NULL) + DBG(1, "Releasing idle DMA channel %p\n", cppi_ch); + + /* But for now, not its IRQ */ + cppi_ch->end_pt = NULL; + channel->status = MUSB_DMA_STATUS_UNKNOWN; + + cppi41_dma_ch_disable(&cppi_ch->dma_ch_obj); + + /* De-allocate Rx free descriptior/buffer queue */ + if (cppi_ch->transmit == 0 && + cppi41_queue_free(cppi_ch->src_queue.q_mgr, + cppi_ch->src_queue.q_num)) + DBG(1, "ERROR: failed to free Rx descriptor/buffer queue\n"); +} + +static void cppi41_mode_update(struct cppi41_channel *cppi_ch, u8 mode) +{ + if (mode != cppi_ch->dma_mode) { + struct cppi41 *cppi = cppi_ch->channel.private_data; + void *__iomem reg_base = cppi->musb->ctrl_base; + u32 reg_val; + u8 ep_num = cppi_ch->ch_num + 1; + + if (cppi_ch->transmit) { + reg_val = musb_readl(reg_base, USB_TX_MODE_REG); + reg_val &= ~USB_TX_MODE_MASK(ep_num); + reg_val |= mode << USB_TX_MODE_SHIFT(ep_num); + musb_writel(reg_base, USB_TX_MODE_REG, reg_val); + } else { + reg_val = musb_readl(reg_base, USB_RX_MODE_REG); + reg_val &= ~USB_RX_MODE_MASK(ep_num); + reg_val |= mode << USB_RX_MODE_SHIFT(ep_num); + musb_writel(reg_base, USB_RX_MODE_REG, reg_val); + } + cppi_ch->dma_mode = mode; + } +} + +/* + * CPPI 4.1 Tx: + * ============ + * Tx is a lot more reasonable than Rx: RNDIS mode seems to behave well except + * how it handles the exactly-N-packets case. It appears that there's a hiccup + * in that case (maybe the DMA completes before a ZLP gets written?) boiling + * down to not being able to rely on the XFER DMA writing any terminating zero + * length packet before the next transfer is started... + * + * The generic RNDIS mode does not have this misfeature, so we prefer using it + * instead. We then send the terminating ZLP *explictly* using DMA instead of + * doing it by PIO after an IRQ. + * + */ + +/** + * cppi41_next_tx_segment - DMA write for the next chunk of a buffer + * @tx_ch: Tx channel + * + * Context: controller IRQ-locked + */ +static unsigned cppi41_next_tx_segment(struct cppi41_channel *tx_ch) +{ + struct cppi41 *cppi = tx_ch->channel.private_data; + struct usb_pkt_desc *curr_pd; + u32 length = tx_ch->length - tx_ch->curr_offset; + u32 pkt_size = tx_ch->pkt_size; + unsigned num_pds, n; + + /* + * Tx can use the generic RNDIS mode where we can probably fit this + * transfer in one PD and one IRQ. The only time we would NOT want + * to use it is when the hardware constraints prevent it... + */ + if ((pkt_size & 0x3f) == 0 && length > pkt_size) { + num_pds = 1; + pkt_size = length; + cppi41_mode_update(tx_ch, USB_GENERIC_RNDIS_MODE); + } else { + num_pds = (length + pkt_size - 1) / pkt_size; + cppi41_mode_update(tx_ch, USB_TRANSPARENT_MODE); + } + + /* + * If length of transmit buffer is 0 or a multiple of the endpoint size, + * then send the zero length packet. + */ + if (!length || (tx_ch->transfer_mode && length % pkt_size == 0)) + num_pds++; + + DBG(4, "TX DMA%u, %s, maxpkt %u, %u PDs, addr %#x, len %u\n", + tx_ch->ch_num, tx_ch->dma_mode ? "accelerated" : "transparent", + pkt_size, num_pds, tx_ch->start_addr + tx_ch->curr_offset, length); + + for (n = 0; n < num_pds; n++) { + struct cppi41_host_pkt_desc *hw_desc; + + /* Get Tx host packet descriptor from the free pool */ + curr_pd = usb_get_free_pd(cppi); + if (curr_pd == NULL) { + DBG(1, "No Tx PDs\n"); + break; + } + + if (length < pkt_size) + pkt_size = length; + + hw_desc = &curr_pd->hw_desc; + hw_desc->desc_info = (CPPI41_DESC_TYPE_HOST << + CPPI41_DESC_TYPE_SHIFT) | pkt_size; + hw_desc->tag_info = tx_ch->tag_info; + hw_desc->pkt_info = cppi->pkt_info; + + hw_desc->buf_ptr = tx_ch->start_addr + tx_ch->curr_offset; + hw_desc->buf_len = pkt_size; + hw_desc->next_desc_ptr = 0; + + curr_pd->ch_num = tx_ch->ch_num; + curr_pd->ep_num = tx_ch->end_pt->epnum; + + tx_ch->curr_offset += pkt_size; + length -= pkt_size; + + if (pkt_size == 0) + tx_ch->zlp_queued = 1; + + DBG(5, "TX PD %p: buf %08x, len %08x, pkt info %08x\n", curr_pd, + hw_desc->buf_ptr, hw_desc->buf_len, hw_desc->pkt_info); + + cppi41_queue_push(&tx_ch->queue_obj, curr_pd->dma_addr, + USB_CPPI41_DESC_ALIGN, pkt_size); + } + + return n; +} + +static void cppi41_autoreq_update(struct cppi41_channel *rx_ch, u8 autoreq) +{ + struct cppi41 *cppi = rx_ch->channel.private_data; + + if (is_host_active(cppi->musb) && + autoreq != rx_ch->autoreq) { + void *__iomem reg_base = cppi->musb->ctrl_base; + u32 reg_val = musb_readl(reg_base, USB_AUTOREQ_REG); + u8 ep_num = rx_ch->ch_num + 1; + + reg_val &= ~USB_RX_AUTOREQ_MASK(ep_num); + reg_val |= autoreq << USB_RX_AUTOREQ_SHIFT(ep_num); + + musb_writel(reg_base, USB_AUTOREQ_REG, reg_val); + rx_ch->autoreq = autoreq; + } +} + +static void cppi41_set_ep_size(struct cppi41_channel *rx_ch, u32 pkt_size) +{ + struct cppi41 *cppi = rx_ch->channel.private_data; + void *__iomem reg_base = cppi->musb->ctrl_base; + u8 ep_num = rx_ch->ch_num + 1; + + musb_writel(reg_base, USB_GENERIC_RNDIS_EP_SIZE_REG(ep_num), pkt_size); +} + +/* + * CPPI 4.1 Rx: + * ============ + * Consider a 1KB bulk Rx buffer in two scenarios: (a) it's fed two 300 byte + * packets back-to-back, and (b) it's fed two 512 byte packets back-to-back. + * (Full speed transfers have similar scenarios.) + * + * The correct behavior for Linux is that (a) fills the buffer with 300 bytes, + * and the next packet goes into a buffer that's queued later; while (b) fills + * the buffer with 1024 bytes. How to do that with accelerated DMA modes? + * + * Rx queues in RNDIS mode (one single BD) handle (a) correctly but (b) loses + * BADLY because nothing (!) happens when that second packet fills the buffer, + * much less when a third one arrives -- which makes it not a "true" RNDIS mode. + * In the RNDIS protocol short-packet termination is optional, and it's fine if + * the peripherals (not hosts!) pad the messages out to end of buffer. Standard + * PCI host controller DMA descriptors implement that mode by default... which + * is no accident. + * + * Generic RNDIS mode is the only way to reliably make both cases work. This + * mode is identical to the "normal" RNDIS mode except for the case where the + * last packet of the segment matches the max USB packet size -- in this case, + * the packet will be closed when a value (0x10000 max) in the Generic RNDIS + * EP Size register is reached. This mode will work for the network drivers + * (CDC/RNDIS) as well as for the mass storage drivers where there is no short + * packet. + * + * BUT we can only use non-transparent modes when USB packet size is a multiple + * of 64 bytes. Let's see what happens when this is not the case... + * + * Rx queues (2 BDs with 512 bytes each) have converse problems to RNDIS mode: + * (b) is handled right but (a) loses badly. DMA doesn't stop after receiving + * a short packet and processes both of those PDs; so both packets are loaded + * into the buffer (with 212 byte gap between them), and the next buffer queued + * will NOT get its 300 bytes of data. Even in the case when there should be + * no short packets (URB_SHORT_NOT_OK is set), queueing several packets in the + * host mode doesn't win us anything since we have to manually "prod" the Rx + * process after each packet is received by setting ReqPkt bit in endpoint's + * RXCSR; in the peripheral mode without short packets, queueing could be used + * BUT we'll have to *teardown* the channel if a short packet still arrives in + * the peripheral mode, and to "collect" the left-over packet descriptors from + * the free descriptor/buffer queue in both cases... + * + * One BD at a time is the only way to make make both cases work reliably, with + * software handling both cases correctly, at the significant penalty of needing + * an IRQ per packet. (The lack of I/O overlap can be slightly ameliorated by + * enabling double buffering.) + * + * There seems to be no way to identify for sure the cases where the CDC mode + * is appropriate... + * + */ + +/** + * cppi41_next_rx_segment - DMA read for the next chunk of a buffer + * @rx_ch: Rx channel + * + * Context: controller IRQ-locked + * + * NOTE: In the transparent mode, we have to queue one packet at a time since: + * - we must avoid starting reception of another packet after receiving + * a short packet; + * - in host mode we have to set ReqPkt bit in the endpoint's RXCSR after + * receiving each packet but the last one... ugly! + */ +static unsigned cppi41_next_rx_segment(struct cppi41_channel *rx_ch) +{ + struct cppi41 *cppi = rx_ch->channel.private_data; + struct usb_pkt_desc *curr_pd; + struct cppi41_host_pkt_desc *hw_desc; + u32 length = rx_ch->length - rx_ch->curr_offset; + u32 pkt_size = rx_ch->pkt_size; + u32 max_rx_transfer_size = 128 * 1024; + u32 i, n_bd , pkt_len; + struct usb_gadget_driver *gadget_driver; + + if (is_peripheral_active(cppi->musb)) { + /* TODO: temporary fix for CDC/RNDIS which needs to be in + * GENERIC_RNDIS mode. Without this RNDIS gadget taking + * more then 2K ms for a 64 byte pings. + */ +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + gadget_driver = cppi->musb->gadget_driver; +#endif + if (!strcmp(gadget_driver->driver.name, "g_ether")) { + cppi41_mode_update(rx_ch, USB_GENERIC_RNDIS_MODE); + } else { + max_rx_transfer_size = 512; + cppi41_mode_update(rx_ch, USB_TRANSPARENT_MODE); + } + pkt_len = 0; + if (rx_ch->length < max_rx_transfer_size) + pkt_len = rx_ch->length; + cppi41_set_ep_size(rx_ch, pkt_len); + } else { + /* + * Rx can use the generic RNDIS mode where we can + * probably fit this transfer in one PD and one IRQ + * (or two with a short packet). + */ + if ((pkt_size & 0x3f) == 0 && length >= 2 * pkt_size) { + cppi41_mode_update(rx_ch, USB_GENERIC_RNDIS_MODE); + cppi41_autoreq_update(rx_ch, USB_AUTOREQ_ALL_BUT_EOP); + + if (likely(length < 0x10000)) + pkt_size = length - length % pkt_size; + else + pkt_size = 0x10000; + cppi41_set_ep_size(rx_ch, pkt_size); + } else { + cppi41_mode_update(rx_ch, USB_TRANSPARENT_MODE); + cppi41_autoreq_update(rx_ch, USB_NO_AUTOREQ); + } + } + + DBG(4, "RX DMA%u, %s, maxpkt %u, addr %#x, rec'd %u/%u\n", + rx_ch->ch_num, rx_ch->dma_mode ? "accelerated" : "transparent", + pkt_size, rx_ch->start_addr + rx_ch->curr_offset, + rx_ch->curr_offset, rx_ch->length); + + /* calculate number of bd required */ + n_bd = (length + max_rx_transfer_size - 1)/max_rx_transfer_size; + + for (i = 0; i < n_bd ; ++i) { + /* Get Rx packet descriptor from the free pool */ + curr_pd = usb_get_free_pd(cppi); + if (curr_pd == NULL) { + /* Shouldn't ever happen! */ + DBG(4, "No Rx PDs\n"); + goto sched; + } + + pkt_len = + (length > max_rx_transfer_size) ? max_rx_transfer_size : length; + + hw_desc = &curr_pd->hw_desc; + hw_desc->orig_buf_ptr = rx_ch->start_addr + rx_ch->curr_offset; + hw_desc->orig_buf_len = pkt_len; + + curr_pd->ch_num = rx_ch->ch_num; + curr_pd->ep_num = rx_ch->end_pt->epnum; + + curr_pd->eop = (length -= pkt_len) ? 0 : 1; + rx_ch->curr_offset += pkt_len; + + /* + * Push the free Rx packet descriptor + * to the free descriptor/buffer queue. + */ + cppi41_queue_push(&rx_ch->queue_obj, curr_pd->dma_addr, + USB_CPPI41_DESC_ALIGN, 0); + } + +sched: + /* + * HCD arranged ReqPkt for the first packet. + * We arrange it for all but the last one. + */ + if (is_host_active(cppi->musb) && rx_ch->channel.actual_len) { + void __iomem *epio = rx_ch->end_pt->regs; + u16 csr = musb_readw(epio, MUSB_RXCSR); + + csr |= MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_H_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + } + + /* enable schedular if not enabled */ + if (is_peripheral_active(cppi->musb) && (n_bd > 0)) + cppi41_enable_sched_rx(); + return 1; +} + +/** + * cppi41_channel_program - program channel for data transfer + * @channel: the channel + * @maxpacket: max packet size + * @mode: for Rx, 1 unless the USB protocol driver promised to treat + * all short reads as errors and kick in high level fault recovery; + * for Tx, 0 unless the protocol driver _requires_ short-packet + * termination mode + * @dma_addr: DMA address of buffer + * @length: length of buffer + * + * Context: controller IRQ-locked + */ +static int cppi41_channel_program(struct dma_channel *channel, u16 maxpacket, + u8 mode, dma_addr_t dma_addr, u32 length) +{ + struct cppi41_channel *cppi_ch; + unsigned queued; + + cppi_ch = container_of(channel, struct cppi41_channel, channel); + + switch (channel->status) { + case MUSB_DMA_STATUS_BUS_ABORT: + case MUSB_DMA_STATUS_CORE_ABORT: + /* Fault IRQ handler should have handled cleanup */ + WARNING("%cx DMA%d not cleaned up after abort!\n", + cppi_ch->transmit ? 'T' : 'R', cppi_ch->ch_num); + break; + case MUSB_DMA_STATUS_BUSY: + WARNING("Program active channel? %cx DMA%d\n", + cppi_ch->transmit ? 'T' : 'R', cppi_ch->ch_num); + break; + case MUSB_DMA_STATUS_UNKNOWN: + DBG(1, "%cx DMA%d not allocated!\n", + cppi_ch->transmit ? 'T' : 'R', cppi_ch->ch_num); + return 0; + case MUSB_DMA_STATUS_FREE: + break; + } + + channel->status = MUSB_DMA_STATUS_BUSY; + + /* Set the transfer parameters, then queue up the first segment */ + cppi_ch->start_addr = dma_addr; + cppi_ch->curr_offset = 0; + cppi_ch->pkt_size = maxpacket; + cppi_ch->length = length; + cppi_ch->transfer_mode = mode; + cppi_ch->zlp_queued = 0; + cppi_ch->channel.actual_len = 0; + + /* Tx or Rx channel? */ + if (cppi_ch->transmit) + queued = cppi41_next_tx_segment(cppi_ch); + else + queued = cppi41_next_rx_segment(cppi_ch); + + return queued > 0; +} + +static struct usb_pkt_desc *usb_get_pd_ptr(struct cppi41 *cppi, + unsigned long pd_addr) +{ + if (pd_addr >= cppi->pd_mem_phys && pd_addr < cppi->pd_mem_phys + + USB_CPPI41_MAX_PD * USB_CPPI41_DESC_ALIGN) + return pd_addr - cppi->pd_mem_phys + cppi->pd_mem; + else + return NULL; +} + +static int usb_check_teardown(struct cppi41_channel *cppi_ch, + unsigned long pd_addr) +{ + u32 info; + + if (cppi41_get_teardown_info(pd_addr, &info)) { + DBG(1, "ERROR: not a teardown descriptor\n"); + return 0; + } + + if ((info & CPPI41_TEARDOWN_TX_RX_MASK) == + (!cppi_ch->transmit << CPPI41_TEARDOWN_TX_RX_SHIFT) && + (info & CPPI41_TEARDOWN_DMA_NUM_MASK) == + (usb_cppi41_info.dma_block << CPPI41_TEARDOWN_DMA_NUM_SHIFT) && + (info & CPPI41_TEARDOWN_CHAN_NUM_MASK) == + (usb_cppi41_info.ep_dma_ch[cppi_ch->ch_num] << + CPPI41_TEARDOWN_CHAN_NUM_SHIFT)) + return 1; + + DBG(1, "ERROR: unexpected values in teardown descriptor\n"); + return 0; +} + +/* + * We can't handle the channel teardown via the default completion queue in + * context of the controller IRQ-locked, so we use the dedicated teardown + * completion queue which we can just poll for a teardown descriptor, not + * interfering with the Tx completion queue processing. + */ +static void usb_tx_ch_teardown(struct cppi41_channel *tx_ch) +{ + struct cppi41 *cppi = tx_ch->channel.private_data; + struct musb *musb = cppi->musb; + void __iomem *reg_base = musb->ctrl_base; + u32 td_reg, timeout = 0xfffff; + u8 ep_num = tx_ch->ch_num + 1; + unsigned long pd_addr; + + /* Initiate teardown for Tx DMA channel */ + cppi41_dma_ch_teardown(&tx_ch->dma_ch_obj); + + /* Wait for a descriptor to be queued and pop it... */ + do { + td_reg = musb_readl(reg_base, USB_TEARDOWN_REG); + td_reg |= USB_TX_TDOWN_MASK(ep_num); + musb_writel(reg_base, USB_TEARDOWN_REG, td_reg); + + pd_addr = cppi41_queue_pop(&cppi->queue_obj); + } while (!pd_addr && timeout--); + + if (pd_addr) { + + dprintk("Descriptor (%08lx) popped from teardown completion " + "queue\n", pd_addr); + + if (usb_check_teardown(tx_ch, pd_addr)) { + dprintk("Teardown Desc (%p) rcvd\n", pd_addr); + } else + ERR("Invalid PD(%08lx)popped from TearDn completion" + "queue\n", pd_addr); + } else { + if (timeout <= 0) + ERR("Teardown Desc not rcvd\n"); + } +} + +/* + * For Rx DMA channels, the situation is more complex: there's only a single + * completion queue for all our needs, so we have to temporarily redirect the + * completed descriptors to our teardown completion queue, with a possibility + * of a completed packet landing there as well... + */ +static void usb_rx_ch_teardown(struct cppi41_channel *rx_ch) +{ + struct cppi41 *cppi = rx_ch->channel.private_data; + u32 timeout = 0xfffff; + + cppi41_dma_ch_default_queue(&rx_ch->dma_ch_obj, 0, cppi->teardownQNum); + + /* Initiate teardown for Rx DMA channel */ + cppi41_dma_ch_teardown(&rx_ch->dma_ch_obj); + + do { + struct usb_pkt_desc *curr_pd; + unsigned long pd_addr; + + /* Wait for a descriptor to be queued and pop it... */ + do { + pd_addr = cppi41_queue_pop(&cppi->queue_obj); + } while (!pd_addr && timeout--); + + if (timeout <= 0) { + ERR("teardown Desc not found\n"); + break; + } + + dprintk("Descriptor (%08lx) popped from teardown completion " + "queue\n", pd_addr); + + /* + * We might have popped a completed Rx PD, so check if the + * physical address is within the PD region first. If it's + * not the case, it must be a teardown descriptor... + * */ + curr_pd = usb_get_pd_ptr(cppi, pd_addr); + if (curr_pd == NULL) { + if (usb_check_teardown(rx_ch, pd_addr)) + break; + continue; + } + + /* Paranoia: check if PD is from the right channel... */ + if (curr_pd->ch_num != rx_ch->ch_num) { + ERR("Unexpected channel %d in Rx PD\n", + curr_pd->ch_num); + continue; + } + + /* Extract the buffer length from the completed PD */ + rx_ch->channel.actual_len += curr_pd->hw_desc.buf_len; + + /* + * Return Rx PDs to the software list -- + * this is protected by critical section. + */ + usb_put_free_pd(cppi, curr_pd); + } while (0); + + /* Now restore the default Rx completion queue... */ + cppi41_dma_ch_default_queue(&rx_ch->dma_ch_obj, usb_cppi41_info.q_mgr, + usb_cppi41_info.rx_comp_q[0]); +} + +/* + * cppi41_channel_abort + * + * Context: controller IRQ-locked, endpoint selected. + */ +static int cppi41_channel_abort(struct dma_channel *channel) +{ + struct cppi41 *cppi; + struct cppi41_channel *cppi_ch; + struct musb *musb; + void __iomem *reg_base, *epio; + unsigned long pd_addr; + u32 csr, td_reg; + u8 ch_num, ep_num; + + cppi_ch = container_of(channel, struct cppi41_channel, channel); + ch_num = cppi_ch->ch_num; + + switch (channel->status) { + case MUSB_DMA_STATUS_BUS_ABORT: + case MUSB_DMA_STATUS_CORE_ABORT: + /* From Rx or Tx fault IRQ handler */ + case MUSB_DMA_STATUS_BUSY: + /* The hardware needs shutting down... */ + dprintk("%s: DMA busy, status = %x\n", + __func__, channel->status); + break; + case MUSB_DMA_STATUS_UNKNOWN: + DBG(1, "%cx DMA%d not allocated\n", + cppi_ch->transmit ? 'T' : 'R', ch_num); + /* FALLTHROUGH */ + case MUSB_DMA_STATUS_FREE: + return 0; + } + + cppi = cppi_ch->channel.private_data; + musb = cppi->musb; + reg_base = musb->ctrl_base; + epio = cppi_ch->end_pt->regs; + ep_num = ch_num + 1; + +#ifdef DEBUG_CPPI_TD + printk("Before teardown:"); + print_pd_list(cppi->pd_pool_head); +#endif + + if (cppi_ch->transmit) { + dprintk("Tx channel teardown, cppi_ch = %p\n", cppi_ch); + + /* Tear down Tx DMA channel */ + usb_tx_ch_teardown(cppi_ch); + + /* Issue CPPI FIFO teardown for Tx channel */ + td_reg = musb_readl(reg_base, USB_TEARDOWN_REG); + td_reg |= USB_TX_TDOWN_MASK(ep_num); + musb_writel(reg_base, USB_TEARDOWN_REG, td_reg); + + /* Flush FIFO of the endpoint */ + csr = musb_readw(epio, MUSB_TXCSR); + csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_H_WZC_BITS; + musb_writew(epio, MUSB_TXCSR, csr); + musb_writew(epio, MUSB_TXCSR, csr); + } else { /* Rx */ + dprintk("Rx channel teardown, cppi_ch = %p\n", cppi_ch); + + /* Flush FIFO of the endpoint */ + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_H_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + + /* Issue CPPI FIFO teardown for Rx channel */ + td_reg = musb_readl(reg_base, USB_TEARDOWN_REG); + td_reg |= USB_RX_TDOWN_MASK(ep_num); + musb_writel(reg_base, USB_TEARDOWN_REG, td_reg); + + /* Tear down Rx DMA channel */ + usb_rx_ch_teardown(cppi_ch); + + /* + * NOTE: docs don't guarantee any of this works... we expect + * that if the USB core stops telling the CPPI core to pull + * more data from it, then it'll be safe to flush current Rx + * DMA state iff any pending FIFO transfer is done. + */ + + /* For host, ensure ReqPkt is never set again */ + cppi41_autoreq_update(cppi_ch, USB_NO_AUTOREQ); + + /* For host, clear (just) ReqPkt at end of current packet(s) */ + if (is_host_active(cppi->musb)) + csr &= ~MUSB_RXCSR_H_REQPKT; + csr |= MUSB_RXCSR_H_WZC_BITS; + + /* Clear DMA enable */ + csr &= ~MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + + /* Flush the FIFO of endpoint once again */ + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_H_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + + udelay(50); + } + + /* + * There might be PDs in the Rx/Tx source queue that were not consumed + * by the DMA controller -- they need to be recycled properly. + */ + while ((pd_addr = cppi41_queue_pop(&cppi_ch->queue_obj)) != 0) { + struct usb_pkt_desc *curr_pd; + + curr_pd = usb_get_pd_ptr(cppi, pd_addr); + if (curr_pd == NULL) { + ERR("Invalid PD popped from source queue\n"); + continue; + } + + /* + * Return Rx/Tx PDs to the software list -- + * this is protected by critical section. + */ + dprintk("Returning PD %p to the free PD list\n", curr_pd); + usb_put_free_pd(cppi, curr_pd); + } + +#ifdef DEBUG_CPPI_TD + printk("After teardown:"); + print_pd_list(cppi->pd_pool_head); +#endif + + /* Re-enable the DMA channel */ + cppi41_dma_ch_enable(&cppi_ch->dma_ch_obj); + + channel->status = MUSB_DMA_STATUS_FREE; + + return 0; +} + +/** + * dma_controller_create - instantiate an object representing DMA controller. + */ +struct dma_controller * __init dma_controller_create(struct musb *musb, + void __iomem *mregs) +{ + struct cppi41 *cppi; + + cppi = kzalloc(sizeof *cppi, GFP_KERNEL); + if (!cppi) + return NULL; + + /* Initialize the CPPI 4.1 DMA controller structure */ + cppi->musb = musb; + cppi->controller.start = cppi41_controller_start; + cppi->controller.stop = cppi41_controller_stop; + cppi->controller.channel_alloc = cppi41_channel_alloc; + cppi->controller.channel_release = cppi41_channel_release; + cppi->controller.channel_program = cppi41_channel_program; + cppi->controller.channel_abort = cppi41_channel_abort; + + return &cppi->controller; +} + +/** + * dma_controller_destroy - destroy a previously instantiated DMA controller + * @controller: the controller + */ +void dma_controller_destroy(struct dma_controller *controller) +{ + struct cppi41 *cppi; + + cppi = container_of(controller, struct cppi41, controller); + + /* Free the CPPI object */ + kfree(cppi); +} + +static void usb_process_tx_queue(struct cppi41 *cppi, unsigned index) +{ + struct cppi41_queue_obj tx_queue_obj; + unsigned long pd_addr; + + if (cppi41_queue_init(&tx_queue_obj, usb_cppi41_info.q_mgr, + usb_cppi41_info.tx_comp_q[index])) { + DBG(1, "ERROR: cppi41_queue_init failed for " + "Tx completion queue"); + return; + } + + while ((pd_addr = cppi41_queue_pop(&tx_queue_obj)) != 0) { + struct usb_pkt_desc *curr_pd; + struct cppi41_channel *tx_ch; + u8 ch_num, ep_num; + u32 length; + + curr_pd = usb_get_pd_ptr(cppi, pd_addr); + if (curr_pd == NULL) { + ERR("Invalid PD popped from Tx completion queue\n"); + continue; + } + + /* Extract the data from received packet descriptor */ + ch_num = curr_pd->ch_num; + ep_num = curr_pd->ep_num; + length = curr_pd->hw_desc.buf_len; + + tx_ch = &cppi->tx_cppi_ch[ch_num]; + tx_ch->channel.actual_len += length; + + /* + * Return Tx PD to the software list -- + * this is protected by critical section + */ + usb_put_free_pd(cppi, curr_pd); + + if ((tx_ch->curr_offset < tx_ch->length) || + (tx_ch->transfer_mode && !tx_ch->zlp_queued)) + cppi41_next_tx_segment(tx_ch); + else if (tx_ch->channel.actual_len >= tx_ch->length) { + tx_ch->channel.status = MUSB_DMA_STATUS_FREE; + + /* Tx completion routine callback */ + musb_dma_completion(cppi->musb, ep_num, 1); + } + } +} + +static void usb_process_rx_queue(struct cppi41 *cppi, unsigned index) +{ + struct cppi41_queue_obj rx_queue_obj; + unsigned long pd_addr; + + if (cppi41_queue_init(&rx_queue_obj, usb_cppi41_info.q_mgr, + usb_cppi41_info.rx_comp_q[index])) { + DBG(1, "ERROR: cppi41_queue_init failed for Rx queue\n"); + return; + } + + while ((pd_addr = cppi41_queue_pop(&rx_queue_obj)) != 0) { + struct usb_pkt_desc *curr_pd; + struct cppi41_channel *rx_ch; + u8 ch_num, ep_num; + u32 length; + + curr_pd = usb_get_pd_ptr(cppi, pd_addr); + if (curr_pd == NULL) { + ERR("Invalid PD popped from Rx completion queue\n"); + continue; + } + + /* Extract the data from received packet descriptor */ + ch_num = curr_pd->ch_num; + ep_num = curr_pd->ep_num; + length = curr_pd->hw_desc.buf_len; + + rx_ch = &cppi->rx_cppi_ch[ch_num]; + rx_ch->channel.actual_len += length; + + if (curr_pd->eop) { + curr_pd->eop = 0; + /* disable the rx dma schedular */ + if (is_peripheral_active(cppi->musb)) { + cppi41_disable_sched_rx(); + musb_dma_completion(cppi->musb, ep_num, 0); + } + } + + /* + * Return Rx PD to the software list -- + * this is protected by critical section + */ + usb_put_free_pd(cppi, curr_pd); + + if (unlikely(rx_ch->channel.actual_len >= rx_ch->length || + length < curr_pd->hw_desc.orig_buf_len)) { + rx_ch->channel.status = MUSB_DMA_STATUS_FREE; + + /* Rx completion routine callback */ + musb_dma_completion(cppi->musb, ep_num, 0); + } else { + if (is_peripheral_active(cppi->musb) && + ((rx_ch->length - rx_ch->curr_offset) > 0)) + cppi41_next_rx_segment(rx_ch); + } + } +} + +/* + * cppi41_completion - handle interrupts from the Tx/Rx completion queues + * + * NOTE: since we have to manually prod the Rx process in the transparent mode, + * we certainly want to handle the Rx queues first. + */ +void cppi41_completion(struct musb *musb, u32 rx, u32 tx) +{ + struct cppi41 *cppi; + unsigned index; + + cppi = container_of(musb->dma_controller, struct cppi41, controller); + + /* Process packet descriptors from the Rx queues */ + for (index = 0; rx != 0; rx >>= 1, index++) + if (rx & 1) + usb_process_rx_queue(cppi, index); + + /* Process packet descriptors from the Tx completion queues */ + for (index = 0; tx != 0; tx >>= 1, index++) + if (tx & 1) + usb_process_tx_queue(cppi, index); +} diff --git a/drivers/usb/musb/cppi41_dma.h b/drivers/usb/musb/cppi41_dma.h new file mode 100644 index 00000000000..34004ee02e9 --- /dev/null +++ b/drivers/usb/musb/cppi41_dma.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (c) 2008, MontaVista Software, Inc. + * + * This program is free software; you can distribute 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 it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ + +#ifndef _CPPI41_DMA_H_ +#define _CPPI41_DMA_H_ +#include + +/** + * struct usb_cppi41_info - CPPI 4.1 USB implementation details + * @dma_block: DMA block number + * @ep_dma_ch: DMA channel numbers used for EPs 1 .. Max_EP + * @q_mgr: queue manager number + * @num_tx_comp_q: number of the Tx completion queues + * @num_rx_comp_q: number of the Rx queues + * @tx_comp_q: pointer to the list of the Tx completion queue numbers + * @rx_comp_q: pointer to the list of the Rx queue numbers + */ +struct usb_cppi41_info { + u8 dma_block; + u8 ep_dma_ch[USB_CPPI41_NUM_CH]; + u8 q_mgr; + u8 num_tx_comp_q; + u8 num_rx_comp_q; + const u16 *tx_comp_q; + const u16 *rx_comp_q; +}; + +extern const struct usb_cppi41_info usb_cppi41_info; + +/** + * cppi41_completion - Tx/Rx completion queue interrupt handling hook + * @musb: the controller + * @rx: bitmask having bit N set if Rx queue N is not empty + * @tx: bitmask having bit N set if Tx completion queue N is not empty + */ +void cppi41_completion(struct musb *musb, u32 rx, u32 tx); + +/** + * cppi41_disable_sched_rx + */ +int cppi41_disable_sched_rx(void); + +/** + * cppi41_enable_sched_rx + */ +int cppi41_enable_sched_rx(void); +#endif /* _CPPI41_DMA_H_ */ diff --git a/drivers/usb/musb/musb_procfs.c b/drivers/usb/musb/musb_procfs.c new file mode 100644 index 00000000000..8b6b1fe5bdd --- /dev/null +++ b/drivers/usb/musb/musb_procfs.c @@ -0,0 +1,840 @@ +/* + * MUSB OTG driver debug support + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include /* FIXME remove procfs writes */ +#include + +#include "musb_core.h" + +#include "davinci.h" + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + +static int dump_qh(struct musb_qh *qh, char *buf, unsigned max) +{ + int count; + int tmp; + struct usb_host_endpoint *hep = qh->hep; + struct urb *urb; + + count = snprintf(buf, max, " qh %p dev%d ep%d%s max%d\n", + qh, qh->dev->devnum, qh->epnum, + ({ char *s; switch (qh->type) { + case USB_ENDPOINT_XFER_BULK: + s = "-bulk"; break; + case USB_ENDPOINT_XFER_INT: + s = "-int"; break; + case USB_ENDPOINT_XFER_CONTROL: + s = ""; break; + default: + s = "iso"; break; + }; s; }), + qh->maxpacket); + if (count <= 0) + return 0; + buf += count; + max -= count; + + list_for_each_entry(urb, &hep->urb_list, urb_list) { + tmp = snprintf(buf, max, "\t%s urb %p %d/%d\n", + usb_pipein(urb->pipe) ? "in" : "out", + urb, urb->actual_length, + urb->transfer_buffer_length); + if (tmp <= 0) + break; + tmp = min(tmp, (int)max); + count += tmp; + buf += tmp; + max -= tmp; + } + return count; +} + +static int +dump_queue(struct list_head *q, char *buf, unsigned max) +{ + int count = 0; + struct musb_qh *qh; + + list_for_each_entry(qh, q, ring) { + int tmp; + + tmp = dump_qh(qh, buf, max); + if (tmp <= 0) + break; + tmp = min(tmp, (int)max); + count += tmp; + buf += tmp; + max -= tmp; + } + return count; +} + +#endif /* HCD */ + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC +static int dump_ep(struct musb_ep *ep, char *buffer, unsigned max) +{ + char *buf = buffer; + int code = 0; + void __iomem *regs = ep->hw_ep->regs; + char *mode = "1buf"; + + if (ep->is_in) { + if (ep->hw_ep->tx_double_buffered) + mode = "2buf"; + } else { + if (ep->hw_ep->rx_double_buffered) + mode = "2buf"; + } + + do { + struct usb_request *req; + + code = snprintf(buf, max, + "\n%s (hw%d): %s%s, csr %04x maxp %04x\n", + ep->name, ep->current_epnum, + mode, ep->dma ? " dma" : "", + musb_readw(regs, + (ep->is_in || !ep->current_epnum) + ? MUSB_TXCSR + : MUSB_RXCSR), + musb_readw(regs, ep->is_in + ? MUSB_TXMAXP + : MUSB_RXMAXP) + ); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + + if ((is_cppi_enabled() || is_cppi41_enabled()) + && ep->current_epnum) { + unsigned cppi = ep->current_epnum - 1; + void __iomem *base = ep->musb->ctrl_base; + unsigned off1 = cppi << 2; + void __iomem *ram = base; + char tmp[16]; + + if (ep->is_in) { + ram += DAVINCI_TXCPPI_STATERAM_OFFSET(cppi); + tmp[0] = 0; + } else { + ram += DAVINCI_RXCPPI_STATERAM_OFFSET(cppi); + snprintf(tmp, sizeof tmp, "%d left, ", + musb_readl(base, + DAVINCI_RXCPPI_BUFCNT0_REG + off1)); + } + + code = snprintf(buf, max, "%cX DMA%d: %s" + "%08x %08x, %08x %08x; " + "%08x %08x %08x .. %08x\n", + ep->is_in ? 'T' : 'R', + ep->current_epnum - 1, tmp, + musb_readl(ram, 0 * 4), + musb_readl(ram, 1 * 4), + musb_readl(ram, 2 * 4), + musb_readl(ram, 3 * 4), + musb_readl(ram, 4 * 4), + musb_readl(ram, 5 * 4), + musb_readl(ram, 6 * 4), + musb_readl(ram, 7 * 4)); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + + if (list_empty(&ep->req_list)) { + code = snprintf(buf, max, "\t(queue empty)\n"); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + break; + } + list_for_each_entry(req, &ep->req_list, list) { + code = snprintf(buf, max, "\treq %p, %s%s%d/%d\n", + req, + req->zero ? "zero, " : "", + req->short_not_ok ? "!short, " : "", + req->actual, req->length); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } while (0); + return buf - buffer; +} +#endif + +static int +dump_end_info(struct musb *musb, u8 epnum, char *aBuffer, unsigned max) +{ + int code = 0; + char *buf = aBuffer; + struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + + do { + musb_ep_select(musb->mregs, epnum); +#ifdef CONFIG_USB_MUSB_HDRC_HCD + if (is_host_active(musb)) { + int dump_rx, dump_tx; + void __iomem *regs = hw_ep->regs; + + /* TEMPORARY (!) until we have a real periodic + * schedule tree ... + */ + if (!epnum) { + /* control is shared, uses RX queue + * but (mostly) shadowed tx registers + */ + dump_tx = !list_empty(&musb->control); + dump_rx = 0; + } else if (hw_ep == musb->bulk_ep) { + dump_tx = !list_empty(&musb->out_bulk); + dump_rx = !list_empty(&musb->in_bulk); + } else if (hw_ep->in_qh || hw_ep->out_qh) { + if (hw_ep->in_qh) + dump_rx = 1; + else + dump_rx = 0; + dump_tx = !dump_rx; + } else + break; + /* END TEMPORARY */ + + + if (dump_rx) { + code = snprintf(buf, max, + "\nRX%d: %s rxcsr %04x interval %02x " + "max %04x type %02x; " + "dev %d hub %d port %d" + "\n", + epnum, + hw_ep->rx_double_buffered + ? "2buf" : "1buf", + musb_readw(regs, MUSB_RXCSR), + musb_readb(regs, MUSB_RXINTERVAL), + musb_readw(regs, MUSB_RXMAXP), + musb_readb(regs, MUSB_RXTYPE), + /* FIXME: assumes multipoint */ + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_RXFUNCADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_RXHUBADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_RXHUBPORT)) + ); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + + if ((is_cppi_enabled() || is_cppi41_enabled()) + && epnum + && hw_ep->rx_channel) { + unsigned cppi = epnum - 1; + unsigned off1 = cppi << 2; + void __iomem *base; + void __iomem *ram; + char tmp[16]; + + base = musb->ctrl_base; + ram = DAVINCI_RXCPPI_STATERAM_OFFSET( + cppi) + base; + snprintf(tmp, sizeof tmp, "%d left, ", + musb_readl(base, + DAVINCI_RXCPPI_BUFCNT0_REG + + off1)); + + code = snprintf(buf, max, + " rx dma%d: %s" + "%08x %08x, %08x %08x; " + "%08x %08x %08x .. %08x\n", + cppi, tmp, + musb_readl(ram, 0 * 4), + musb_readl(ram, 1 * 4), + musb_readl(ram, 2 * 4), + musb_readl(ram, 3 * 4), + musb_readl(ram, 4 * 4), + musb_readl(ram, 5 * 4), + musb_readl(ram, 6 * 4), + musb_readl(ram, 7 * 4)); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + + if (hw_ep == musb->bulk_ep + && !list_empty( + &musb->in_bulk)) { + code = dump_queue(&musb->in_bulk, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } else if (hw_ep->in_qh) { + code = dump_qh(hw_ep->in_qh, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } + + if (dump_tx) { + code = snprintf(buf, max, + "\nTX%d: %s txcsr %04x interval %02x " + "max %04x type %02x; " + "dev %d hub %d port %d" + "\n", + epnum, + hw_ep->tx_double_buffered + ? "2buf" : "1buf", + musb_readw(regs, MUSB_TXCSR), + musb_readb(regs, MUSB_TXINTERVAL), + musb_readw(regs, MUSB_TXMAXP), + musb_readb(regs, MUSB_TXTYPE), + /* FIXME: assumes multipoint */ + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_TXFUNCADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_TXHUBADDR)), + musb_readb(musb->mregs, + MUSB_BUSCTL_OFFSET(epnum, + MUSB_TXHUBPORT)) + ); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + + if ((is_cppi_enabled() || is_cppi41_enabled()) + && epnum + && hw_ep->tx_channel) { + unsigned cppi = epnum - 1; + void __iomem *base; + void __iomem *ram; + + base = musb->ctrl_base; + ram = DAVINCI_RXCPPI_STATERAM_OFFSET( + cppi) + base; + code = snprintf(buf, max, + " tx dma%d: " + "%08x %08x, %08x %08x; " + "%08x %08x %08x .. %08x\n", + cppi, + musb_readl(ram, 0 * 4), + musb_readl(ram, 1 * 4), + musb_readl(ram, 2 * 4), + musb_readl(ram, 3 * 4), + musb_readl(ram, 4 * 4), + musb_readl(ram, 5 * 4), + musb_readl(ram, 6 * 4), + musb_readl(ram, 7 * 4)); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + + if (hw_ep == musb->control_ep + && !list_empty( + &musb->control)) { + code = dump_queue(&musb->control, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } else if (hw_ep == musb->bulk_ep + && !list_empty( + &musb->out_bulk)) { + code = dump_queue(&musb->out_bulk, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } else if (hw_ep->out_qh) { + code = dump_qh(hw_ep->out_qh, + buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } + } +#endif +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + if (is_peripheral_active(musb)) { + code = 0; + + if (hw_ep->ep_in.desc || !epnum) { + code = dump_ep(&hw_ep->ep_in, buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + if (hw_ep->ep_out.desc) { + code = dump_ep(&hw_ep->ep_out, buf, max); + if (code <= 0) + break; + code = min(code, (int) max); + buf += code; + max -= code; + } + } +#endif + } while (0); + + return buf - aBuffer; +} + +/* Dump the current status and compile options. + * @param musb the device driver instance + * @param buffer where to dump the status; it must be big enough to hold the + * result otherwise "BAD THINGS HAPPENS(TM)". + */ +static int dump_header_stats(struct musb *musb, char *buffer) +{ + int code, count = 0; + const void __iomem *mbase = musb->mregs; + + *buffer = 0; + count = sprintf(buffer, "Status: %sHDRC, Mode=%s " + "(Power=%02x, DevCtl=%02x)\n", + (musb->is_multipoint ? "M" : ""), MUSB_MODE(musb), + musb_readb(mbase, MUSB_POWER), + musb_readb(mbase, MUSB_DEVCTL)); + if (count <= 0) + return 0; + buffer += count; + + code = sprintf(buffer, "OTG state: %s; %sactive\n", + otg_state_string(musb), + musb->is_active ? "" : "in"); + if (code <= 0) + goto done; + buffer += code; + count += code; + + code = sprintf(buffer, + "Options: " +#ifdef CONFIG_MUSB_PIO_ONLY + "pio" +#elif defined(CONFIG_USB_TI_CPPI_DMA) + "cppi-dma" +#elif defined(CONFIG_USB_INVENTRA_DMA) + "musb-dma" +#elif defined(CONFIG_USB_TUSB_OMAP_DMA) + "tusb-omap-dma" +#else + "?dma?" +#endif + ", " +#ifdef CONFIG_USB_MUSB_OTG + "otg (peripheral+host)" +#elif defined(CONFIG_USB_GADGET_MUSB_HDRC) + "peripheral" +#elif defined(CONFIG_USB_MUSB_HDRC_HCD) + "host" +#endif + ", debug=%d [eps=%d]\n", + musb_debug, + musb->nr_endpoints); + if (code <= 0) + goto done; + count += code; + buffer += code; + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + code = sprintf(buffer, "Peripheral address: %02x\n", + musb_readb(musb->ctrl_base, MUSB_FADDR)); + if (code <= 0) + goto done; + buffer += code; + count += code; +#endif + +#ifdef CONFIG_USB_MUSB_HDRC_HCD + code = sprintf(buffer, "Root port status: %08x\n", + musb->port1_status); + if (code <= 0) + goto done; + buffer += code; + count += code; +#endif + +#ifdef CONFIG_ARCH_DAVINCI + code = sprintf(buffer, + "DaVinci: ctrl=%02x stat=%1x phy=%03x\n" + "\trndis=%05x auto=%04x intsrc=%08x intmsk=%08x" + "\n", + musb_readl(musb->ctrl_base, DAVINCI_USB_CTRL_REG), + musb_readl(musb->ctrl_base, DAVINCI_USB_STAT_REG), + __raw_readl((void __force __iomem *) + IO_ADDRESS(USBPHY_CTL_PADDR)), + musb_readl(musb->ctrl_base, DAVINCI_RNDIS_REG), + musb_readl(musb->ctrl_base, DAVINCI_AUTOREQ_REG), + musb_readl(musb->ctrl_base, + DAVINCI_USB_INT_SOURCE_REG), + musb_readl(musb->ctrl_base, + DAVINCI_USB_INT_MASK_REG)); + if (code <= 0) + goto done; + count += code; + buffer += code; +#endif /* DAVINCI */ + +#ifdef CONFIG_USB_TUSB6010 + code = sprintf(buffer, + "TUSB6010: devconf %08x, phy enable %08x drive %08x" + "\n\totg %03x timer %08x" + "\n\tprcm conf %08x mgmt %08x; int src %08x mask %08x" + "\n", + musb_readl(musb->ctrl_base, TUSB_DEV_CONF), + musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL_ENABLE), + musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL), + musb_readl(musb->ctrl_base, TUSB_DEV_OTG_STAT), + musb_readl(musb->ctrl_base, TUSB_DEV_OTG_TIMER), + musb_readl(musb->ctrl_base, TUSB_PRCM_CONF), + musb_readl(musb->ctrl_base, TUSB_PRCM_MNGMT), + musb_readl(musb->ctrl_base, TUSB_INT_SRC), + musb_readl(musb->ctrl_base, TUSB_INT_MASK)); + if (code <= 0) + goto done; + count += code; + buffer += code; +#endif /* DAVINCI */ + + if ((is_cppi_enabled() || is_cppi41_enabled()) + && musb->dma_controller) { + code = sprintf(buffer, + "CPPI: txcr=%d txsrc=%01x txena=%01x; " + "rxcr=%d rxsrc=%01x rxena=%01x " + "\n", + musb_readl(musb->ctrl_base, + DAVINCI_TXCPPI_CTRL_REG), + musb_readl(musb->ctrl_base, + DAVINCI_TXCPPI_RAW_REG), + musb_readl(musb->ctrl_base, + DAVINCI_TXCPPI_INTENAB_REG), + musb_readl(musb->ctrl_base, + DAVINCI_RXCPPI_CTRL_REG), + musb_readl(musb->ctrl_base, + DAVINCI_RXCPPI_RAW_REG), + musb_readl(musb->ctrl_base, + DAVINCI_RXCPPI_INTENAB_REG)); + if (code <= 0) + goto done; + count += code; + buffer += code; + } + +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + if (is_peripheral_enabled(musb)) { + code = sprintf(buffer, "Gadget driver: %s\n", + musb->gadget_driver + ? musb->gadget_driver->driver.name + : "(none)"); + if (code <= 0) + goto done; + count += code; + buffer += code; + } +#endif + +done: + return count; +} + +/* Write to ProcFS + * + * C soft-connect + * c soft-disconnect + * I enable HS + * i disable HS + * s stop session + * F force session (OTG-unfriendly) + * E rElinquish bus (OTG) + * H request host mode + * h cancel host request + * T start sending TEST_PACKET + * D set/query the debug level + */ +static int musb_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char cmd; + u8 reg; + struct musb *musb = (struct musb *)data; + void __iomem *mbase = musb->mregs; + + /* MOD_INC_USE_COUNT; */ + + if (unlikely(copy_from_user(&cmd, buffer, 1))) + return -EFAULT; + + switch (cmd) { + case 'S': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + | MUSB_POWER_SUSPENDM; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'C': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + | MUSB_POWER_SOFTCONN; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'c': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + & ~MUSB_POWER_SOFTCONN; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'I': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + | MUSB_POWER_HSENAB; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'i': + if (mbase) { + reg = musb_readb(mbase, MUSB_POWER) + & ~MUSB_POWER_HSENAB; + musb_writeb(mbase, MUSB_POWER, reg); + } + break; + + case 'F': + reg = musb_readb(mbase, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_SESSION; + musb_writeb(mbase, MUSB_DEVCTL, reg); + break; + + case 'H': + if (mbase) { + reg = musb_readb(mbase, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_HR; + musb_writeb(mbase, MUSB_DEVCTL, reg); + /* MUSB_HST_MODE( ((struct musb*)data) ); */ + /* WARNING("Host Mode\n"); */ + } + break; + + case 'h': + if (mbase) { + reg = musb_readb(mbase, MUSB_DEVCTL); + reg &= ~MUSB_DEVCTL_HR; + musb_writeb(mbase, MUSB_DEVCTL, reg); + } + break; + + case 'T': + if (mbase) { + musb_load_testpacket(musb); + musb_writeb(mbase, MUSB_TESTMODE, + MUSB_TEST_PACKET); + } + break; + +#if (CONFIG_USB_MUSB_DEBUG > 0) + /* set/read debug level */ + case 'D':{ + if (count > 1) { + char digits[8], *p = digits; + int i = 0, level = 0, sign = 1; + int len = min(count - 1, (unsigned long)8); + + if (copy_from_user(&digits, &buffer[1], len)) + return -EFAULT; + + /* optional sign */ + if (*p == '-') { + len -= 1; + sign = -sign; + p++; + } + + /* read it */ + while (i++ < len && *p > '0' && *p < '9') { + level = level * 10 + (*p - '0'); + p++; + } + + level *= sign; + DBG(1, "debug level %d\n", level); + musb_debug = level; + } + } + break; + + + case '?': + INFO("?: you are seeing it\n"); + INFO("S: suspend the usb bus\n"); + INFO("C/c: soft connect enable/disable\n"); + INFO("I/i: hispeed enable/disable\n"); + INFO("F: force session start\n"); + INFO("H: host mode\n"); + INFO("T: start sending TEST_PACKET\n"); + INFO("D: set/read dbug level\n"); + break; +#endif + + default: + ERR("Command %c not implemented\n", cmd); + break; + } + + musb_platform_try_idle(musb, 0); + + return count; +} + +static int musb_proc_read(char *page, char **start, + off_t off, int count, int *eof, void *data) +{ + char *buffer = page; + int code = 0; + unsigned long flags; + struct musb *musb = data; + unsigned epnum; + + count -= off; + count -= 1; /* for NUL at end */ + if (count <= 0) + return -EINVAL; + + spin_lock_irqsave(&musb->lock, flags); + + code = dump_header_stats(musb, buffer); + if (code > 0) { + buffer += code; + count -= code; + } + + /* generate the report for the end points */ + /* REVISIT ... not unless something's connected! */ + for (epnum = 0; count >= 0 && epnum < musb->nr_endpoints; + epnum++) { + code = dump_end_info(musb, epnum, buffer, count); + if (code > 0) { + buffer += code; + count -= code; + } + } + + musb_platform_try_idle(musb, 0); + + spin_unlock_irqrestore(&musb->lock, flags); + *eof = 1; + + return buffer - page; +} + +void __devexit musb_debug_delete(char *name, struct musb *musb) +{ + if (musb->proc_entry) + remove_proc_entry(name, NULL); +} + +struct proc_dir_entry *__init +musb_debug_create(char *name, struct musb *data) +{ + struct proc_dir_entry *pde; + + /* FIXME convert everything to seq_file; then later, debugfs */ + + if (!name) + return NULL; + + pde = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, NULL); + data->proc_entry = pde; + if (pde) { + pde->data = data; + /* pde->owner = THIS_MODULE; */ + + pde->read_proc = musb_proc_read; + pde->write_proc = musb_proc_write; + + pde->size = 0; + + pr_debug("Registered /proc/%s\n", name); + } else { + pr_debug("Cannot create a valid proc file entry"); + } + + return pde; +} diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h new file mode 100644 index 00000000000..46e4714014e --- /dev/null +++ b/drivers/video/omap/omapfb.h @@ -0,0 +1,227 @@ +/* + * File: drivers/video/omap/omapfb.h + * + * Framebuffer driver for TI OMAP boards + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __OMAPFB_H +#define __OMAPFB_H + +#include +#include +#include + +#define OMAPFB_EVENT_READY 1 +#define OMAPFB_EVENT_DISABLED 2 + +#define OMAP_LCDC_INV_VSYNC 0x0001 +#define OMAP_LCDC_INV_HSYNC 0x0002 +#define OMAP_LCDC_INV_PIX_CLOCK 0x0004 +#define OMAP_LCDC_INV_OUTPUT_EN 0x0008 +#define OMAP_LCDC_HSVS_RISING_EDGE 0x0010 +#define OMAP_LCDC_HSVS_OPPOSITE 0x0020 + +#define OMAP_LCDC_SIGNAL_MASK 0x003f + +#define OMAP_LCDC_PANEL_TFT 0x0100 + +#define OMAPFB_PLANE_XRES_MIN 8 +#define OMAPFB_PLANE_YRES_MIN 8 + +struct omapfb_device; + +struct lcd_panel { + const char *name; + int config; /* TFT/STN, signal inversion */ + int bpp; /* Pixel format in fb mem */ + int data_lines; /* Lines on LCD HW interface */ + + int x_res, y_res; + int pixel_clock; /* In kHz */ + int hsw; /* Horizontal synchronization + pulse width */ + int hfp; /* Horizontal front porch */ + int hbp; /* Horizontal back porch */ + int vsw; /* Vertical synchronization + pulse width */ + int vfp; /* Vertical front porch */ + int vbp; /* Vertical back porch */ + int acb; /* ac-bias pin frequency */ + int pcd; /* pixel clock divider. + Obsolete use pixel_clock instead */ + + int (*init) (struct lcd_panel *panel, + struct omapfb_device *fbdev); + void (*cleanup) (struct lcd_panel *panel); + int (*enable) (struct lcd_panel *panel); + void (*disable) (struct lcd_panel *panel); + unsigned long (*get_caps) (struct lcd_panel *panel); + int (*set_bklight_level)(struct lcd_panel *panel, + unsigned int level); + unsigned int (*get_bklight_level)(struct lcd_panel *panel); + unsigned int (*get_bklight_max) (struct lcd_panel *panel); + int (*run_test) (struct lcd_panel *panel, int test_num); +}; + +struct extif_timings { + int cs_on_time; + int cs_off_time; + int we_on_time; + int we_off_time; + int re_on_time; + int re_off_time; + int we_cycle_time; + int re_cycle_time; + int cs_pulse_width; + int access_time; + + int clk_div; + + u32 tim[5]; /* set by extif->convert_timings */ + + int converted; +}; + +struct lcd_ctrl_extif { + int (*init) (struct omapfb_device *fbdev); + void (*cleanup) (void); + void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); + unsigned long (*get_max_tx_rate)(void); + int (*convert_timings) (struct extif_timings *timings); + void (*set_timings) (const struct extif_timings *timings); + void (*set_bits_per_cycle)(int bpc); + void (*write_command) (const void *buf, unsigned int len); + void (*read_data) (void *buf, unsigned int len); + void (*write_data) (const void *buf, unsigned int len); + void (*transfer_area) (int width, int height, + void (callback)(void *data), void *data); + int (*setup_tearsync) (unsigned pin_cnt, + unsigned hs_pulse_time, unsigned vs_pulse_time, + int hs_pol_inv, int vs_pol_inv, int div); + int (*enable_tearsync) (int enable, unsigned line); + + unsigned long max_transmit_size; +}; + +struct omapfb_notifier_block { + struct notifier_block nb; + void *data; + int plane_idx; +}; + +typedef int (*omapfb_notifier_callback_t)(struct notifier_block *, + unsigned long event, + void *fbi); + +struct lcd_ctrl { + const char *name; + void *data; + + int (*init) (struct omapfb_device *fbdev, + int ext_mode, + struct omapfb_mem_desc *req_md); + void (*cleanup) (void); + void (*bind_client) (struct omapfb_notifier_block *nb); + void (*get_caps) (int plane, struct omapfb_caps *caps); + int (*set_update_mode)(enum omapfb_update_mode mode); + enum omapfb_update_mode (*get_update_mode)(void); + int (*setup_plane) (int plane, int channel_out, + unsigned long offset, + int screen_width, + int pos_x, int pos_y, int width, + int height, int color_mode); + int (*set_rotate) (int angle); + int (*setup_mem) (int plane, size_t size, + int mem_type, unsigned long *paddr); + int (*mmap) (struct fb_info *info, + struct vm_area_struct *vma); + int (*set_scale) (int plane, + int orig_width, int orig_height, + int out_width, int out_height); + int (*enable_plane) (int plane, int enable); + int (*update_window) (struct fb_info *fbi, + struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data); + void (*sync) (void); + void (*suspend) (void); + void (*resume) (void); + int (*run_test) (int test_num); + int (*setcolreg) (u_int regno, u16 red, u16 green, + u16 blue, u16 transp, + int update_hw_mem); + int (*set_color_key) (struct omapfb_color_key *ck); + int (*get_color_key) (struct omapfb_color_key *ck); +}; + +enum omapfb_state { + OMAPFB_DISABLED = 0, + OMAPFB_SUSPENDED = 99, + OMAPFB_ACTIVE = 100 +}; + +struct omapfb_plane_struct { + int idx; + struct omapfb_plane_info info; + enum omapfb_color_format color_mode; + struct omapfb_device *fbdev; +}; + +struct omapfb_device { + int state; + int ext_lcdc; /* Using external + LCD controller */ + struct mutex rqueue_mutex; + + int palette_size; + u32 pseudo_palette[17]; + + struct lcd_panel *panel; /* LCD panel */ + const struct lcd_ctrl *ctrl; /* LCD controller */ + const struct lcd_ctrl *int_ctrl; /* internal LCD ctrl */ + struct lcd_ctrl_extif *ext_if; /* LCD ctrl external + interface */ + struct device *dev; + struct fb_var_screeninfo new_var; /* for mode changes */ + + struct omapfb_mem_desc mem_desc; + struct fb_info *fb_info[OMAPFB_PLANE_NUM]; +}; + +#ifdef CONFIG_ARCH_OMAP1 +extern struct lcd_ctrl omap1_lcd_ctrl; +#else +extern struct lcd_ctrl omap2_disp_ctrl; +#endif + +extern void omapfb_register_panel(struct lcd_panel *panel); +extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); +extern void omapfb_notify_clients(struct omapfb_device *fbdev, + unsigned long event); +extern int omapfb_register_client(struct omapfb_notifier_block *nb, + omapfb_notifier_callback_t callback, + void *callback_data); +extern int omapfb_unregister_client(struct omapfb_notifier_block *nb); +extern int omapfb_update_window_async(struct fb_info *fbi, + struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data); + +#endif /* __OMAPFB_H */ diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig new file mode 100644 index 00000000000..d877c361abd --- /dev/null +++ b/drivers/video/omap2/Kconfig @@ -0,0 +1,9 @@ +config OMAP2_VRAM + bool + +config OMAP2_VRFB + bool + +source "drivers/video/omap2/dss/Kconfig" +source "drivers/video/omap2/omapfb/Kconfig" +source "drivers/video/omap2/displays/Kconfig" diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile new file mode 100644 index 00000000000..d853d05dad3 --- /dev/null +++ b/drivers/video/omap2/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_OMAP2_VRAM) += vram.o +obj-$(CONFIG_OMAP2_VRFB) += vrfb.o + +obj-y += dss/ +obj-y += omapfb/ +obj-y += displays/ diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig new file mode 100644 index 00000000000..5e07aa39d7c --- /dev/null +++ b/drivers/video/omap2/displays/Kconfig @@ -0,0 +1,40 @@ +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_SHARP_LQ043T1DG01 + tristate "Sharp LQ043T1DG01 LCD Panel" + depends on OMAP2_DSS + help + LCD Panel used in TI's OMAP3517 EVM boards + +config PANEL_GINGER + tristate "Ginger LCDs" + depends on OMAP2_DSS + help + LCD Panels for Ginger Console + +config PANEL_TAAL + tristate "Taal DSI Panel" + depends on OMAP2_DSS_DSI + help + Taal DSI command mode panel from TPO. + +endmenu diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile new file mode 100644 index 00000000000..e503dc487d5 --- /dev/null +++ b/drivers/video/omap2/displays/Makefile @@ -0,0 +1,6 @@ +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_PANEL_SHARP_LQ043T1DG01) += panel-sharp-lq043t1dg01.o +obj-$(CONFIG_PANEL_GINGER) += panel-ginger.o +obj-$(CONFIG_PANEL_TAAL) += panel-taal.o diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c new file mode 100644 index 00000000000..eb48d1afd80 --- /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 + * + * 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 . + */ + +#include +#include + +#include + +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-ginger.c b/drivers/video/omap2/displays/panel-ginger.c new file mode 100644 index 00000000000..c1ba06dee50 --- /dev/null +++ b/drivers/video/omap2/displays/panel-ginger.c @@ -0,0 +1,116 @@ +/* + * LCD panel driver for Ginger Console + * Author: LynxLuna + * + * 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 . + */ + +#include +#include + +#include + +static char _lcd_type [10] = "Unknown\0"; + +static struct omap_video_timings ginger_lcd_timing = { + .x_res = 480, + .y_res = 272, + .hsw = 41, /* hsync_len (4) - 1 */ + .hfp = 2, /* right_margin (4) - 1 */ + .hbp = 2, /* left_margin (40) - 1 */ + .vsw = 10, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 2, /* upper_margin (8) - 1 */ + .pixel_clock = 9600, +}; + +static int ginger_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + printk(KERN_INFO "Probing Ginger LCD... [%s]", _lcd_type ); + dssdev->panel.timings = ginger_lcd_timing; + return 0; +} + +static void ginger_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int ginger_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 ginger_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 ginger_panel_suspend(struct omap_dss_device *dssdev) +{ + ginger_panel_disable(dssdev); + return 0; +} + +static int ginger_panel_resume(struct omap_dss_device *dssdev) +{ + return ginger_panel_enable(dssdev); +} + +static struct omap_dss_driver ginger_driver = { + .probe = ginger_panel_probe, + .remove = ginger_panel_remove, + + .enable = ginger_panel_enable, + .disable = ginger_panel_disable, + .suspend = ginger_panel_suspend, + .resume = ginger_panel_resume, + + .driver = { + .name = "ginger_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init ginger_panel_drv_init(void) +{ + printk ("Registering Ginger LCD Driver...."); + return omap_dss_register_driver(&ginger_driver); +} + +static void __exit ginger_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&ginger_driver); +} + +module_param_string(lcd,_lcd_type, 10, 0); + +module_init(ginger_panel_drv_init); +module_exit(ginger_panel_drv_exit); +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 00000000000..3f0477e0da5 --- /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 + * + * 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 . + */ + +#include +#include + +#include + +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-lq043t1dg01.c b/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c new file mode 100644 index 00000000000..e75798edbb5 --- /dev/null +++ b/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c @@ -0,0 +1,120 @@ +/* + * LCD panel driver for Sharp LQ043T1DG01 + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * 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 . + */ + +#include +#include +#include +#include + +#include + +static struct omap_video_timings sharp_lq_timings = { + .x_res = 480, + .y_res = 272, + + .pixel_clock = 9000, + + .hsw = 42, + .hfp = 3, + .hbp = 2, + + .vsw = 11, + .vfp = 3, + .vbp = 2, +}; + +static int sharp_lq_panel_probe(struct omap_dss_device *dssdev) +{ + + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO; + dssdev->panel.acb = 0x0; + dssdev->panel.timings = sharp_lq_timings; + + return 0; +} + +static void sharp_lq_panel_remove(struct omap_dss_device *dssdev) +{ +} + +static int sharp_lq_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 sharp_lq_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 sharp_lq_panel_suspend(struct omap_dss_device *dssdev) +{ + sharp_lq_panel_disable(dssdev); + return 0; +} + +static int sharp_lq_panel_resume(struct omap_dss_device *dssdev) +{ + return sharp_lq_panel_enable(dssdev); +} + +static struct omap_dss_driver sharp_lq_driver = { + .probe = sharp_lq_panel_probe, + .remove = sharp_lq_panel_remove, + + .enable = sharp_lq_panel_enable, + .disable = sharp_lq_panel_disable, + .suspend = sharp_lq_panel_suspend, + .resume = sharp_lq_panel_resume, + + .driver = { + .name = "sharp_lq_panel", + .owner = THIS_MODULE, + }, +}; + +static int __init sharp_lq_panel_drv_init(void) +{ + return omap_dss_register_driver(&sharp_lq_driver); +} + +static void __exit sharp_lq_panel_drv_exit(void) +{ + omap_dss_unregister_driver(&sharp_lq_driver); +} + +module_init(sharp_lq_panel_drv_init); +module_exit(sharp_lq_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 00000000000..bbe880bbe79 --- /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 + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#include + +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); + pr_err("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 00000000000..1f01dfc5e52 --- /dev/null +++ b/drivers/video/omap2/displays/panel-taal.c @@ -0,0 +1,1003 @@ +/* + * Taal DSI command mode panel + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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_RDDSDR 0x0f +#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 + +/* #define TAAL_USE_ESD_CHECK */ +#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000) + +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; + + struct workqueue_struct *esd_wq; + struct delayed_work esd_work; +}; + +static void taal_esd_work(struct work_struct *work); + +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) + 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; + + dev_dbg(&dssdev->dev, "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, + }; + + dev_dbg(&dssdev->dev, "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; + } + td->dssdev = dssdev; + + td->esd_wq = create_singlethread_workqueue("taal_esd"); + if (td->esd_wq == NULL) { + dev_err(&dssdev->dev, "can't create ESD workqueue\n"); + r = -ENOMEM; + goto err2; + } + INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work); + + 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("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) { + dev_err(&dssdev->dev, "GPIO request failed\n"); + goto err3; + } + + gpio_direction_input(gpio); + + r = request_irq(gpio_to_irq(gpio), taal_te_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING, + "taal vsync", dssdev); + + if (r) { + dev_err(&dssdev->dev, "IRQ request failed\n"); + gpio_free(gpio); + goto err3; + } + + 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 err4; + } + + return 0; +err4: + if (td->use_ext_te) { + int gpio = dssdev->phy.dsi.ext_te_gpio; + free_irq(gpio_to_irq(gpio), dssdev); + gpio_free(gpio); + } +err3: + backlight_device_unregister(bldev); +err2: + cancel_delayed_work_sync(&td->esd_work); + destroy_workqueue(td->esd_wq); +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; + + dev_dbg(&dssdev->dev, "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); + + cancel_delayed_work_sync(&td->esd_work); + destroy_workqueue(td->esd_wq); + + 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; + + dev_dbg(&dssdev->dev, "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); + + r = taal_sleep_out(td); + if (r) + goto err; + + r = taal_get_id(&id1, &id2, &id3); + if (r) + goto err; + + /* 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); + +#ifdef TAAL_USE_ESD_CHECK + queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); +#endif + + 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; + } + + return 0; +err: + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + return r; +} + +static void taal_disable(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + + dev_dbg(&dssdev->dev, "disable\n"); + + cancel_delayed_work(&td->esd_work); + + 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) { + dev_err(&dssdev->dev, "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; + + dev_dbg(&dssdev->dev, "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; + + dev_dbg(&dssdev->dev, "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) { + dev_err(&dssdev->dev, "read error\n"); + goto err; + } + + buf_used += r; + + if (r < plen) { + dev_err(&dssdev->dev, "short read\n"); + break; + } + + if (signal_pending(current)) { + dev_err(&dssdev->dev, "signal pending, " + "aborting memory read\n"); + r = -ERESTARTSYS; + goto err; + } + } + + r = buf_used; + +err: + dsi_vc_set_max_rx_packet_size(TCH, 1); + + return r; +} + +static void taal_esd_work(struct work_struct *work) +{ + struct taal_data *td = container_of(work, struct taal_data, + esd_work.work); + struct omap_dss_device *dssdev = td->dssdev; + u8 state1, state2; + int r; + + if (!td->enabled) + return; + + dsi_bus_lock(); + + r = taal_dcs_read_1(DCS_RDDSDR, &state1); + if (r) { + dev_err(&dssdev->dev, "failed to read Taal status\n"); + goto err; + } + + /* Run self diagnostics */ + r = taal_sleep_out(td); + if (r) { + dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n"); + goto err; + } + + r = taal_dcs_read_1(DCS_RDDSDR, &state2); + if (r) { + dev_err(&dssdev->dev, "failed to read Taal status\n"); + goto err; + } + + /* Each sleep out command will trigger a self diagnostic and flip + * Bit6 if the test passes. + */ + if (!((state1 ^ state2) & (1 << 6))) { + dev_err(&dssdev->dev, "LCD self diagnostics failed\n"); + goto err; + } + /* Self-diagnostics result is also shown on TE GPIO line. We need + * to re-enable TE after self diagnostics */ + if (td->use_ext_te && td->te_enabled) + taal_enable_te(dssdev, true); + + dsi_bus_unlock(); + + queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + + return; +err: + dev_err(&dssdev->dev, "performing LCD reset\n"); + + taal_disable(dssdev); + taal_enable(dssdev); + + dsi_bus_unlock(); + + queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); +} + +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 = "taal", + .owner = THIS_MODULE, + }, +}; + +static int __init taal_init(void) +{ + omap_dss_register_driver(&taal_driver); + + return 0; +} + +static void __exit taal_exit(void) +{ + omap_dss_unregister_driver(&taal_driver); +} + +module_init(taal_init); +module_exit(taal_exit); + +MODULE_AUTHOR("Tomi Valkeinen "); +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 00000000000..5b7a8bcc1ad --- /dev/null +++ b/drivers/video/omap2/dss/Kconfig @@ -0,0 +1,104 @@ +menuconfig OMAP2_DSS + tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" + depends on ARCH_OMAP2 || ARCH_OMAP3 + help + OMAP2/3 Display Subsystem support. + +if OMAP2_DSS + +config OMAP2_VRAM_SIZE + int "VRAM size (MB)" + range 0 32 + default 0 + 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=" 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. +choice + prompt OMAP2_VENC_OUT_TYPE + depends on OMAP2_DSS_VENC + default OMAP2_VENC_OUT_TYPE_SVIDEO + +config OMAP2_VENC_OUT_TYPE_SVIDEO + bool "Use S-Video output interface" + help + Select this option if you want to choose TV out over S-Video + +config OMAP2_VENC_OUT_TYPE_COMPOSITE + bool "Use Composite output interface" + help + Select this option if you want to choose TV out over Composite +endchoice + +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 + 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 00000000000..980c72c2db9 --- /dev/null +++ b/drivers/video/omap2/dss/Makefile @@ -0,0 +1,6 @@ +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 diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c new file mode 100644 index 00000000000..6900b6ab6ec --- /dev/null +++ b/drivers/video/omap2/dss/core.c @@ -0,0 +1,919 @@ +/* + * linux/drivers/video/omap2/dss/core.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "CORE" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; +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 core_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, "- CORE -\n"); + + seq_printf(s, "internal clk count\t\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(); +} + +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()) + 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()) + 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()) + 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) +{ + core_dump_clocks(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); + + 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 + } + +#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 + 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"); + dss_disable_all_devices(); +} + +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 "); +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 00000000000..adba0da44e5 --- /dev/null +++ b/drivers/video/omap2/dss/dispc.c @@ -0,0 +1,3087 @@ +/* + * linux/drivers/video/omap2/dss/dispc.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "DISPC" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "dss.h" + +/* DISPC */ +#define DISPC_BASE 0x48050400 + +#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) +#define DISPC_CONFIG DISPC_REG(0x0044) +#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) +#define DISPC_TIMING_V DISPC_REG(0x0068) +#define DISPC_POL_FREQ DISPC_REG(0x006C) +#define DISPC_DIVISOR DISPC_REG(0x0070) +#define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) +#define DISPC_SIZE_DIG DISPC_REG(0x0078) +#define DISPC_SIZE_LCD DISPC_REG(0x007C) + +/* 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) +#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) +#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) +#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8) +#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC) + +#define DISPC_CPR_COEF_R DISPC_REG(0x0220) +#define DISPC_CPR_COEF_G DISPC_REG(0x0224) +#define DISPC_CPR_COEF_B DISPC_REG(0x0228) + +#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) + + +#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 | \ + 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) }; + +static struct { + void __iomem *base; + + 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); +} + +#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); + + /* 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); + + 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; + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + + return REG_GET(DISPC_CONTROL, bit, bit) == 1; +} + +void dispc_go(enum omap_channel channel) +{ + int bit; + + enable_clocks(1); + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 0; /* LCDENABLE */ + else + bit = 1; /* DIGITALENABLE */ + + /* if the channel is not enabled, we don't need GO */ + if (REG_GET(DISPC_CONTROL, bit, bit) == 0) + goto end; + + if (channel == OMAP_DSS_CHANNEL_LCD) + bit = 5; /* GOLCD */ + else + bit = 6; /* GODIGIT */ + + 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); +end: + enable_clocks(0); +} + +static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); +} + +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); +} + +static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); +} + +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)); + +#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) }; + + 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) }; + + dispc_write_reg(ba1_reg[plane], paddr); +} + +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) }; + + 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) }; + 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) }; + + 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) +{ + + BUG_ON(plane == OMAP_DSS_VIDEO1); + + if (cpu_is_omap24xx()) + return; + + 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); +} + +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) }; + + 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) }; + + 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; + + 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; + } + + 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: + 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: + 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); +} + +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(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_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) }; + 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 + 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) }; + 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 + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 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) }; + + 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) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + 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) }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac1_reg[plane-1], val); +} + + +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) | (0x3 << 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; + l |= five_taps ? (1 << 22) : 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) +{ + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) { + int vidrot = 0; + + if (mirroring) { + switch (rotation) { + case OMAP_DSS_ROT_0: + vidrot = 2; + break; + case OMAP_DSS_ROT_90: + vidrot = 1; + break; + case OMAP_DSS_ROT_180: + vidrot = 0; + break; + case OMAP_DSS_ROT_270: + vidrot = 3; + break; + } + } else { + switch (rotation) { + case OMAP_DSS_ROT_0: + vidrot = 0; + break; + case OMAP_DSS_ROT_90: + vidrot = 1; + break; + case OMAP_DSS_ROT_180: + vidrot = 2; + break; + case OMAP_DSS_ROT_270: + vidrot = 3; + break; + } + } + + REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); + + if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270) + 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); + } +} + +static int color_mode_to_bpp(enum omap_color_mode color_mode) +{ + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + return 1; + case OMAP_DSS_COLOR_CLUT2: + return 2; + case OMAP_DSS_COLOR_CLUT4: + return 4; + case OMAP_DSS_COLOR_CLUT8: + return 8; + case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + return 16; + case OMAP_DSS_COLOR_RGB24P: + return 24; + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + return 32; + default: + BUG(); + } +} + +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_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; + + /* FIXME CLUT formats */ + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + case OMAP_DSS_COLOR_CLUT2: + case OMAP_DSS_COLOR_CLUT4: + case OMAP_DSS_COLOR_CLUT8: + BUG(); + return; + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 4; + break; + default: + ps = color_mode_to_bpp(color_mode) / 8; + break; + } + + 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 OMAP_DSS_ROT_0: + case OMAP_DSS_ROT_180: + /* + * 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 OMAP_DSS_ROT_90: + case OMAP_DSS_ROT_270: + *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 OMAP_DSS_ROT_0 + 4: + case OMAP_DSS_ROT_180 + 4: + /* 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 OMAP_DSS_ROT_90 + 4: + case OMAP_DSS_ROT_270 + 4: + *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; + + /* FIXME CLUT formats */ + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + case OMAP_DSS_COLOR_CLUT2: + case OMAP_DSS_COLOR_CLUT4: + case OMAP_DSS_COLOR_CLUT8: + BUG(); + return; + default: + ps = color_mode_to_bpp(color_mode) / 8; + break; + } + + 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 == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) { + 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 OMAP_DSS_ROT_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 OMAP_DSS_ROT_90: + *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 OMAP_DSS_ROT_180: + *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 OMAP_DSS_ROT_270: + *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 OMAP_DSS_ROT_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 OMAP_DSS_ROT_90 + 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 OMAP_DSS_ROT_180 + 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 OMAP_DSS_ROT_270 + 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(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(); + + 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(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() * 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) +{ + 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; + + 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_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + if (cpu_is_omap24xx()) + return -EINVAL; + /* fall through */ + case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + 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_RGBX32: + case OMAP_DSS_COLOR_RGB12U: + if (cpu_is_omap24xx()) + return -EINVAL; + /* fall through */ + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + break; + + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + if (cpu_is_omap24xx()) + return -EINVAL; + if (plane == OMAP_DSS_VIDEO1) + return -EINVAL; + break; + + 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(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)) { + DSSERR("failed to set up scaling, fclk too low\n"); + return -EINVAL; + } + + if (five_taps) + fclk = calc_fclk_five_taps(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()) { + DSSERR("failed to set up scaling, " + "required fclk rate = %lu Hz, " + "current fclk rate = %lu Hz\n", + 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; + + 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); + + 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); + + _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); + + if (plane != OMAP_DSS_VIDEO1) + _dispc_setup_global_alpha(plane, global_alpha); + + 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(bool enable) +{ + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); +} + +void dispc_enable_lcd_out(bool enable) +{ + struct completion frame_done_completion; + bool is_on; + int r; + + 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 */ + is_on = REG_GET(DISPC_CONTROL, 0, 0); + + if (!enable && is_on) { + init_completion(&frame_done_completion); + + r = omap_dispc_register_isr(dispc_disable_isr, + &frame_done_completion, + DISPC_IRQ_FRAMEDONE); + + if (r) + DSSERR("failed to register FRAMEDONE isr\n"); + } + + _enable_lcd_out(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, + DISPC_IRQ_FRAMEDONE); + + 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_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); + 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 }; + + 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 }; + u32 l; + + BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && + channel != OMAP_DSS_CHANNEL_LCD); + + 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 }; + + enable_clocks(1); + 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 }; + + enable_clocks(1); + if (type) { + 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); + 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) +{ + if (cpu_is_omap24xx()) + return; + + 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; + + if (cpu_is_omap24xx()) + return false; + + 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); + 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(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); + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); + enable_clocks(0); +} + +void dispc_set_parallel_interface_mode(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); + + l = dispc_read_reg(DISPC_CONTROL); + + 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(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); + dispc_write_reg(DISPC_TIMING_H, timing_h); + dispc_write_reg(DISPC_TIMING_V, timing_v); + enable_clocks(0); +} + +/* change name to mode? */ +void dispc_set_lcd_timings(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(timings->hsw, timings->hfp, timings->hbp, + timings->vsw, timings->vfp, timings->vbp); + + dispc_set_lcd_size(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("xres %u yres %u\n", 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); +} + +static void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div) +{ + BUG_ON(lck_div < 1); + BUG_ON(pck_div < 2); + + enable_clocks(1); + dispc_write_reg(DISPC_DIVISOR, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); + enable_clocks(0); +} + +static void dispc_get_lcd_divisor(int *lck_div, int *pck_div) +{ + u32 l; + l = dispc_read_reg(DISPC_DIVISOR); + *lck_div = FLD_GET(l, 23, 16); + *pck_div = FLD_GET(l, 7, 0); +} + +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(void) +{ + int lcd; + unsigned long r; + u32 l; + + l = dispc_read_reg(DISPC_DIVISOR); + + lcd = FLD_GET(l, 23, 16); + + r = dispc_fclk_rate(); + + return r / lcd; +} + +unsigned long dispc_pclk_rate(void) +{ + int lcd, pcd; + unsigned long r; + u32 l; + + l = dispc_read_reg(DISPC_DIVISOR); + + 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(&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, "fck\t\t%-16lu\n", dispc_fclk_rate()); + seq_printf(s, "lck\t\t%-16lulck div\t%u\n", dispc_lclk_rate(), lcd); + seq_printf(s, "pck\t\t%-16lupck div\t%u\n", dispc_pclk_rate(), pcd); + + 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); + DUMPREG(DISPC_DIVISOR); + 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)); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +#undef DUMPREG +} + +static void _dispc_set_pol_freq(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); + dispc_write_reg(DISPC_POL_FREQ, l); + enable_clocks(0); +} + +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb) +{ + _dispc_set_pol_freq((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); +} + +/* with fck as input clock rate, find dispc dividers that produce req_pck */ +void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, + struct dispc_clock_info *cinfo) +{ + 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: + cinfo->lck_div = best_ld; + cinfo->pck_div = best_pd; + cinfo->lck = fck / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; +} + +/* calculate clock rates using dividers in cinfo */ +int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, + struct dispc_clock_info *cinfo) +{ + if (cinfo->lck_div > 255 || cinfo->lck_div == 0) + return -EINVAL; + if (cinfo->pck_div < 2 || cinfo->pck_div > 255) + return -EINVAL; + + cinfo->lck = dispc_fclk_rate / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; + + return 0; +} + +int dispc_set_clock_div(struct dispc_clock_info *cinfo) +{ + DSSDBG("lck = %ld (%d)\n", cinfo->lck, cinfo->lck_div); + DSSDBG("pck = %ld (%d)\n", cinfo->pck, cinfo->pck_div); + + dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div); + + return 0; +} + +int dispc_get_clock_div(struct dispc_clock_info *cinfo) +{ + unsigned long fck; + + fck = dispc_fclk_rate(); + + cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); + + cinfo->lck = 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); + /* flush posted write */ + dispc_read_reg(DISPC_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; + } + } + } + + 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 */ + /* XXX this should be somewhere in plat-omap */ + 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; + } + + 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) +{ + 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) +{ + 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); + + 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 00000000000..3b92b84b956 --- /dev/null +++ b/drivers/video/omap2/dss/display.c @@ -0,0 +1,671 @@ +/* + * linux/drivers/video/omap2/dss/display.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "DISPLAY" + +#include +#include +#include +#include +#include + +#include +#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, "%d\n", enabled); +} + +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 burst_size_bytes; + + *burst_size = OMAP_DSS_BURST_16x32; + burst_size_bytes = 16 * 32 / 8; + + *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 + 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: + return 24; + 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; + + 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 + 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 + 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); +} + +static int dss_disable_device(struct device *dev, void *data) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + dssdev->disable(dssdev); + return 0; +} + +void dss_disable_all_devices(void) +{ + struct bus_type *bus = dss_get_bus(); + bus_for_each_dev(bus, NULL, NULL, dss_disable_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 00000000000..2d71031baa2 --- /dev/null +++ b/drivers/video/omap2/dss/dpi.c @@ -0,0 +1,399 @@ +/* + * linux/drivers/video/omap2/dss/dpi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "DPI" + +#include +#include +#include +#include + +#include +#include + +#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 dsi_cinfo; + struct dispc_clock_info dispc_cinfo; + int r; + + r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo, + &dispc_cinfo); + if (r) + return r; + + r = dsi_pll_set_clock_div(&dsi_cinfo); + if (r) + return r; + + dss_select_clk_source(0, 1); + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) + return r; + + *fck = dsi_cinfo.dsi1_pll_fclk; + *lck_div = dispc_cinfo.lck_div; + *pck_div = dispc_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 dss_clock_info dss_cinfo; + struct dispc_clock_info dispc_cinfo; + int r; + + r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo); + if (r) + return r; + + r = dss_set_clock_div(&dss_cinfo); + if (r) + return r; + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) + return r; + + *fck = dss_cinfo.fck; + *lck_div = dispc_cinfo.lck_div; + *pck_div = dispc_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); + + dispc_set_pol_freq(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; + + 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; + } + + dispc_set_lcd_timings(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_PARALLELMODE_BYPASS); + dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : + OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(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(dssdev, 0, 1); + if (r) + goto err3; +#endif + r = dpi_set_mode(dssdev); + if (r) + goto err4; + + mdelay(2); + + dispc_enable_lcd_out(1); + + r = dssdev->driver->enable(dssdev); + if (r) + goto err5; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; + +err5: + dispc_enable_lcd_out(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); + + dispc_enable_lcd_out(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); + + dispc_enable_lcd_out(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); + + dispc_enable_lcd_out(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; + 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 dsi_cinfo; + struct dispc_clock_info dispc_cinfo; + r = dsi_pll_calc_clock_div_pck(is_tft, + timings->pixel_clock * 1000, + &dsi_cinfo, &dispc_cinfo); + + if (r) + return r; + + fck = dsi_cinfo.dsi1_pll_fclk; + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_cinfo.pck_div; + } +#else + { + struct dss_clock_info dss_cinfo; + struct dispc_clock_info dispc_cinfo; + r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000, + &dss_cinfo, &dispc_cinfo); + + if (r) + return r; + + fck = dss_cinfo.fck; + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_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) { + dispc_enable_lcd_out(0); + dpi.update_enabled = 0; + } else { + dispc_enable_lcd_out(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 00000000000..ce93a22c191 --- /dev/null +++ b/drivers/video/omap2/dss/dsi.c @@ -0,0 +1,3708 @@ +/* + * linux/drivers/video/omap2/dss/dsi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "DSI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dss.h" + +/*#define VERBOSE_IRQ*/ +#define DSI_CATCH_MISSING_TE + +#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) +#define LP_DIV_MAX ((1 << 13) - 1) + +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; + + struct dsi_clock_info current_cinfo; + + 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; + +#ifdef DSI_CATCH_MISSING_TE + struct timer_list te_timer; +#endif + + 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); + } + +#ifdef DSI_CATCH_MISSING_TE + if (irqstatus & DSI_IRQ_TE_TRIGGER) + del_timer(&dsi.te_timer); +#endif + + for (i = 0; i < 4; ++i) { + if ((irqstatus & (1<phy.dsi.div.lp_clk_div; + + if (lp_clk_div == 0 || lp_clk_div > LP_DIV_MAX) + return -EINVAL; + + dsi_fclk = dsi_fclk_rate(); + + lp_clk = dsi_fclk / 2 / lp_clk_div; + + DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); + dsi.current_cinfo.lp_clk = lp_clk; + dsi.current_cinfo.lp_clk_div = lp_clk_div; + + REG_FLD_MOD(DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_CLK_DIVISOR */ + + REG_FLD_MOD(DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, + 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; +} + +/* calculate clock rates using dividers in cinfo */ +static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) +{ + if (cinfo->regn == 0 || cinfo->regn > REGN_MAX) + return -EINVAL; + + if (cinfo->regm == 0 || cinfo->regm > REGM_MAX) + return -EINVAL; + + if (cinfo->regm3 > REGM3_MAX) + return -EINVAL; + + if (cinfo->regm4 > REGM4_MAX) + return -EINVAL; + + if (cinfo->use_dss2_fck) { + cinfo->clkin = dss_clk_get_rate(DSS_CLK_FCK2); + /* XXX it is unclear if highfreq should be used + * with DSS2_FCK source also */ + cinfo->highfreq = 0; + } else { + cinfo->clkin = dispc_pclk_rate(); + + if (cinfo->clkin < 32000000) + cinfo->highfreq = 0; + else + cinfo->highfreq = 1; + } + + cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1)); + + if (cinfo->fint > FINT_MAX || cinfo->fint < FINT_MIN) + return -EINVAL; + + cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint; + + if (cinfo->clkin4ddr > 1800 * 1000 * 1000) + return -EINVAL; + + if (cinfo->regm3 > 0) + cinfo->dsi1_pll_fclk = cinfo->clkin4ddr / cinfo->regm3; + else + cinfo->dsi1_pll_fclk = 0; + + if (cinfo->regm4 > 0) + cinfo->dsi2_pll_fclk = cinfo->clkin4ddr / cinfo->regm4; + else + cinfo->dsi2_pll_fclk = 0; + + return 0; +} + +int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, + struct dsi_clock_info *dsi_cinfo, + struct dispc_clock_info *dispc_cinfo) +{ + struct dsi_clock_info cur, best; + struct dispc_clock_info best_dispc; + 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"); + *dsi_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(&best_dispc, 0, sizeof(best_dispc)); + + 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.clkin4ddr = a / b * 1000; + + if (cur.clkin4ddr > 1800 * 1000 * 1000) + break; + + /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */ + for (cur.regm3 = 1; cur.regm3 < REGM3_MAX; + ++cur.regm3) { + struct dispc_clock_info cur_dispc; + cur.dsi1_pll_fclk = cur.clkin4ddr / 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; + + dispc_find_clk_divs(is_tft, req_pck, + cur.dsi1_pll_fclk, + &cur_dispc); + + if (abs(cur_dispc.pck - req_pck) < + abs(best_dispc.pck - req_pck)) { + best = cur; + best_dispc = cur_dispc; + + if (cur_dispc.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 */ + best.regm4 = 0; + best.dsi2_pll_fclk = 0; + + if (dsi_cinfo) + *dsi_cinfo = best; + if (dispc_cinfo) + *dispc_cinfo = best_dispc; + + dsi.cache_req_pck = req_pck; + dsi.cache_clk_freq = 0; + dsi.cache_cinfo = best; + + return 0; +} + +int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) +{ + int r = 0; + u32 l; + int f; + + DSSDBGF(); + + dsi.current_cinfo.fint = cinfo->fint; + dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr; + dsi.current_cinfo.dsi1_pll_fclk = cinfo->dsi1_pll_fclk; + dsi.current_cinfo.dsi2_pll_fclk = cinfo->dsi2_pll_fclk; + + dsi.current_cinfo.regn = cinfo->regn; + dsi.current_cinfo.regm = cinfo->regm; + dsi.current_cinfo.regm3 = cinfo->regm3; + dsi.current_cinfo.regm4 = cinfo->regm4; + + 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("CLKIN4DDR = 2 * %d / %d * %lu / %d = %lu\n", + cinfo->regm, + cinfo->regn, + cinfo->clkin, + cinfo->highfreq + 1, + cinfo->clkin4ddr); + + DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", + cinfo->clkin4ddr / 1000 / 1000 / 2); + + DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4); + + 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 > 0 ? cinfo->regm3 - 1 : 0, + 22, 19); /* DSI_CLOCK_DIV */ + l = FLD_MOD(l, cinfo->regm4 > 0 ? cinfo->regm4 - 1 : 0, + 26, 23); /* DSIPROTO_CLOCK_DIV */ + dsi_write_reg(DSI_PLL_CONFIGURATION1, l); + + BUG_ON(cinfo->fint < 750000 || cinfo->fint > 2100000); + if (cinfo->fint < 1000000) + f = 0x3; + else if (cinfo->fint < 1250000) + f = 0x4; + else if (cinfo->fint < 1500000) + f = 0x5; + else if (cinfo->fint < 1750000) + f = 0x6; + else + f = 0x7; + + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ + l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, + 11, 11); /* DSI_PLL_CLKSEL */ + 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(struct omap_dss_device *dssdev, bool enable_hsclk, + bool enable_hsdiv) +{ + int r = 0; + enum dsi_pll_power_state pwstate; + + DSSDBG("PLL init\n"); + + enable_clocks(1); + dsi_enable_pll_clock(1); + + 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"); +} + +void dsi_dump_clocks(struct seq_file *s) +{ + int clksel; + struct dsi_clock_info *cinfo = &dsi.current_cinfo; + + enable_clocks(1); + + clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); + + seq_printf(s, "- DSI PLL -\n"); + + seq_printf(s, "dsi pll source = %s\n", + clksel == 0 ? + "dss2_alwon_fclk" : "pclkfree"); + + seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); + + seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n", + cinfo->clkin4ddr, cinfo->regm); + + seq_printf(s, "dsi1_pll_fck\t%-16luregm3 %u\t(%s)\n", + cinfo->dsi1_pll_fclk, + cinfo->regm3, + dss_get_dispc_clk_source() == 0 ? "off" : "on"); + + seq_printf(s, "dsi2_pll_fck\t%-16luregm4 %u\t(%s)\n", + cinfo->dsi2_pll_fclk, + cinfo->regm4, + dss_get_dsi_clk_source() == 0 ? "off" : "on"); + + 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_FCLK\t%lu\n", dsi_fclk_rate()); + + seq_printf(s, "DDR_CLK\t\t%lu\n", + cinfo->clkin4ddr / 4); + + seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs()); + + seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk); + + seq_printf(s, "VP_CLK\t\t%lu\n" + "VP_PCLK\t\t%lu\n", + dispc_lclk_rate(), + dispc_pclk_rate()); + + 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 */ + unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; +} + +static inline unsigned ddr2ns(unsigned ddr) +{ + unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + return ddr * 1000 * 1000 / (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_reset_tx_fifo(int channel) +{ + u32 mask; + u32 l; + + /* set fifosize of the channel to 0, then return the old size */ + l = dsi_read_reg(DSI_TX_FIFO_VC_SIZE); + + mask = FLD_MASK((8 * channel) + 7, (8 * channel) + 4); + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l & ~mask); + + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l); +} + +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; + + 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) +{ + int r; + r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + len, 0); + + if (r) + return r; + + r = dsi_vc_send_bta_sync(channel); + + return r; +} +EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); + +static void dsi_set_lp_rx_timeout(unsigned long ns) +{ + u32 r; + unsigned x4, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x4 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 4; + x4 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x4 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); + x4 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("LP_TX_TO over limit, setting it to max\n"); + ticks = 0x1fff; + x4 = 1; + x16 = 1; + } + + 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 %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x4 ? " x4" : "", x16 ? " x16" : ""); +} + +static void dsi_set_ta_timeout(unsigned long ns) +{ + u32 r; + unsigned x8, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in DSI_FCK */ + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x8 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 8; + x8 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x8 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (8 * 16); + x8 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("TA_TO over limit, setting it to max\n"); + ticks = 0x1fff; + x8 = 1; + x16 = 1; + } + + 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 %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x8 ? " x8" : "", x16 ? " x16" : ""); +} + +static void dsi_set_stop_state_counter(unsigned long ns) +{ + u32 r; + unsigned x4, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in DSI_FCK */ + + fck = dsi_fclk_rate(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x4 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 4; + x4 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x4 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); + x4 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("STOP_STATE_COUNTER_IO over limit, " + "setting it to max\n"); + ticks = 0x1fff; + x4 = 1; + x16 = 1; + } + + 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 %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x4 ? " x4" : "", x16 ? " x16" : ""); +} + +static void dsi_set_hs_tx_timeout(unsigned long ns) +{ + u32 r; + unsigned x4, x16; + unsigned long fck; + unsigned long ticks; + + /* ticks in TxByteClkHS */ + + fck = dsi_get_txbyteclkhs(); + ticks = (fck / 1000 / 1000) * ns / 1000; + x4 = 0; + x16 = 0; + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 4; + x4 = 1; + x16 = 0; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / 16; + x4 = 0; + x16 = 1; + } + + if (ticks > 0x1fff) { + ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16); + x4 = 1; + x16 = 1; + } + + if (ticks > 0x1fff) { + DSSWARN("HS_TX_TO over limit, setting it to max\n"); + ticks = 0x1fff; + x4 = 1; + x16 = 1; + } + + 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 %lu ns (%#lx ticks%s%s)\n", + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / + (fck / 1000 / 1000), + ticks, x4 ? " x4" : "", x16 ? " x16" : ""); +} +static int dsi_proto_config(struct omap_dss_device *dssdev) +{ + u32 r; + int buswidth = 0; + + 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); + dsi_set_ta_timeout(6400000); + dsi_set_lp_rx_timeout(48000); + dsi_set_hs_tx_timeout(1000000); + + 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 */ + r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/ + 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 + ths_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) +{ + unsigned bytespp; + unsigned bytespl; + unsigned bytespf; + unsigned total_len; + unsigned packet_payload; + unsigned packet_len; + u32 l; + bool use_te_trigger; + const unsigned channel = 0; + /* line buffer is 1024 x 24bits */ + /* XXX: for some reason using full buffer size causes considerable TX + * slowdown with update sizes that fill the whole buffer */ + const unsigned line_buf_size = 1023 * 3; + + 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); + + bytespp = dssdev->ctrl.pixel_size / 8; + bytespl = w * bytespp; + bytespf = bytespl * h; + + /* NOTE: packet_payload has to be equal to N * bytespl, where N is + * number of lines in a packet. See errata about VP_CLK_RATIO */ + + if (bytespf < line_buf_size) + packet_payload = bytespf; + else + packet_payload = (line_buf_size) / bytespl * bytespl; + + packet_len = packet_payload + 1; /* 1 byte for DCS cmd */ + total_len = (bytespf / packet_payload) * packet_len; + + if (bytespf % packet_payload) + total_len += (bytespf % 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); + +#ifdef DSI_CATCH_MISSING_TE + mod_timer(&dsi.te_timer, jiffies + msecs_to_jiffies(250)); +#endif + } +} + +#ifdef DSI_CATCH_MISSING_TE +static void dsi_te_timeout(unsigned long arg) +{ + DSSERR("TE not received for 250ms!\n"); +} +#endif + +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 int dsi_set_update_mode(struct omap_dss_device *dssdev, + enum omap_dss_update_mode mode) +{ + int r = 0; + int i; + + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); + + if (dsi.update_mode != mode) { + dsi.update_mode = mode; + + /* Mark the overlays dirty, and do apply(), so that we get the + * overlays configured properly after update mode change. */ + 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; + } + + r = dssdev->manager->apply(dssdev->manager); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE && + mode == OMAP_DSS_UPDATE_AUTO) { + u16 w, h; + + DSSDBG("starting auto update\n"); + + dssdev->get_resolution(dssdev, &w, &h); + + dsi_set_update_region(dssdev, 0, 0, w, h); + + dsi_perf_mark_start_auto(); + + wake_up(&dsi.waitqueue); + } + } + + return r; +} + +static int dsi_set_te(struct omap_dss_device *dssdev, bool enable) +{ + int r; + r = dssdev->driver->enable_te(dssdev, enable); + /* 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"); + + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + DSSERR("Received error during frame transfer:\n"); + dsi_vc_flush_receive_data(0); + } + +#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); + + dispc_set_lcd_size(w, h); + } + + 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); + wait_event_timeout(dsi.waitqueue, + dsi.framedone_received == true, + timeout); + + if (!dsi.framedone_received) { + DSSERR("framedone timeout\n"); + DSSERR("failed update %d,%d %dx%d\n", + x, y, w, h); + + dispc_enable_sidle(); + dispc_enable_lcd_out(0); + + dsi_reset_tx_fifo(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; + } + + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI); + dispc_enable_fifohandcheck(1); + + dispc_set_tft_data_lines(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(&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_configure_dsi_clocks(struct omap_dss_device *dssdev) +{ + struct dsi_clock_info cinfo; + int r; + + /* we always use DSS2_FCK as input clock */ + cinfo.use_dss2_fck = true; + cinfo.regn = dssdev->phy.dsi.div.regn; + cinfo.regm = dssdev->phy.dsi.div.regm; + cinfo.regm3 = dssdev->phy.dsi.div.regm3; + cinfo.regm4 = dssdev->phy.dsi.div.regm4; + r = dsi_calc_clock_rates(&cinfo); + if (r) + return r; + + r = dsi_pll_set_clock_div(&cinfo); + if (r) { + DSSERR("Failed to set dsi clocks\n"); + return r; + } + + return 0; +} + +static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) +{ + struct dispc_clock_info dispc_cinfo; + int r; + unsigned long long fck; + + fck = dsi_get_dsi1_pll_rate(); + + dispc_cinfo.lck_div = dssdev->phy.dsi.div.lck_div; + dispc_cinfo.pck_div = dssdev->phy.dsi.div.pck_div; + + r = dispc_calc_clock_rates(fck, &dispc_cinfo); + if (r) { + DSSERR("Failed to calc dispc clocks\n"); + return r; + } + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) { + DSSERR("Failed to set dispc clocks\n"); + return r; + } + + return 0; +} + +static int dsi_display_init_dsi(struct omap_dss_device *dssdev) +{ + int r; + + _dsi_print_reset_status(); + + r = dsi_pll_init(dssdev, true, true); + if (r) + goto err0; + + r = dsi_configure_dsi_clocks(dssdev); + if (r) + goto err1; + + dss_select_clk_source(true, true); + + DSSDBG("PLL OK\n"); + + r = dsi_configure_dispc_clocks(dssdev); + if (r) + goto err2; + + r = dsi_complexio_init(dssdev); + if (r) + goto err2; + + _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 err3; + + /* 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 err4; + } + + /* enable high-speed after initial config */ + dsi_vc_enable_hs(0, 1); + + return 0; +err4: + dsi_if_enable(0); +err3: + dsi_complexio_uninit(); +err2: + dss_select_clk_source(false, false); +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); + + dss_select_clk_source(false, false); + 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 err4; + + dsi_set_update_mode(dssdev, dsi.user_update_mode); + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return 0; + +err4: + + dsi_display_uninit_dsi(dssdev); +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_set_update_mode(dssdev, dsi.user_update_mode); + + 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; + + if (w == 1) { + r = -EINVAL; + 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) +{ + int r = 0; + + DSSDBGF("%d", mode); + + mutex_lock(&dsi.lock); + dsi_bus_lock(); + + dsi.user_update_mode = mode; + r = dsi_set_update_mode(dssdev, mode); + + dsi_bus_unlock(); + mutex_unlock(&dsi.lock); + + return r; +} + +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); + + /* Memory read usually changes the update area. This will + * force the next update to re-set the update area */ + dsi.active_update_region.dirty = true; + + 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 = fifo_size - burst_size_bytes * 8; +} + +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; + + /* XXX these should be figured out dynamically */ + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | + OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; + + dsi.vc[0].dssdev = dssdev; + dsi.vc[1].dssdev = dssdev; + + return 0; +} + +int dsi_init(struct platform_device *pdev) +{ + u32 rev; + int r; + struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO-1 + }; + + spin_lock_init(&dsi.errors_lock); + dsi.errors = 0; + + 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"); + r = PTR_ERR(dsi.thread); + goto err0; + } + 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); + +#ifdef DSI_CATCH_MISSING_TE + init_timer(&dsi.te_timer); + dsi.te_timer.function = dsi_te_timeout; + dsi.te_timer.data = 0; +#endif + + 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"); + r = -ENOMEM; + goto err1; + } + + 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"); + r = PTR_ERR(dsi.vdds_dsi_reg); + goto err2; + } + + 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; +err2: + iounmap(dsi.base); +err1: + kthread_stop(dsi.thread); +err0: + return r; +} + +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 00000000000..9b05ee65a15 --- /dev/null +++ b/drivers/video/omap2/dss/dss.c @@ -0,0 +1,596 @@ +/* + * linux/drivers/video/omap2/dss/dss.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "DSS" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "dss.h" + +#define DSS_BASE 0x48050000 + +#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_IRQSTATUS DSS_REG(0x0018) +#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) + +#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; + + struct clk *dpll4_m4_ck; + + unsigned long cache_req_pck; + unsigned long cache_prate; + struct dss_clock_info cache_dss_cinfo; + struct dispc_clock_info cache_dispc_cinfo; + + 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); +} + +int dss_sdi_enable(void) +{ + unsigned long timeout; + + 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 */ + timeout = jiffies + msecs_to_jiffies(500); + while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) { + if (time_after_eq(jiffies, timeout)) { + DSSERR("PLL lock request timed out\n"); + goto err1; + } + } + + /* Clearing PLL_GO bit */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28); + + /* Waiting for PLL to lock */ + timeout = jiffies + msecs_to_jiffies(500); + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) { + if (time_after_eq(jiffies, timeout)) { + DSSERR("PLL lock timed out\n"); + goto err1; + } + } + + dispc_lcd_enable_signal(1); + + /* Waiting for SDI reset to complete */ + timeout = jiffies + msecs_to_jiffies(500); + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) { + if (time_after_eq(jiffies, timeout)) { + DSSERR("SDI reset timed out\n"); + goto err2; + } + } + + return 0; + + err2: + dispc_lcd_enable_signal(0); + err1: + /* Reset SDI PLL */ + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ + + dispc_pck_free_enable(0); + + return -ETIMEDOUT; +} + +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_clocks(struct seq_file *s) +{ + unsigned long dpll4_ck_rate; + unsigned long dpll4_m4_ck_rate; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck); + + seq_printf(s, "- DSS -\n"); + + seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); + + seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n", + dpll4_ck_rate, + dpll4_ck_rate / dpll4_m4_ck_rate, + dss_clk_get_rate(DSS_CLK_FCK1)); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +} + +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_IRQSTATUS); + 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 */ + 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); +} + +/* calculate clock rates using dividers in cinfo */ +int dss_calc_clock_rates(struct dss_clock_info *cinfo) +{ + unsigned long prate; + + if (cinfo->fck_div > 16 || cinfo->fck_div == 0) + return -EINVAL; + + prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + + cinfo->fck = prate / cinfo->fck_div; + + return 0; +} + +int dss_set_clock_div(struct dss_clock_info *cinfo) +{ + unsigned long prate; + int r; + + if (cpu_is_omap34xx()) { + prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + DSSDBG("dpll4_m4 = %ld\n", prate); + + r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div); + if (r) + return r; + } + + DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div); + + return 0; +} + +int dss_get_clock_div(struct dss_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(dss.dpll4_m4_ck)); + cinfo->fck_div = prate / (cinfo->fck / 2); + } else { + cinfo->fck_div = 0; + } + + return 0; +} + +unsigned long dss_get_dpll4_rate(void) +{ + if (cpu_is_omap34xx()) + return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + else + return 0; +} + +int dss_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dss_clock_info *dss_cinfo, + struct dispc_clock_info *dispc_cinfo) +{ + unsigned long prate; + struct dss_clock_info best_dss; + struct dispc_clock_info best_dispc; + + unsigned long fck; + + u16 fck_div; + + int match = 0; + int min_fck_per_pck; + + prate = dss_get_dpll4_rate(); + + fck = dss_clk_get_rate(DSS_CLK_FCK1); + if (req_pck == dss.cache_req_pck && + ((cpu_is_omap34xx() && prate == dss.cache_prate) || + dss.cache_dss_cinfo.fck == fck)) { + DSSDBG("dispc clock info found from cache.\n"); + *dss_cinfo = dss.cache_dss_cinfo; + *dispc_cinfo = dss.cache_dispc_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(&best_dss, 0, sizeof(best_dss)); + memset(&best_dispc, 0, sizeof(best_dispc)); + + if (cpu_is_omap24xx()) { + struct dispc_clock_info cur_dispc; + /* XXX can we change the clock on omap2? */ + fck = dss_clk_get_rate(DSS_CLK_FCK1); + fck_div = 1; + + dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); + match = 1; + + best_dss.fck = fck; + best_dss.fck_div = fck_div; + + best_dispc = cur_dispc; + + goto found; + } else if (cpu_is_omap34xx()) { + for (fck_div = 16; fck_div > 0; --fck_div) { + struct dispc_clock_info cur_dispc; + + fck = prate / fck_div * 2; + + if (fck > DISPC_MAX_FCK) + continue; + + if (min_fck_per_pck && + fck < req_pck * min_fck_per_pck) + continue; + + match = 1; + + dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); + + if (abs(cur_dispc.pck - req_pck) < + abs(best_dispc.pck - req_pck)) { + + best_dss.fck = fck; + best_dss.fck_div = fck_div; + + best_dispc = cur_dispc; + + if (cur_dispc.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 (dss_cinfo) + *dss_cinfo = best_dss; + if (dispc_cinfo) + *dispc_cinfo = best_dispc; + + dss.cache_req_pck = req_pck; + dss.cache_prate = prate; + dss.cache_dss_cinfo = best_dss; + dss.cache_dispc_cinfo = best_dispc; + + return 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) +{ + u32 irqstatus; + + irqstatus = dss_read_reg(DSS_IRQSTATUS); + + if (irqstatus & (1<<0)) /* DISPC_IRQ */ + dispc_irq_handler(); +#ifdef CONFIG_OMAP2_DSS_DSI + if (irqstatus & (1<<1)) /* DSI_IRQ */ + dsi_irq_handler(); +#endif + + 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 */ +} + +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(0x48050440) & ~0x3, 0x48050440); + + /* 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); + + _omap_dss_reset(); + } + + /* autoidle */ + REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); + + /* 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 + + r = request_irq(INT_24XX_DSS_IRQ, + cpu_is_omap24xx() + ? dss_irq_handler_omap2 + : dss_irq_handler_omap3, + 0, "OMAP DSS", NULL); + + if (r < 0) { + DSSERR("omap2 dss: request_irq failed\n"); + goto fail1; + } + + if (cpu_is_omap34xx()) { + dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); + if (IS_ERR(dss.dpll4_m4_ck)) { + DSSERR("Failed to get dpll4_m4_ck\n"); + r = PTR_ERR(dss.dpll4_m4_ck); + goto fail2; + } + } + + 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; + +fail2: + free_irq(INT_24XX_DSS_IRQ, NULL); +fail1: + iounmap(dss.base); +fail0: + return r; +} + +void dss_exit(void) +{ + if (cpu_is_omap34xx()) + clk_put(dss.dpll4_m4_ck); + + free_irq(INT_24XX_DSS_IRQ, NULL); + + iounmap(dss.base); +} + diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h new file mode 100644 index 00000000000..8da5ac42151 --- /dev/null +++ b/drivers/video/omap2/dss/dss.h @@ -0,0 +1,370 @@ +/* + * linux/drivers/video/omap2/dss/dss.h + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#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_8x32 = 1, + OMAP_DSS_BURST_16x32 = 2, +}; + +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 dss_clock_info { + /* rates that we get with dividers below */ + unsigned long fck; + + /* dividers */ + u16 fck_div; +}; + +struct dispc_clock_info { + /* rates that we get with dividers below */ + unsigned long lck; + unsigned long pck; + + /* dividers */ + u16 lck_div; + u16 pck_div; +}; + +struct dsi_clock_info { + /* rates that we get with dividers below */ + unsigned long fint; + unsigned long clkin4ddr; + unsigned long clkin; + unsigned long dsi1_pll_fclk; + unsigned long dsi2_pll_fclk; + + unsigned long lp_clk; + + /* dividers */ + u16 regn; + u16 regm; + u16 regm3; + u16 regm4; + + u16 lp_clk_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_disable_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); +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_save_context(void); +void dss_restore_context(void); + +void dss_dump_regs(struct seq_file *s); + +void dss_sdi_init(u8 datapairs); +int 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); + +unsigned long dss_get_dpll4_rate(void); +int dss_calc_clock_rates(struct dss_clock_info *cinfo); +int dss_set_clock_div(struct dss_clock_info *cinfo); +int dss_get_clock_div(struct dss_clock_info *cinfo); +int dss_calc_clock_div(bool is_tft, unsigned long req_pck, + struct dss_clock_info *dss_cinfo, + struct dispc_clock_info *dispc_cinfo); + +/* 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); +int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo); +int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, + struct dsi_clock_info *cinfo, + struct dispc_clock_info *dispc_cinfo); +int dsi_pll_init(struct omap_dss_device *dssdev, 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(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); +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); + +bool dispc_go_busy(enum omap_channel channel); +void dispc_go(enum omap_channel channel); +void dispc_enable_lcd_out(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_parallel_interface_mode mode); +void dispc_set_tft_data_lines(u8 data_lines); +void dispc_set_lcd_display_type(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(struct omap_video_timings *timings); +unsigned long dispc_fclk_rate(void); +unsigned long dispc_lclk_rate(void); +unsigned long dispc_pclk_rate(void); +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb); +void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, + struct dispc_clock_info *cinfo); +int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, + 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); + + +/* 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); + +#endif diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c new file mode 100644 index 00000000000..27d9c465c85 --- /dev/null +++ b/drivers/video/omap2/dss/manager.c @@ -0,0 +1,1487 @@ +/* + * linux/drivers/video/omap2/dss/manager.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "MANAGER" + +#include +#include +#include +#include +#include + +#include +#include + +#include "dss.h" + +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 : ""); +} + +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, "%d\n", mgr->info.trans_enabled); +} + +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, "%d\n", mgr->info.alpha_enabled); +} + +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; +}; + +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; + struct overlay_cache_data overlay_cache[3]; + struct manager_cache_data manager_cache[2]; + + 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; + + if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + channel = OMAP_DSS_CHANNEL_DIGIT; + } 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; + + irq = DISPC_IRQ_FRAMEDONE; + } else { + irq = DISPC_IRQ_VSYNC; + } + channel = OMAP_DSS_CHANNEL_LCD; + } + + 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; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; + channel = OMAP_DSS_CHANNEL_DIGIT; + } 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; + + irq = DISPC_IRQ_FRAMEDONE; + } else { + irq = DISPC_IRQ_VSYNC; + } + channel = OMAP_DSS_CHANNEL_LCD; + } + + 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; + + 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 (dispc_is_overlay_scaled(c)) { + /* If the overlay is scaled, the update area has + * already been enlarged to cover the whole overlay. We + * only need to adjust x/y here */ + x = c->pos_x - mc->x; + y = c->pos_y - mc->y; + } else { + 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); + + 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); + + 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); + + dispc_set_burst_size(plane, c->burst_size); + dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); + + 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[2]; + bool mgr_go[2]; + bool busy; + + r = 0; + busy = false; + + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + mgr_go[0] = false; + mgr_go[1] = 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(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[2]; + + mgr_busy[0] = dispc_go_busy(0); + mgr_busy[1] = dispc_go_busy(1); + + 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); + 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; + 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; + + 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; +#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); + 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 supports only graphics source transparency color key and alpha + * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ + + if (mgr->info.alpha_enabled && mgr->info.trans_enabled && + mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) + return -EINVAL; + + 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; + } + + mgr->info_dirty = true; + + 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 < 2; ++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; + break; + } + + 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); + diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c new file mode 100644 index 00000000000..b7f9a733984 --- /dev/null +++ b/drivers/video/omap2/dss/overlay.c @@ -0,0 +1,680 @@ +/* + * linux/drivers/video/omap2/dss/overlay.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "OVERLAY" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dss.h" + +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 : ""); +} + +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 (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, "%d\n", ovl->info.enabled); +} + +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); + + /* 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 + 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; +} + +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); + +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, + 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; + } + + 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; + + 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[3]; + +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) +{ + mgr->num_overlays = 3; + 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 < 3; ++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 = cpu_is_omap34xx() ? + OMAP_DSS_COLOR_GFX_OMAP3 : + OMAP_DSS_COLOR_GFX_OMAP2; + ovl->caps = OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + case 1: + ovl->name = "vid1"; + ovl->id = OMAP_DSS_VIDEO1; + ovl->supported_modes = cpu_is_omap34xx() ? + OMAP_DSS_COLOR_VID1_OMAP3 : + OMAP_DSS_COLOR_VID_OMAP2; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + case 2: + ovl->name = "vid2"; + ovl->id = OMAP_DSS_VIDEO2; + ovl->supported_modes = cpu_is_omap34xx() ? + OMAP_DSS_COLOR_VID2_OMAP3 : + OMAP_DSS_COLOR_VID_OMAP2; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + break; + } + + 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; + 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); + + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { + 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) { + 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 < 3; 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 00000000000..d0b3006ad8a --- /dev/null +++ b/drivers/video/omap2/dss/rfbi.c @@ -0,0 +1,1309 @@ +/* + * linux/drivers/video/omap2/dss/rfbi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "RFBI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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(width, height); + + dispc_enable_lcd_out(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; + } + + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI); + + dispc_set_tft_data_lines(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 00000000000..c24f307d3da --- /dev/null +++ b/drivers/video/omap2/dss/sdi.c @@ -0,0 +1,277 @@ +/* + * linux/drivers/video/omap2/dss/sdi.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "SDI" + +#include +#include +#include +#include + +#include +#include "dss.h" + +static struct { + bool skip_init; + bool update_enabled; +} sdi; + +static void sdi_basic_init(void) +{ + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); + + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_tft_data_lines(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 dss_clock_info dss_cinfo; + struct dispc_clock_info dispc_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; + } + + /* 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; + + dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, + dssdev->panel.acb); + + if (!sdi.skip_init) { + r = dss_calc_clock_div(1, t->pixel_clock * 1000, + &dss_cinfo, &dispc_cinfo); + } else { + r = dss_get_clock_div(&dss_cinfo); + r = dispc_get_clock_div(&dispc_cinfo); + } + + if (r) + goto err2; + + fck = dss_cinfo.fck; + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_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; + } + + + dispc_set_lcd_timings(t); + + r = dss_set_clock_div(&dss_cinfo); + if (r) + goto err2; + + r = dispc_set_clock_div(&dispc_cinfo); + if (r) + goto err2; + + if (!sdi.skip_init) { + dss_sdi_init(dssdev->phy.sdi.datapairs); + r = dss_sdi_enable(); + if (r) + goto err1; + mdelay(2); + } + + dispc_enable_lcd_out(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: + dispc_enable_lcd_out(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) + if (sdi_display_resume(dssdev)) + return; + + if (dssdev->driver->disable) + dssdev->driver->disable(dssdev); + + dispc_enable_lcd_out(0); + + dss_sdi_disable(); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + 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); + + dispc_enable_lcd_out(0); + + dss_sdi_disable(); + + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int sdi_display_resume(struct omap_dss_device *dssdev) +{ + int r; + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) + return -EINVAL; + + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + + r = dss_sdi_enable(); + if (r) + goto err; + mdelay(2); + + dispc_enable_lcd_out(1); + + if (dssdev->driver->resume) + dssdev->driver->resume(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +err: + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + return r; +} + +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) { + dispc_enable_lcd_out(0); + sdi.update_enabled = 0; + } else { + dispc_enable_lcd_out(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 0; +} + +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 00000000000..749a5a0f5be --- /dev/null +++ b/drivers/video/omap2/dss/venc.c @@ -0,0 +1,797 @@ +/* + * linux/drivers/video/omap2/dss/venc.c + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#define DSS_SUBSYS_NAME "VENC" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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) +{ + u32 l; + + 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); + + l = 0; + + if (dssdev->phy.venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE) + l |= 1 << 1; + else /* S-Video */ + l |= (1 << 0) | (1 << 2); + + if (dssdev->phy.venc.invert_polarity == false) + l |= 1 << 3; + + venc_write_reg(VENC_OUTPUT_CONTROL, l); + + 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; +} + +static enum omap_dss_update_mode venc_display_get_update_mode( + struct omap_dss_device *dssdev) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return OMAP_DSS_UPDATE_AUTO; + else + return OMAP_DSS_UPDATE_DISABLED; +} + +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; + dssdev->get_update_mode = venc_display_get_update_mode; + + 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 00000000000..bb694cc52a5 --- /dev/null +++ b/drivers/video/omap2/omapfb/Kconfig @@ -0,0 +1,37 @@ +menuconfig FB_OMAP2 + tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)" + depends on FB && OMAP2_DSS + + select OMAP2_VRAM + select OMAP2_VRFB + 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 00000000000..51c2e00d9bf --- /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 00000000000..4c4bafdfaa4 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -0,0 +1,755 @@ +/* + * linux/drivers/video/omap2/omapfb-ioctl.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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_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; + } + +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_nolock(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; + + return display->update(display, x, y, w, h); +} + +/* This function is exported for SGX driver use */ +int omapfb_update_window(struct fb_info *fbi, + u32 x, u32 y, u32 w, u32 h) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_device *fbdev = ofbi->fbdev; + int r; + + omapfb_lock(fbdev); + lock_fb_info(fbi); + + r = omapfb_update_window_nolock(fbi, x, y, w, h); + + unlock_fb_info(fbi); + omapfb_unlock(fbdev); + + return r; +} +EXPORT_SYMBOL(omapfb_update_window); + +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; + struct omapfb_vram_info vram_info; + struct omapfb_tearsync_info tearsync_info; + } 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_nolock(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_nolock(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)); + if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) + p.caps.ctrl |= OMAPFB_CAPS_MANUAL_UPDATE; + if (display->caps & OMAP_DSS_DISPLAY_CAP_TEAR_ELIM) + p.caps.ctrl |= OMAPFB_CAPS_TEARSYNC; + + 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; + + case OMAPFB_GET_VRAM_INFO: { + unsigned long vram, free, largest; + + DBG("ioctl GET_VRAM_INFO\n"); + + omap_vram_get_info(&vram, &free, &largest); + p.vram_info.total = vram; + p.vram_info.free = free; + p.vram_info.largest_free_block = largest; + + if (copy_to_user((void __user *)arg, &p.vram_info, + sizeof(p.vram_info))) + r = -EFAULT; + break; + } + + case OMAPFB_SET_TEARSYNC: { + DBG("ioctl SET_TEARSYNC\n"); + + if (copy_from_user(&p.tearsync_info, (void __user *)arg, + sizeof(p.tearsync_info))) { + r = -EFAULT; + break; + } + + if (!display->enable_te) { + r = -ENODEV; + break; + } + + r = display->enable_te(display, !!p.tearsync_info.enabled); + + 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 00000000000..ef299839858 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -0,0 +1,2261 @@ +/* + * linux/drivers/video/omap2/omapfb-main.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "omapfb.h" + +#define MODULE_NAME "omapfb" + +#define OMAPFB_PLANE_XRES_MIN 8 +#define OMAPFB_PLANE_YRES_MIN 8 + +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; +module_param_named(test, omapfb_test_pattern, bool, 0644); +#endif + +static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi); + +#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; + } +} + +static 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; +} + +static 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 int fb_mode_to_dss_mode(struct fb_var_screeninfo *var, + enum omap_color_mode *mode) +{ + 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 *m = &omapfb_colormodes[i]; + if (var->nonstd == m->nonstd) { + assign_colormode_to_var(var, m); + *mode = m->dssmode; + return 0; + } + } + + return -EINVAL; + } + + /* then try exact match of bpp and colors */ + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *m = &omapfb_colormodes[i]; + if (cmp_var_to_colormode(var, m)) { + assign_colormode_to_var(var, m); + *mode = m->dssmode; + return 0; + } + } + + /* 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; + } + + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { + struct omapfb_colormode *m = &omapfb_colormodes[i]; + if (dssmode == m->dssmode) { + assign_colormode_to_var(var, m); + *mode = m->dssmode; + return 0; + } + } + + return -EINVAL; +} + +static int check_fb_res_bounds(struct fb_var_screeninfo *var) +{ + int xres_min = OMAPFB_PLANE_XRES_MIN; + int xres_max = 2048; + int yres_min = OMAPFB_PLANE_YRES_MIN; + int yres_max = 2048; + + /* 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; + + return 0; +} + +static void shrink_height(unsigned long max_frame_size, + struct fb_var_screeninfo *var) +{ + DBG("can't fit FB into memory, reducing y\n"); + var->yres_virtual = max_frame_size / + (var->xres_virtual * var->bits_per_pixel >> 3); + + if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN) + var->yres_virtual = OMAPFB_PLANE_YRES_MIN; + + if (var->yres > var->yres_virtual) + var->yres = var->yres_virtual; +} + +static void shrink_width(unsigned long max_frame_size, + struct fb_var_screeninfo *var) +{ + DBG("can't fit FB into memory, reducing x\n"); + var->xres_virtual = max_frame_size / var->yres_virtual / + (var->bits_per_pixel >> 3); + + if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN) + var->xres_virtual = OMAPFB_PLANE_XRES_MIN; + + if (var->xres > var->xres_virtual) + var->xres = var->xres_virtual; +} + +static int check_vrfb_fb_size(unsigned long region_size, + const struct fb_var_screeninfo *var) +{ + unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual, + var->yres_virtual, var->bits_per_pixel >> 3); + + return min_phys_size > region_size ? -EINVAL : 0; +} + +static int check_fb_size(const struct omapfb_info *ofbi, + struct fb_var_screeninfo *var) +{ + unsigned long max_frame_size = ofbi->region.size; + int bytespp = var->bits_per_pixel >> 3; + unsigned long line_size = var->xres_virtual * bytespp; + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { + /* One needs to check for both VRFB and OMAPFB limitations. */ + if (check_vrfb_fb_size(max_frame_size, var)) + shrink_height(omap_vrfb_max_height( + max_frame_size, var->xres_virtual, bytespp) * + line_size, var); + + if (check_vrfb_fb_size(max_frame_size, var)) { + DBG("cannot fit FB to memory\n"); + return -EINVAL; + } + + return 0; + } + + DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size); + + if (line_size * var->yres_virtual > max_frame_size) + shrink_height(max_frame_size, var); + + if (line_size * var->yres_virtual > max_frame_size) { + shrink_width(max_frame_size, var); + line_size = var->xres_virtual * bytespp; + } + + if (line_size * var->yres_virtual > max_frame_size) { + DBG("cannot fit FB to memory\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Consider if VRFB assisted rotation is in use and if the virtual space for + * the zero degree view needs to be mapped. The need for mapping also acts as + * the trigger for setting up the hardware on the context in question. This + * ensures that one does not attempt to access the virtual view before the + * hardware is serving the address translations. + */ +static int setup_vrfb_rotation(struct fb_info *fbi) +{ + struct omapfb_info *ofbi = FB2OFB(fbi); + struct omapfb2_mem_region *rg = &ofbi->region; + struct vrfb *vrfb = &rg->vrfb; + struct fb_var_screeninfo *var = &fbi->var; + struct fb_fix_screeninfo *fix = &fbi->fix; + unsigned bytespp; + bool yuv_mode; + enum omap_color_mode mode; + int r; + bool reconf; + + if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB) + return 0; + + DBG("setup_vrfb_rotation\n"); + + r = fb_mode_to_dss_mode(var, &mode); + if (r) + return r; + + bytespp = var->bits_per_pixel >> 3; + + yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY; + + /* We need to reconfigure VRFB if the resolution changes, if yuv mode + * is enabled/disabled, or if bytes per pixel changes */ + + /* XXX we shouldn't allow this when framebuffer is mmapped */ + + reconf = false; + + if (yuv_mode != vrfb->yuv_mode) + reconf = true; + else if (bytespp != vrfb->bytespp) + reconf = true; + else if (vrfb->xres != var->xres_virtual || + vrfb->yres != var->yres_virtual) + reconf = true; + + if (vrfb->vaddr[0] && reconf) { + fbi->screen_base = NULL; + fix->smem_start = 0; + fix->smem_len = 0; + iounmap(vrfb->vaddr[0]); + vrfb->vaddr[0] = NULL; + DBG("setup_vrfb_rotation: reset fb\n"); + } + + if (vrfb->vaddr[0]) + return 0; + + omap_vrfb_setup(&rg->vrfb, rg->paddr, + var->xres_virtual, + var->yres_virtual, + bytespp, yuv_mode); + + /* Now one can ioremap the 0 angle view */ + r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0); + if (r) + return r; + + /* used by open/write in fbmem.c */ + fbi->screen_base = ofbi->region.vrfb.vaddr[0]; + + fix->smem_start = ofbi->region.vrfb.paddr[0]; + + 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; + } + + fix->smem_len = var->yres_virtual * fix->line_length; + + return 0; +} + +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); + + /* 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; + } + + fix->smem_len = var->yres_virtual * fix->line_length; + } else { + fix->line_length = + (var->xres_virtual * var->bits_per_pixel) >> 3; + fix->smem_len = rg->size; + } + + fix->smem_start = omapfb_get_region_paddr(ofbi); + + 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; +} + +/* 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); + enum omap_color_mode mode = 0; + int i; + int r; + + DBG("check_fb_var %d\n", ofbi->id); + + if (ofbi->region.size == 0) + return 0; + + r = fb_mode_to_dss_mode(var, &mode); + if (r) { + DBG("cannot convert var to omap dss mode\n"); + return r; + } + + 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; + + if (check_fb_res_bounds(var)) + return -EINVAL; + + if (check_fb_size(ofbi, var)) + 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; +} + +static unsigned calc_rotation_offset_dma(struct fb_var_screeninfo *var, + struct fb_fix_screeninfo *fix, int rotation) +{ + unsigned offset; + + offset = var->yoffset * fix->line_length + + var->xoffset * (var->bits_per_pixel >> 3); + + return offset; +} + +static unsigned calc_rotation_offset_vrfb(struct fb_var_screeninfo *var, + struct fb_fix_screeninfo *fix, int rotation) +{ + unsigned offset; + + if (rotation == FB_ROTATE_UD) + offset = (var->yres_virtual - var->yres) * + fix->line_length; + else if (rotation == FB_ROTATE_CW) + offset = (var->yres_virtual - var->yres) * + (var->bits_per_pixel >> 3); + else + offset = 0; + + if (rotation == FB_ROTATE_UR) + offset += var->yoffset * fix->line_length + + var->xoffset * (var->bits_per_pixel >> 3); + else if (rotation == FB_ROTATE_UD) + offset -= var->yoffset * fix->line_length + + var->xoffset * (var->bits_per_pixel >> 3); + else if (rotation == FB_ROTATE_CW) + offset -= var->xoffset * fix->line_length + + var->yoffset * (var->bits_per_pixel >> 3); + else if (rotation == FB_ROTATE_CCW) + offset += var->xoffset * fix->line_length + + var->yoffset * (var->bits_per_pixel >> 3); + + return offset; +} + + +/* 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; + } + + + 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); + } + + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) + offset = calc_rotation_offset_vrfb(var, fix, rotation); + else + offset = calc_rotation_offset_dma(var, fix, rotation); + + data_start_p += offset; + data_start_v += offset; + + if (offset) + DBG("offset %d, %d = %d\n", + var->xoffset, var->yoffset, offset); + + DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v); + + r = fb_mode_to_dss_mode(var, &mode); + if (r) { + DBG("fb_mode_to_dss_mode failed"); + 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 = setup_vrfb_rotation(fbi); + if (r) + return r; + + r = omapfb_apply_changes(fbi, 0); + + return r; +} + +static int omapfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct fb_var_screeninfo new_var; + int r; + + DBG("pan_display(%d)\n", FB2OFB(fbi)->id); + + if (var->xoffset == fbi->var.xoffset && + var->yoffset == fbi->var.yoffset) + return 0; + + new_var = fbi->var; + new_var.xoffset = var->xoffset; + new_var.yoffset = var->yoffset; + + fbi->var = new_var; + + 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 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; + + 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); + + 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(OMAP_VRAM_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 { + r = omap_vrfb_request_ctx(&rg->vrfb); + if (r) { + dev_err(fbdev->dev, "vrfb create ctx failed\n"); + return r; + } + + 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) { + size = max(omap_vrfb_min_phys_size(w, h, bytespp), + omap_vrfb_min_phys_size(h, w, bytespp)); + + DBG("adjusting fb mem size for VRFB, %u -> %lu\n", + w * h * bytespp, size); + } else { + 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); + r = setup_vrfb_rotation(fbi); + if (r) + goto err; + } + + 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 */ +static 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; + + if (ofbi->region.size == 0) { + 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 = setup_vrfb_rotation(fbi); + if (r) + goto err; + + 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]); + + 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; +} + +static 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); + + if (def_mode && strlen(def_mode) > 0) { + if (omapfb_parse_def_modes(fbdev)) + dev_warn(&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_warn(fbdev->dev, "failed to apply dispc config\n"); + } + + DBG("mgr->apply'ed\n"); + + /* 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_warn(&pdev->dev, "cannot find default display\n"); + def_display = NULL; + } + + if (def_display) { +#ifndef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + u16 w, h; +#endif + r = def_display->enable(def_display); + if (r) + dev_warn(fbdev->dev, "Failed to enable display '%s'\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->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 /* MANUAL_UPDATE */ + 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); + + def_display->get_resolution(def_display, &w, &h); + def_display->update(def_display, 0, 0, w, h); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + } + } + + 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 "); +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 00000000000..62bb88f5c19 --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -0,0 +1,507 @@ +/* + * linux/drivers/video/omap2/omapfb-sysfs.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 00000000000..f7c9c739e5e --- /dev/null +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -0,0 +1,146 @@ +/* + * linux/drivers/video/omap2/omapfb.h + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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 . + */ + +#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ +#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ + +#ifdef CONFIG_FB_OMAP2_DEBUG_SUPPORT +#define DEBUG +#endif + +#include + +#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; +}; + +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_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 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 diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c new file mode 100644 index 00000000000..55a4de5e5d1 --- /dev/null +++ b/drivers/video/omap2/vram.c @@ -0,0 +1,655 @@ +/* + * VRAM manager for OMAP + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef DEBUG +#define DBG(format, ...) pr_debug("VRAM: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define OMAP2_SRAM_START 0x40200000 +/* Maximum size, in reality this is smaller if SRAM is partially locked. */ +#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ + +/* postponed regions are used to temporarily store region information at boot + * time when we cannot yet allocate the region list */ +#define MAX_POSTPONED_REGIONS 10 + +static bool vram_initialized; +static int postponed_cnt; +static struct { + unsigned long paddr; + size_t size; +} postponed_regions[MAX_POSTPONED_REGIONS]; + +struct vram_alloc { + struct list_head list; + unsigned long paddr; + unsigned pages; +}; + +struct vram_region { + struct list_head list; + struct list_head alloc_list; + unsigned long paddr; + unsigned pages; +}; + +static DEFINE_MUTEX(region_mutex); +static LIST_HEAD(region_list); + +static inline int region_mem_type(unsigned long paddr) +{ + if (paddr >= OMAP2_SRAM_START && + paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) + return OMAP_VRAM_MEMTYPE_SRAM; + else + return OMAP_VRAM_MEMTYPE_SDRAM; +} + +static struct vram_region *omap_vram_create_region(unsigned long paddr, + unsigned pages) +{ + struct vram_region *rm; + + rm = kzalloc(sizeof(*rm), GFP_KERNEL); + + if (rm) { + INIT_LIST_HEAD(&rm->alloc_list); + rm->paddr = paddr; + rm->pages = pages; + } + + return rm; +} + +#if 0 +static void omap_vram_free_region(struct vram_region *vr) +{ + list_del(&vr->list); + kfree(vr); +} +#endif + +static struct vram_alloc *omap_vram_create_allocation(struct vram_region *vr, + unsigned long paddr, unsigned pages) +{ + struct vram_alloc *va; + struct vram_alloc *new; + + new = kzalloc(sizeof(*va), GFP_KERNEL); + + if (!new) + return NULL; + + new->paddr = paddr; + new->pages = pages; + + list_for_each_entry(va, &vr->alloc_list, list) { + if (va->paddr > new->paddr) + break; + } + + list_add_tail(&new->list, &va->list); + + return new; +} + +static void omap_vram_free_allocation(struct vram_alloc *va) +{ + list_del(&va->list); + kfree(va); +} + +int omap_vram_add_region(unsigned long paddr, size_t size) +{ + struct vram_region *rm; + unsigned pages; + + if (vram_initialized) { + DBG("adding region paddr %08lx size %d\n", + paddr, size); + + size &= PAGE_MASK; + pages = size >> PAGE_SHIFT; + + rm = omap_vram_create_region(paddr, pages); + if (rm == NULL) + return -ENOMEM; + + list_add(&rm->list, ®ion_list); + } else { + if (postponed_cnt == MAX_POSTPONED_REGIONS) + return -ENOMEM; + + postponed_regions[postponed_cnt].paddr = paddr; + postponed_regions[postponed_cnt].size = size; + + ++postponed_cnt; + } + return 0; +} + +int omap_vram_free(unsigned long paddr, size_t size) +{ + struct vram_region *rm; + struct vram_alloc *alloc; + unsigned start, end; + + DBG("free mem paddr %08lx size %d\n", paddr, size); + + size = PAGE_ALIGN(size); + + mutex_lock(®ion_mutex); + + list_for_each_entry(rm, ®ion_list, list) { + list_for_each_entry(alloc, &rm->alloc_list, list) { + start = alloc->paddr; + end = alloc->paddr + (alloc->pages >> PAGE_SHIFT); + + if (start >= paddr && end < paddr + size) + goto found; + } + } + + mutex_unlock(®ion_mutex); + return -EINVAL; + +found: + omap_vram_free_allocation(alloc); + + mutex_unlock(®ion_mutex); + return 0; +} +EXPORT_SYMBOL(omap_vram_free); + +static int _omap_vram_reserve(unsigned long paddr, unsigned pages) +{ + struct vram_region *rm; + struct vram_alloc *alloc; + size_t size; + + size = pages << PAGE_SHIFT; + + list_for_each_entry(rm, ®ion_list, list) { + unsigned long start, end; + + DBG("checking region %lx %d\n", rm->paddr, rm->pages); + + if (region_mem_type(rm->paddr) != region_mem_type(paddr)) + continue; + + start = rm->paddr; + end = start + (rm->pages << PAGE_SHIFT) - 1; + if (start > paddr || end < paddr + size - 1) + continue; + + DBG("block ok, checking allocs\n"); + + list_for_each_entry(alloc, &rm->alloc_list, list) { + end = alloc->paddr - 1; + + if (start <= paddr && end >= paddr + size - 1) + goto found; + + start = alloc->paddr + (alloc->pages << PAGE_SHIFT); + } + + end = rm->paddr + (rm->pages << PAGE_SHIFT) - 1; + + if (!(start <= paddr && end >= paddr + size - 1)) + continue; +found: + DBG("found area start %lx, end %lx\n", start, end); + + if (omap_vram_create_allocation(rm, paddr, pages) == NULL) + return -ENOMEM; + + return 0; + } + + return -ENOMEM; +} + +int omap_vram_reserve(unsigned long paddr, size_t size) +{ + unsigned pages; + int r; + + DBG("reserve mem paddr %08lx size %d\n", paddr, size); + + size = PAGE_ALIGN(size); + pages = size >> PAGE_SHIFT; + + mutex_lock(®ion_mutex); + + r = _omap_vram_reserve(paddr, pages); + + mutex_unlock(®ion_mutex); + + return r; +} +EXPORT_SYMBOL(omap_vram_reserve); + +static void _omap_vram_dma_cb(int lch, u16 ch_status, void *data) +{ + struct completion *compl = data; + complete(compl); +} + +static int _omap_vram_clear(u32 paddr, unsigned pages) +{ + struct completion compl; + unsigned elem_count; + unsigned frame_count; + int r; + int lch; + + init_completion(&compl); + + r = omap_request_dma(OMAP_DMA_NO_DEVICE, "VRAM DMA", + _omap_vram_dma_cb, + &compl, &lch); + if (r) { + pr_err("VRAM: request_dma failed for memory clear\n"); + return -EBUSY; + } + + elem_count = pages * PAGE_SIZE / 4; + frame_count = 1; + + omap_set_dma_transfer_params(lch, OMAP_DMA_DATA_TYPE_S32, + elem_count, frame_count, + OMAP_DMA_SYNC_ELEMENT, + 0, 0); + + omap_set_dma_dest_params(lch, 0, OMAP_DMA_AMODE_POST_INC, + paddr, 0, 0); + + omap_set_dma_color_mode(lch, OMAP_DMA_CONSTANT_FILL, 0x000000); + + omap_start_dma(lch); + + if (wait_for_completion_timeout(&compl, msecs_to_jiffies(1000)) == 0) { + omap_stop_dma(lch); + pr_err("VRAM: dma timeout while clearing memory\n"); + r = -EIO; + goto err; + } + + r = 0; +err: + omap_free_dma(lch); + + return r; +} + +static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) +{ + struct vram_region *rm; + struct vram_alloc *alloc; + + list_for_each_entry(rm, ®ion_list, list) { + unsigned long start, end; + + DBG("checking region %lx %d\n", rm->paddr, rm->pages); + + if (region_mem_type(rm->paddr) != mtype) + continue; + + start = rm->paddr; + + list_for_each_entry(alloc, &rm->alloc_list, list) { + end = alloc->paddr; + + if (end - start >= pages << PAGE_SHIFT) + goto found; + + start = alloc->paddr + (alloc->pages << PAGE_SHIFT); + } + + end = rm->paddr + (rm->pages << PAGE_SHIFT); +found: + if (end - start < pages << PAGE_SHIFT) + continue; + + DBG("found %lx, end %lx\n", start, end); + + alloc = omap_vram_create_allocation(rm, start, pages); + if (alloc == NULL) + return -ENOMEM; + + *paddr = start; + + _omap_vram_clear(start, pages); + + return 0; + } + + return -ENOMEM; +} + +int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr) +{ + unsigned pages; + int r; + + BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size); + + DBG("alloc mem type %d size %d\n", mtype, size); + + size = PAGE_ALIGN(size); + pages = size >> PAGE_SHIFT; + + mutex_lock(®ion_mutex); + + r = _omap_vram_alloc(mtype, pages, paddr); + + mutex_unlock(®ion_mutex); + + return r; +} +EXPORT_SYMBOL(omap_vram_alloc); + +void omap_vram_get_info(unsigned long *vram, + unsigned long *free_vram, + unsigned long *largest_free_block) +{ + struct vram_region *vr; + struct vram_alloc *va; + + *vram = 0; + *free_vram = 0; + *largest_free_block = 0; + + mutex_lock(®ion_mutex); + + list_for_each_entry(vr, ®ion_list, list) { + unsigned free; + unsigned long pa; + + pa = vr->paddr; + *vram += vr->pages << PAGE_SHIFT; + + list_for_each_entry(va, &vr->alloc_list, list) { + free = va->paddr - pa; + *free_vram += free; + if (free > *largest_free_block) + *largest_free_block = free; + pa = va->paddr + (va->pages << PAGE_SHIFT); + } + + free = vr->paddr + (vr->pages << PAGE_SHIFT) - pa; + *free_vram += free; + if (free > *largest_free_block) + *largest_free_block = free; + } + + mutex_unlock(®ion_mutex); +} +EXPORT_SYMBOL(omap_vram_get_info); + +#if defined(CONFIG_DEBUG_FS) +static int vram_debug_show(struct seq_file *s, void *unused) +{ + struct vram_region *vr; + struct vram_alloc *va; + unsigned size; + + mutex_lock(®ion_mutex); + + list_for_each_entry(vr, ®ion_list, list) { + size = vr->pages << PAGE_SHIFT; + seq_printf(s, "%08lx-%08lx (%d bytes)\n", + vr->paddr, vr->paddr + size - 1, + size); + + list_for_each_entry(va, &vr->alloc_list, list) { + size = va->pages << PAGE_SHIFT; + seq_printf(s, " %08lx-%08lx (%d bytes)\n", + va->paddr, va->paddr + size - 1, + size); + } + } + + mutex_unlock(®ion_mutex); + + return 0; +} + +static int vram_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, vram_debug_show, inode->i_private); +} + +static const struct file_operations vram_debug_fops = { + .open = vram_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init omap_vram_create_debugfs(void) +{ + struct dentry *d; + + d = debugfs_create_file("vram", S_IRUGO, NULL, + NULL, &vram_debug_fops); + if (IS_ERR(d)) + return PTR_ERR(d); + + return 0; +} +#endif + +static __init int omap_vram_init(void) +{ + int i; + + vram_initialized = 1; + + for (i = 0; i < postponed_cnt; i++) + omap_vram_add_region(postponed_regions[i].paddr, + postponed_regions[i].size); + +#ifdef CONFIG_DEBUG_FS + if (omap_vram_create_debugfs()) + pr_err("VRAM: Failed to create debugfs file\n"); +#endif + + return 0; +} + +arch_initcall(omap_vram_init); + +/* boottime vram alloc stuff */ + +/* set from board file */ +static u32 omap_vram_sram_start __initdata; +static u32 omap_vram_sram_size __initdata; + +/* set from board file */ +static u32 omap_vram_sdram_start __initdata; +static u32 omap_vram_sdram_size __initdata; + +/* set from kernel cmdline */ +static u32 omap_vram_def_sdram_size __initdata; +static u32 omap_vram_def_sdram_start __initdata; + +static void __init omap_vram_early_vram(char **p) +{ + omap_vram_def_sdram_size = memparse(*p, p); + if (**p == ',') + omap_vram_def_sdram_start = simple_strtoul((*p) + 1, p, 16); +} +__early_param("vram=", omap_vram_early_vram); + +/* + * Called from map_io. We need to call to this early enough so that we + * can reserve the fixed SDRAM regions before VM could get hold of them. + */ +void __init omap_vram_reserve_sdram(void) +{ + struct bootmem_data *bdata; + unsigned long sdram_start, sdram_size; + u32 paddr; + u32 size = 0; + + /* cmdline arg overrides the board file definition */ + if (omap_vram_def_sdram_size) { + size = omap_vram_def_sdram_size; + paddr = omap_vram_def_sdram_start; + } + + if (!size) { + size = omap_vram_sdram_size; + paddr = omap_vram_sdram_start; + } + +#ifdef CONFIG_OMAP2_VRAM_SIZE + if (!size) { + size = CONFIG_OMAP2_VRAM_SIZE * 1024 * 1024; + paddr = 0; + } +#endif + + if (!size) + return; + + size = PAGE_ALIGN(size); + + bdata = NODE_DATA(0)->bdata; + sdram_start = bdata->node_min_pfn << PAGE_SHIFT; + sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start; + + if (paddr) { + if ((paddr & ~PAGE_MASK) || paddr < sdram_start || + paddr + size > sdram_start + sdram_size) { + pr_err("Illegal SDRAM region for VRAM\n"); + return; + } + + if (reserve_bootmem(paddr, size, BOOTMEM_EXCLUSIVE) < 0) { + pr_err("FB: failed to reserve VRAM\n"); + return; + } + } else { + if (size > sdram_size) { + pr_err("Illegal SDRAM size for VRAM\n"); + return; + } + + paddr = virt_to_phys(alloc_bootmem_pages(size)); + BUG_ON(paddr & ~PAGE_MASK); + } + + omap_vram_add_region(paddr, size); + + pr_info("Reserving %u bytes SDRAM for VRAM\n", size); +} + +/* + * Called at sram init time, before anything is pushed to the SRAM stack. + * Because of the stack scheme, we will allocate everything from the + * start of the lowest address region to the end of SRAM. This will also + * include padding for page alignment and possible holes between regions. + * + * As opposed to the SDRAM case, we'll also do any dynamic allocations at + * this point, since the driver built as a module would have problem with + * freeing / reallocating the regions. + */ +unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart, + unsigned long sram_vstart, + unsigned long sram_size, + unsigned long pstart_avail, + unsigned long size_avail) +{ + unsigned long pend_avail; + unsigned long reserved; + u32 paddr; + u32 size; + + paddr = omap_vram_sram_start; + size = omap_vram_sram_size; + + if (!size) + return 0; + + reserved = 0; + pend_avail = pstart_avail + size_avail; + + if (!paddr) { + /* Dynamic allocation */ + if ((size_avail & PAGE_MASK) < size) { + pr_err("Not enough SRAM for VRAM\n"); + return 0; + } + size_avail = (size_avail - size) & PAGE_MASK; + paddr = pstart_avail + size_avail; + } + + if (paddr < sram_pstart || + paddr + size > sram_pstart + sram_size) { + pr_err("Illegal SRAM region for VRAM\n"); + return 0; + } + + /* Reserve everything above the start of the region. */ + if (pend_avail - paddr > reserved) + reserved = pend_avail - paddr; + size_avail = pend_avail - reserved - pstart_avail; + + omap_vram_add_region(paddr, size); + + if (reserved) + pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved); + + return reserved; +} + +void __init omap_vram_set_sdram_vram(u32 size, u32 start) +{ + omap_vram_sdram_start = start; + omap_vram_sdram_size = size; +} + +void __init omap_vram_set_sram_vram(u32 size, u32 start) +{ + omap_vram_sram_start = start; + omap_vram_sram_size = size; +} diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c new file mode 100644 index 00000000000..fd227160037 --- /dev/null +++ b/drivers/video/omap2/vrfb.c @@ -0,0 +1,315 @@ +/* + * VRFB Rotation Engine + * + * Copyright (C) 2009 Nokia Corporation + * Author: Tomi Valkeinen + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef DEBUG +#define DBG(format, ...) pr_debug("VRFB: " format, ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +#define SMS_ROT_VIRT_BASE(context, rot) \ + (((context >= 4) ? 0xD0000000 : 0x70000000) \ + + (0x4000000 * (context)) \ + + (0x1000000 * (rot))) + +#define OMAP_VRFB_SIZE (2048 * 2048 * 4) + +#define VRFB_PAGE_WIDTH_EXP 5 /* Assuming SDRAM pagesize= 1024 */ +#define VRFB_PAGE_HEIGHT_EXP 5 /* 1024 = 2^5 * 2^5 */ +#define VRFB_PAGE_WIDTH (1 << VRFB_PAGE_WIDTH_EXP) +#define VRFB_PAGE_HEIGHT (1 << VRFB_PAGE_HEIGHT_EXP) +#define SMS_IMAGEHEIGHT_OFFSET 16 +#define SMS_IMAGEWIDTH_OFFSET 0 +#define SMS_PH_OFFSET 8 +#define SMS_PW_OFFSET 4 +#define SMS_PS_OFFSET 0 + +#define VRFB_NUM_CTXS 12 +/* bitmap of reserved contexts */ +static unsigned long ctx_map; + +static DEFINE_MUTEX(ctx_lock); + +/* + * Access to this happens from client drivers or the PM core after wake-up. + * For the first case we require locking at the driver level, for the second + * we don't need locking, since no drivers will run until after the wake-up + * has finished. + */ +static struct { + u32 physical_ba; + u32 control; + u32 size; +} vrfb_hw_context[VRFB_NUM_CTXS]; + +static inline void restore_hw_context(int ctx) +{ + omap2_sms_write_rot_control(vrfb_hw_context[ctx].control, ctx); + omap2_sms_write_rot_size(vrfb_hw_context[ctx].size, ctx); + omap2_sms_write_rot_physical_ba(vrfb_hw_context[ctx].physical_ba, ctx); +} + +static u32 get_image_width_roundup(u16 width, u8 bytespp) +{ + unsigned long stride = width * bytespp; + unsigned long ceil_pages_per_stride = (stride / VRFB_PAGE_WIDTH) + + (stride % VRFB_PAGE_WIDTH != 0); + + return ceil_pages_per_stride * VRFB_PAGE_WIDTH / bytespp; +} + +/* + * This the extra space needed in the VRFB physical area for VRFB to safely wrap + * any memory accesses to the invisible part of the virtual view to the physical + * area. + */ +static inline u32 get_extra_physical_size(u16 image_width_roundup, u8 bytespp) +{ + return (OMAP_VRFB_LINE_LEN - image_width_roundup) * VRFB_PAGE_HEIGHT * + bytespp; +} + +void omap_vrfb_restore_context(void) +{ + int i; + unsigned long map = ctx_map; + + for (i = ffs(map); i; i = ffs(map)) { + /* i=1..32 */ + i--; + map &= ~(1 << i); + restore_hw_context(i); + } +} + +void omap_vrfb_adjust_size(u16 *width, u16 *height, + u8 bytespp) +{ + *width = ALIGN(*width * bytespp, VRFB_PAGE_WIDTH) / bytespp; + *height = ALIGN(*height, VRFB_PAGE_HEIGHT); +} +EXPORT_SYMBOL(omap_vrfb_adjust_size); + +u32 omap_vrfb_min_phys_size(u16 width, u16 height, u8 bytespp) +{ + unsigned long image_width_roundup = get_image_width_roundup(width, + bytespp); + + if (image_width_roundup > OMAP_VRFB_LINE_LEN) + return 0; + + return (width * height * bytespp) + get_extra_physical_size( + image_width_roundup, bytespp); +} +EXPORT_SYMBOL(omap_vrfb_min_phys_size); + +u16 omap_vrfb_max_height(u32 phys_size, u16 width, u8 bytespp) +{ + unsigned long image_width_roundup = get_image_width_roundup(width, + bytespp); + unsigned long height; + unsigned long extra; + + if (image_width_roundup > OMAP_VRFB_LINE_LEN) + return 0; + + extra = get_extra_physical_size(image_width_roundup, bytespp); + + if (phys_size < extra) + return 0; + + height = (phys_size - extra) / (width * bytespp); + + /* Virtual views provided by VRFB are limited to 2048x2048. */ + return min_t(unsigned long, height, 2048); +} +EXPORT_SYMBOL(omap_vrfb_max_height); + +void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, + u16 width, u16 height, + unsigned bytespp, bool yuv_mode) +{ + unsigned pixel_size_exp; + u16 vrfb_width; + u16 vrfb_height; + u8 ctx = vrfb->context; + u32 size; + u32 control; + + DBG("omapfb_set_vrfb(%d, %lx, %dx%d, %d, %d)\n", ctx, paddr, + width, height, bytespp, yuv_mode); + + /* For YUV2 and UYVY modes VRFB needs to handle pixels a bit + * differently. See TRM. */ + if (yuv_mode) { + bytespp *= 2; + width /= 2; + } + + if (bytespp == 4) + pixel_size_exp = 2; + else if (bytespp == 2) + pixel_size_exp = 1; + else + BUG(); + + vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp; + vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT); + + DBG("vrfb w %u, h %u bytespp %d\n", vrfb_width, vrfb_height, bytespp); + + size = vrfb_width << SMS_IMAGEWIDTH_OFFSET; + size |= vrfb_height << SMS_IMAGEHEIGHT_OFFSET; + + control = pixel_size_exp << SMS_PS_OFFSET; + control |= VRFB_PAGE_WIDTH_EXP << SMS_PW_OFFSET; + control |= VRFB_PAGE_HEIGHT_EXP << SMS_PH_OFFSET; + + vrfb_hw_context[ctx].physical_ba = paddr; + vrfb_hw_context[ctx].size = size; + vrfb_hw_context[ctx].control = control; + + omap2_sms_write_rot_physical_ba(paddr, ctx); + omap2_sms_write_rot_size(size, ctx); + omap2_sms_write_rot_control(control, ctx); + + DBG("vrfb offset pixels %d, %d\n", + vrfb_width - width, vrfb_height - height); + + vrfb->xres = width; + vrfb->yres = height; + vrfb->xoffset = vrfb_width - width; + vrfb->yoffset = vrfb_height - height; + vrfb->bytespp = bytespp; + vrfb->yuv_mode = yuv_mode; +} +EXPORT_SYMBOL(omap_vrfb_setup); + +int omap_vrfb_map_angle(struct vrfb *vrfb, u16 height, u8 rot) +{ + unsigned long size = height * OMAP_VRFB_LINE_LEN * vrfb->bytespp; + + vrfb->vaddr[rot] = ioremap_wc(vrfb->paddr[rot], size); + + if (!vrfb->vaddr[rot]) { + printk(KERN_ERR "vrfb: ioremap failed\n"); + return -ENOMEM; + } + + DBG("ioremapped vrfb area %d of size %lu into %p\n", rot, size, + vrfb->vaddr[rot]); + + return 0; +} +EXPORT_SYMBOL(omap_vrfb_map_angle); + +void omap_vrfb_release_ctx(struct vrfb *vrfb) +{ + int rot; + int ctx = vrfb->context; + + if (ctx == 0xff) + return; + + DBG("release ctx %d\n", ctx); + + mutex_lock(&ctx_lock); + + BUG_ON(!(ctx_map & (1 << ctx))); + + clear_bit(ctx, &ctx_map); + + for (rot = 0; rot < 4; ++rot) { + if (vrfb->paddr[rot]) { + release_mem_region(vrfb->paddr[rot], OMAP_VRFB_SIZE); + vrfb->paddr[rot] = 0; + } + } + + vrfb->context = 0xff; + + mutex_unlock(&ctx_lock); +} +EXPORT_SYMBOL(omap_vrfb_release_ctx); + +int omap_vrfb_request_ctx(struct vrfb *vrfb) +{ + int rot; + u32 paddr; + u8 ctx; + int r; + + DBG("request ctx\n"); + + mutex_lock(&ctx_lock); + + for (ctx = 0; ctx < VRFB_NUM_CTXS; ++ctx) + if ((ctx_map & (1 << ctx)) == 0) + break; + + if (ctx == VRFB_NUM_CTXS) { + pr_err("vrfb: no free contexts\n"); + r = -EBUSY; + goto out; + } + + DBG("found free ctx %d\n", ctx); + + set_bit(ctx, &ctx_map); + + memset(vrfb, 0, sizeof(*vrfb)); + + vrfb->context = ctx; + + for (rot = 0; rot < 4; ++rot) { + paddr = SMS_ROT_VIRT_BASE(ctx, rot); + if (!request_mem_region(paddr, OMAP_VRFB_SIZE, "vrfb")) { + pr_err("vrfb: failed to reserve VRFB " + "area for ctx %d, rotation %d\n", + ctx, rot * 90); + omap_vrfb_release_ctx(vrfb); + r = -ENOMEM; + goto out; + } + + vrfb->paddr[rot] = paddr; + + DBG("VRFB %d/%d: %lx\n", ctx, rot*90, vrfb->paddr[rot]); + } + + r = 0; +out: + mutex_unlock(&ctx_lock); + return r; +} +EXPORT_SYMBOL(omap_vrfb_request_ctx); diff --git a/include/linux/can/platform/ti_hecc.h b/include/linux/can/platform/ti_hecc.h new file mode 100644 index 00000000000..4688c7bb1bd --- /dev/null +++ b/include/linux/can/platform/ti_hecc.h @@ -0,0 +1,40 @@ +/* + * TI HECC (High End CAN Controller) driver platform header + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed as is WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/** + * struct hecc_platform_data - HECC Platform Data + * + * @scc_hecc_offset: mostly 0 - should really never change + * @scc_ram_offset: SCC RAM offset + * @hecc_ram_offset: HECC RAM offset + * @mbx_offset: Mailbox RAM offset + * @int_line: Interrupt line to use - 0 or 1 + * @version: version for future use + * + * Platform data structure to get all platform specific settings. + * this structure also accounts the fact that the IP may have different + * RAM and mailbox offsets for different SOC's + */ +struct ti_hecc_platform_data { + u32 scc_hecc_offset; + u32 scc_ram_offset; + u32 hecc_ram_offset; + u32 mbx_offset; + u32 int_line; + u32 version; +}; + + diff --git a/include/linux/davinci_emac.h b/include/linux/davinci_emac.h new file mode 100644 index 00000000000..b318dfd8267 --- /dev/null +++ b/include/linux/davinci_emac.h @@ -0,0 +1,39 @@ +/* + * TI DaVinci EMAC platform support + * + * Author: Kevin Hilman, Deep Root Systems, LLC + * + * 2007 (c) Deep Root Systems, LLC. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef _LINUX_DAVINCI_EMAC_H +#define _LINUX_DAVINCI_EMAC_H + +#include +#include + +struct emac_platform_data { + char mac_addr[ETH_ALEN]; + u32 ctrl_reg_offset; + u32 ctrl_mod_reg_offset; + u32 ctrl_ram_offset; + u32 hw_ram_addr; + u32 mdio_reg_offset; + u32 ctrl_ram_size; + u32 phy_mask; + u32 mdio_max_freq; + u8 rmii_en; + u8 version; + void (*wrapper_interrupt_enable) (void); + void (*wrapper_interrupt_disable) (void); +}; + +enum { + EMAC_VERSION_1, /* DM644x */ + EMAC_VERSION_2, /* DM646x */ +}; + +void davinci_get_mac_addr(struct memory_accessor *mem_acc, void *context); +#endif diff --git a/include/linux/i2c/tsc2004.h b/include/linux/i2c/tsc2004.h new file mode 100644 index 00000000000..044c36f740c --- /dev/null +++ b/include/linux/i2c/tsc2004.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_I2C_TSC2004_H +#define __LINUX_I2C_TSC2004_H + +/* linux/i2c/tsc2004.h */ + +struct tsc2004_platform_data { + u16 model; /* 2004. */ + u16 x_plate_ohms; + + int (*get_pendown_state)(void); + void (*clear_penirq)(void); /* If needed, clear 2nd level + interrupt source */ + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; + +#endif diff --git a/include/linux/omap_resizer.h b/include/linux/omap_resizer.h new file mode 100644 index 00000000000..47b8dd84174 --- /dev/null +++ b/include/linux/omap_resizer.h @@ -0,0 +1,137 @@ +/* + * drivers/media/video/isp/omap_resizer.h + * + * Include file for Resizer module wrapper in TI's OMAP3430 ISP + * + * Copyright (C) 2008 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 OMAP_RESIZER_H +#define OMAP_RESIZER_H + +#include + +/* ioctls definition */ +#define RSZ_IOC_BASE 'R' +#define RSZ_IOC_MAXNR 9 + +/*Ioctl options which are to be passed while calling the ioctl*/ +#define RSZ_REQBUF _IOWR(RSZ_IOC_BASE, 1,\ + struct v4l2_requestbuffers) +#define RSZ_QUERYBUF _IOWR(RSZ_IOC_BASE, 2, struct v4l2_buffer) +#define RSZ_S_PARAM _IOWR(RSZ_IOC_BASE, 3, struct rsz_params) +#define RSZ_G_PARAM _IOWR(RSZ_IOC_BASE, 4, struct rsz_params) +#define RSZ_RESIZE _IOWR(RSZ_IOC_BASE, 5, __s32) +#define RSZ_G_STATUS _IOWR(RSZ_IOC_BASE, 6, struct rsz_status) +#define RSZ_QUEUEBUF _IOWR(RSZ_IOC_BASE, 7, struct v4l2_buffer) +#define RSZ_GET_CROPSIZE _IOWR(RSZ_IOC_BASE, 8, struct rsz_cropsize) +#define RSZ_S_EXP _IOWR(RSZ_IOC_BASE, 9, __s32) + +#define RSZ_INTYPE_YCBCR422_16BIT 0 +#define RSZ_INTYPE_PLANAR_8BIT 1 +#define RSZ_PIX_FMT_UYVY 1 /* cb:y:cr:y */ +#define RSZ_PIX_FMT_YUYV 0 /* y:cb:y:cr */ + +enum config_done { + STATE_CONFIGURED, /* Resizer driver configured + * by application. + */ + STATE_NOT_CONFIGURED /* Resizer driver not + * configured by application. + */ +}; + +/* Structure Definitions */ + +/* used to luma enhancement options */ + +struct rsz_yenh { + __s32 type; /* represents luma enable or + * disable. + */ + __u8 gain; /* represents gain. */ + __u8 slop; /* represents slop. */ + __u8 core; /* Represents core value. */ +}; + +/* Conatins all the parameters for resizing. This structure + * is used to configure resiser parameters + */ +struct rsz_params { + __s32 in_hsize; /* input frame horizontal + * size. + */ + __s32 in_vsize; /* input frame vertical size */ + __s32 in_pitch; /* offset between two rows of + * input frame. + */ + __s32 inptyp; /* for determining 16 bit or + * 8 bit data. + */ + __s32 vert_starting_pixel; /* for specifying vertical + * starting pixel in input. + */ + __s32 horz_starting_pixel; /* for specyfing horizontal + * starting pixel in input. + */ + __s32 cbilin; /* # defined, filter with luma + * or bi-linear interpolation. + */ + __s32 pix_fmt; /* # defined, UYVY or YUYV */ + __s32 out_hsize; /* output frame horizontal + * size. + */ + __s32 out_vsize; /* output frame vertical + * size. + */ + __s32 out_pitch; /* offset between two rows of + * output frame. + */ + __s32 hstph; /* for specifying horizontal + * starting phase. + */ + __s32 vstph; /* for specifying vertical + * starting phase. + */ + __u16 tap4filt_coeffs[32]; /* horizontal filter + * coefficients. + */ + __u16 tap7filt_coeffs[32]; /* vertical filter + * coefficients. + */ + struct rsz_yenh yenh_params; +}; + +/* Contains the status of hardware and channel */ +struct rsz_status { + __s32 chan_busy; /* 1: channel is busy, + * 0: channel is not busy + */ + __s32 hw_busy; /* 1: hardware is busy, + * 0: hardware is not busy + */ + __s32 src; /* # defined, can be either + * SD-RAM or CCDC/PREVIEWER + */ +}; + +/* Passed by application for getting crop size */ +struct rsz_cropsize { + __u32 hcrop; /* Number of pixels per line + * cropped in output image. + */ + + __u32 vcrop; /* Number of lines cropped + * in output image. + */ +}; + +#endif diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h new file mode 100644 index 00000000000..f46c40ac6d4 --- /dev/null +++ b/include/linux/omapfb.h @@ -0,0 +1,251 @@ +/* + * File: include/linux/omapfb.h + * + * Framebuffer driver for TI OMAP boards + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __LINUX_OMAPFB_H__ +#define __LINUX_OMAPFB_H__ + +#include +#include +#include + +/* IOCTL commands. */ + +#define OMAP_IOW(num, dtype) _IOW('O', num, dtype) +#define OMAP_IOR(num, dtype) _IOR('O', num, dtype) +#define OMAP_IOWR(num, dtype) _IOWR('O', num, dtype) +#define OMAP_IO(num) _IO('O', num) + +#define OMAPFB_MIRROR OMAP_IOW(31, int) +#define OMAPFB_SYNC_GFX OMAP_IO(37) +#define OMAPFB_VSYNC OMAP_IO(38) +#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int) +#define OMAPFB_GET_CAPS OMAP_IOR(42, struct omapfb_caps) +#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int) +#define OMAPFB_LCD_TEST OMAP_IOW(45, int) +#define OMAPFB_CTRL_TEST OMAP_IOW(46, int) +#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(47, struct omapfb_update_window_old) +#define OMAPFB_SET_COLOR_KEY OMAP_IOW(50, struct omapfb_color_key) +#define OMAPFB_GET_COLOR_KEY OMAP_IOW(51, struct omapfb_color_key) +#define OMAPFB_SETUP_PLANE OMAP_IOW(52, struct omapfb_plane_info) +#define OMAPFB_QUERY_PLANE OMAP_IOW(53, struct omapfb_plane_info) +#define OMAPFB_UPDATE_WINDOW OMAP_IOW(54, struct omapfb_update_window) +#define OMAPFB_SETUP_MEM OMAP_IOW(55, struct omapfb_mem_info) +#define OMAPFB_QUERY_MEM OMAP_IOW(56, struct omapfb_mem_info) +#define OMAPFB_WAITFORVSYNC OMAP_IO(57) +#define OMAPFB_MEMORY_READ OMAP_IOR(58, struct omapfb_memory_read) +#define OMAPFB_GET_OVERLAY_COLORMODE OMAP_IOR(59, struct omapfb_ovl_colormode) +#define OMAPFB_WAITFORGO OMAP_IO(60) +#define OMAPFB_GET_VRAM_INFO OMAP_IOR(61, struct omapfb_vram_info) +#define OMAPFB_SET_TEARSYNC OMAP_IOW(62, struct omapfb_tearsync_info) + +#define OMAPFB_CAPS_GENERIC_MASK 0x00000fff +#define OMAPFB_CAPS_LCDC_MASK 0x00fff000 +#define OMAPFB_CAPS_PANEL_MASK 0xff000000 + +#define OMAPFB_CAPS_MANUAL_UPDATE 0x00001000 +#define OMAPFB_CAPS_TEARSYNC 0x00002000 +#define OMAPFB_CAPS_PLANE_RELOCATE_MEM 0x00004000 +#define OMAPFB_CAPS_PLANE_SCALE 0x00008000 +#define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE 0x00010000 +#define OMAPFB_CAPS_WINDOW_SCALE 0x00020000 +#define OMAPFB_CAPS_WINDOW_OVERLAY 0x00040000 +#define OMAPFB_CAPS_WINDOW_ROTATE 0x00080000 +#define OMAPFB_CAPS_SET_BACKLIGHT 0x01000000 + +/* Values from DSP must map to lower 16-bits */ +#define OMAPFB_FORMAT_MASK 0x00ff +#define OMAPFB_FORMAT_FLAG_DOUBLE 0x0100 +#define OMAPFB_FORMAT_FLAG_TEARSYNC 0x0200 +#define OMAPFB_FORMAT_FLAG_FORCE_VSYNC 0x0400 +#define OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY 0x0800 +#define OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY 0x1000 + +#define OMAPFB_MEMTYPE_SDRAM 0 +#define OMAPFB_MEMTYPE_SRAM 1 +#define OMAPFB_MEMTYPE_MAX 1 + +enum omapfb_color_format { + OMAPFB_COLOR_RGB565 = 0, + OMAPFB_COLOR_YUV422, + OMAPFB_COLOR_YUV420, + OMAPFB_COLOR_CLUT_8BPP, + OMAPFB_COLOR_CLUT_4BPP, + OMAPFB_COLOR_CLUT_2BPP, + OMAPFB_COLOR_CLUT_1BPP, + OMAPFB_COLOR_RGB444, + OMAPFB_COLOR_YUY422, + + OMAPFB_COLOR_ARGB16, + OMAPFB_COLOR_RGB24U, /* RGB24, 32-bit container */ + OMAPFB_COLOR_RGB24P, /* RGB24, 24-bit container */ + OMAPFB_COLOR_ARGB32, + OMAPFB_COLOR_RGBA32, + OMAPFB_COLOR_RGBX32, +}; + +struct omapfb_update_window { + __u32 x, y; + __u32 width, height; + __u32 format; + __u32 out_x, out_y; + __u32 out_width, out_height; + __u32 reserved[8]; +}; + +struct omapfb_update_window_old { + __u32 x, y; + __u32 width, height; + __u32 format; +}; + +enum omapfb_plane { + OMAPFB_PLANE_GFX = 0, + OMAPFB_PLANE_VID1, + OMAPFB_PLANE_VID2, +}; + +enum omapfb_channel_out { + OMAPFB_CHANNEL_OUT_LCD = 0, + OMAPFB_CHANNEL_OUT_DIGIT, +}; + +struct omapfb_plane_info { + __u32 pos_x; + __u32 pos_y; + __u8 enabled; + __u8 channel_out; + __u8 mirror; + __u8 reserved1; + __u32 out_width; + __u32 out_height; + __u32 reserved2[12]; +}; + +struct omapfb_mem_info { + __u32 size; + __u8 type; + __u8 reserved[3]; +}; + +struct omapfb_caps { + __u32 ctrl; + __u32 plane_color; + __u32 wnd_color; +}; + +enum omapfb_color_key_type { + OMAPFB_COLOR_KEY_DISABLED = 0, + OMAPFB_COLOR_KEY_GFX_DST, + OMAPFB_COLOR_KEY_VID_SRC, +}; + +struct omapfb_color_key { + __u8 channel_out; + __u32 background; + __u32 trans_key; + __u8 key_type; +}; + +enum omapfb_update_mode { + OMAPFB_UPDATE_DISABLED = 0, + OMAPFB_AUTO_UPDATE, + OMAPFB_MANUAL_UPDATE +}; + +struct omapfb_memory_read { + __u16 x; + __u16 y; + __u16 w; + __u16 h; + size_t buffer_size; + void __user *buffer; +}; + +struct omapfb_ovl_colormode { + __u8 overlay_idx; + __u8 mode_idx; + __u32 bits_per_pixel; + __u32 nonstd; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +struct omapfb_vram_info { + __u32 total; + __u32 free; + __u32 largest_free_block; + __u32 reserved[5]; +}; + +struct omapfb_tearsync_info { + __u8 enabled; + __u8 reserved1[3]; + __u16 line; + __u16 reserved2; +}; + +#ifdef __KERNEL__ + +#include + +#ifdef CONFIG_ARCH_OMAP1 +#define OMAPFB_PLANE_NUM 1 +#else +#define OMAPFB_PLANE_NUM 3 +#endif + +struct omapfb_mem_region { + u32 paddr; + void __iomem *vaddr; + unsigned long size; + u8 type; /* OMAPFB_PLANE_MEM_* */ + enum omapfb_color_format format;/* OMAPFB_COLOR_* */ + unsigned format_used:1; /* Must be set when format is set. + * Needed b/c of the badly chosen 0 + * base for OMAPFB_COLOR_* values + */ + unsigned alloc:1; /* allocated by the driver */ + unsigned map:1; /* kernel mapped by the driver */ +}; + +struct omapfb_mem_desc { + int region_cnt; + struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; +}; + +struct omapfb_platform_data { + struct omap_lcd_config lcd; + struct omapfb_mem_desc mem_desc; + void *ctrl_platform_data; +}; + +/* in arch/arm/plat-omap/fb.c */ +extern void omapfb_set_platform_data(struct omapfb_platform_data *data); +extern void omapfb_set_ctrl_platform_data(void *pdata); +extern void omapfb_reserve_sdram(void); + +#endif + +#endif /* __OMAPFB_H */ diff --git a/include/media/dw9710.h b/include/media/dw9710.h new file mode 100644 index 00000000000..b5696e4fa50 --- /dev/null +++ b/include/media/dw9710.h @@ -0,0 +1,35 @@ +/* + * include/media/dw9710.h + * + * Public defines for Auto Focus device + * + * Copyright (C) 2008 Texas Instruments. + * + * Contributors: + * Sergio Aguirre + * Troy Laramy + * Mohit Jalori + * + * 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 DW9710_H +#define DW9710_H + +#define DW9710_NAME "dw9710" +#define DW9710_AF_I2C_ADDR 0x0C + +/** + * struct dw9710_platform_data - platform data values and access functions + * @power_set: Power state access function, zero is off, non-zero is on. + * @priv_data_set: device private data (pointer) access function + */ +struct dw9710_platform_data { + int (*power_set)(enum v4l2_power power); + int (*priv_data_set)(void *); +}; + +#endif /* End of of DW9710_H */ diff --git a/include/media/imx046.h b/include/media/imx046.h new file mode 100644 index 00000000000..2349fcdcc8c --- /dev/null +++ b/include/media/imx046.h @@ -0,0 +1,44 @@ +/* + * imx046.h - Shared settings for the IMX046 CameraChip. + * + * Contributors: + * Dominic Curran + * + * Copyright (C) 2008 Hewlett Packard. + * 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 IMX046_H +#define IMX046_H + +#define IMX046_I2C_ADDR 0x1A + +/* Average black level */ +#define IMX046_BLACK_LEVEL_AVG 64 + +/** + * struct imx046_platform_data - platform data values and access functions + * @power_set: Power state access function, zero is off, non-zero is on. + * @default_regs: Default registers written after power-on or reset. + * @ifparm: Interface parameters access function + * @priv_data_set: device private data (pointer) access function + */ +struct imx046_platform_data { + int (*power_set)(struct v4l2_int_device *s, enum v4l2_power power); + int (*ifparm)(struct v4l2_ifparm *p); + int (*priv_data_set)(struct v4l2_int_device *s, void *); + u32 (*set_xclk)(struct v4l2_int_device *s, u32 xclkfreq); + int (*cfg_interface_bridge)(u32); + int (*csi2_lane_count)(int count); + int (*csi2_cfg_vp_out_ctrl)(u8 vp_out_ctrl); + int (*csi2_ctrl_update)(bool); + int (*csi2_cfg_virtual_id)(u8 ctx, u8 id); + int (*csi2_ctx_update)(u8 ctx, bool); + int (*csi2_calc_phy_cfg0)(u32, u32, u32); +}; + +#endif /* ifndef IMX046_H */ diff --git a/include/media/lv8093.h b/include/media/lv8093.h new file mode 100644 index 00000000000..e028f80cd49 --- /dev/null +++ b/include/media/lv8093.h @@ -0,0 +1,30 @@ +/* + * lv8093.h + * + * Copyright (C) 2008-2009 Texas Instruments. + * Copyright (C) 2009 Hewlett-Packard. + * + * 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 LV8093_H +#define LV8093_H + +/* i2c slave address = 0xE4 */ +#define LV8093_AF_I2C_ADDR 0x72 +#define LV8093_NAME "lv8093" + +/** + * struct lv8093_platform_data - platform data values and access functions + * @power_set: Power state access function, zero is off, non-zero is on. + * @priv_data_set: device private data (pointer) access function + */ +struct lv8093_platform_data { + int (*power_set)(enum v4l2_power power); + int (*priv_data_set)(void *); +}; + +#endif /* End of of LV8093_H */ diff --git a/include/media/mt9p012.h b/include/media/mt9p012.h new file mode 100644 index 00000000000..dc6f5ba3943 --- /dev/null +++ b/include/media/mt9p012.h @@ -0,0 +1,36 @@ +/* + * mt9p012.h - Register definitions for the MT9P012 camera sensor. + * + * Copyright (C) 2009 Texas Instruments. + * + * Contributors: + * Sameer Venkatraman + * Sergio Aguirre + * Martinez Leonides + * + * 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 MT9P012_H +#define MT9P012_H + +#include + +#define MT9P012_I2C_ADDR 0x10 + +/** + * struct mt9p012_platform_data - platform data values and access functions + * @power_set: Power state access function, zero is off, non-zero is on. + * @default_regs: Default registers written after power-on or reset. + * @ifparm: Interface parameters access function + * @priv_data_set: device private data (pointer) access function + */ +struct mt9p012_platform_data { + int (*power_set)(struct v4l2_int_device *s, enum v4l2_power power); + u32 (*set_xclk)(struct v4l2_int_device *s, u32 xclkfreq); + int (*priv_data_set)(struct v4l2_int_device *s, void *); +}; + +#endif /* ifndef MT9P012_H */ diff --git a/include/media/ov3640.h b/include/media/ov3640.h new file mode 100644 index 00000000000..0c1159e4be8 --- /dev/null +++ b/include/media/ov3640.h @@ -0,0 +1,29 @@ +/* + * ov3640.h - Shared settings for the OV3640 CameraChip. + * + * Contributors: + * Pallavi Kulkarni + * Sergio Aguirre + * + * 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 OV3640_H +#define OV3640_H + +#include + +#define OV3640_I2C_ADDR (0x78 >> 1) + +struct ov3640_platform_data { + /* Set power state, zero is off, non-zero is on. */ + int (*power_set)(struct v4l2_int_device *s, enum v4l2_power power); + u32 (*set_xclk)(struct v4l2_int_device *s, u32 xclkfreq); + int (*priv_data_set)(struct v4l2_int_device *s, void *); +}; + +#endif /* ifndef OV3640_H */ diff --git a/include/media/ti-media/ccdc_types.h b/include/media/ti-media/ccdc_types.h new file mode 100644 index 00000000000..5773874bf26 --- /dev/null +++ b/include/media/ti-media/ccdc_types.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + **************************************************************************/ +#ifndef _CCDC_TYPES_H +#define _CCDC_TYPES_H +enum ccdc_pixfmt { + CCDC_PIXFMT_RAW, + CCDC_PIXFMT_YCBCR_16BIT, + CCDC_PIXFMT_YCBCR_8BIT +}; + +enum ccdc_frmfmt { + CCDC_FRMFMT_PROGRESSIVE, + CCDC_FRMFMT_INTERLACED +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum ccdc_pixorder { + CCDC_PIXORDER_YCBYCR, + CCDC_PIXORDER_CBYCRY, +}; + +enum ccdc_buftype { + CCDC_BUFTYPE_FLD_INTERLEAVED, + CCDC_BUFTYPE_FLD_SEPARATED +}; +#endif diff --git a/include/media/ti-media/dm355_ccdc.h b/include/media/ti-media/dm355_ccdc.h new file mode 100644 index 00000000000..0479cdc44b4 --- /dev/null +++ b/include/media/ti-media/dm355_ccdc.h @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM355_CCDC_H +#define _DM355_CCDC_H +#include +#include + +/* enum for No of pixel per line to be avg. in Black Clamping */ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gama width */ +enum ccdc_gamma_width { + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 +}; + +enum ccdc_colpats { + CCDC_RED, + CCDC_GREEN_RED, + CCDC_GREEN_BLUE, + CCDC_BLUE +}; + +struct ccdc_col_pat { + enum ccdc_colpats olop; + enum ccdc_colpats olep; + enum ccdc_colpats elop; + enum ccdc_colpats elep; +}; + +enum ccdc_datasft { + CCDC_DATA_NO_SHIFT, + CCDC_DATA_SHIFT_1BIT, + CCDC_DATA_SHIFT_2BIT, + CCDC_DATA_SHIFT_3BIT, + CCDC_DATA_SHIFT_4BIT, + CCDC_DATA_SHIFT_5BIT, + CCDC_DATA_SHIFT_6BIT +}; + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; +enum ccdc_mfilt1 { + CCDC_NO_MEDIAN_FILTER1, + CCDC_AVERAGE_FILTER1, + CCDC_MEDIAN_FILTER1 +}; + +enum ccdc_mfilt2 { + CCDC_NO_MEDIAN_FILTER2, + CCDC_AVERAGE_FILTER2, + CCDC_MEDIAN_FILTER2 +}; + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gama Width Input */ + enum ccdc_gamma_width gama_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + /* only if bClampEnable is TRUE */ + unsigned char b_clamp_enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is FALSE */ + unsigned short sgain; + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + unsigned char r; + /* Constant value to subtract from Gr component */ + unsigned char gr; + /* Constant value to subtract from Blue component */ + unsigned char b; + /* Constant value to subtract from Gb component */ + unsigned char gb; +}; + +struct ccdc_float { + int integer; + unsigned int decimal; +}; + +#define CCDC_CSC_COEFF_TABLE_SIZE 16 +/* structure for color space converter */ +struct ccdc_csc { + unsigned char enable; + /* + * S8Q5. Use 2 decimal precision, user values range from -3.00 to 3.99. + * example - to use 1.03, set integer part as 1, and decimal part as 3 + * to use -1.03, set integer part as -1 and decimal part as 3 + */ + struct ccdc_float coeff[CCDC_CSC_COEFF_TABLE_SIZE]; +}; + +/* Structures for Vertical Defect Correction*/ +enum ccdc_vdf_csl { + CCDC_VDF_NORMAL, + CCDC_VDF_HORZ_INTERPOL_SAT, + CCDC_VDF_HORZ_INTERPOL +}; + +enum ccdc_vdf_cuda { + CCDC_VDF_WHOLE_LINE_CORRECT, + CCDC_VDF_UPPER_DISABLE +}; + +enum ccdc_dfc_mwr { + CCDC_DFC_MWR_WRITE_COMPLETE, + CCDC_DFC_WRITE_REG +}; + +enum ccdc_dfc_mrd { + CCDC_DFC_READ_COMPLETE, + CCDC_DFC_READ_REG +}; + +enum ccdc_dfc_ma_rst { + CCDC_DFC_INCR_ADDR, + CCDC_DFC_CLR_ADDR +}; + +enum ccdc_dfc_mclr { + CCDC_DFC_CLEAR_COMPLETE, + CCDC_DFC_CLEAR +}; + +struct ccdc_dft_corr_ctl { + enum ccdc_vdf_csl vdfcsl; + enum ccdc_vdf_cuda vdfcuda; + unsigned int vdflsft; +}; + +struct ccdc_dft_corr_mem_ctl { + enum ccdc_dfc_mwr dfcmwr; + enum ccdc_dfc_mrd dfcmrd; + enum ccdc_dfc_ma_rst dfcmarst; + enum ccdc_dfc_mclr dfcmclr; +}; + +#define CCDC_DFT_TABLE_SIZE 16 +/* + * Main Structure for vertical defect correction. Vertical defect + * correction can correct upto 16 defects if defects less than 16 + * then pad the rest with 0 + */ +struct ccdc_vertical_dft { + unsigned char ver_dft_en; + unsigned char gen_dft_en; + unsigned int saturation_ctl; + struct ccdc_dft_corr_ctl dft_corr_ctl; + struct ccdc_dft_corr_mem_ctl dft_corr_mem_ctl; + int table_size; + unsigned int dft_corr_horz[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_vert[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub1[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub2[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub3[CCDC_DFT_TABLE_SIZE]; +}; + +struct ccdc_data_offset { + unsigned char horz_offset; + unsigned char vert_offset; +}; + +/* + * Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data shift to be applied before storing */ + enum ccdc_datasft datasft; + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* median filter for sdram */ + enum ccdc_mfilt1 mfilt1; + enum ccdc_mfilt2 mfilt2; + /* low pass filter enable/disable */ + unsigned char lpf_enable; + /* Threshold of median filter */ + int med_filt_thres; + /* + * horz and vertical data offset. Appliable for defect correction + * and lsc + */ + struct ccdc_data_offset data_offset; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; + /* struture for vertical Defect Correction Module Configuration */ + struct ccdc_vertical_dft vertical_dft; + /* structure for color space converter Module Configuration */ + struct ccdc_csc csc; + /* color patters for bayer capture */ + struct ccdc_col_pat col_pat_field0; + struct ccdc_col_pat col_pat_field1; +}; + +#ifdef __KERNEL__ +#include + +#define CCDC_WIN_PAL {0, 0, 720, 576} +#define CCDC_WIN_VGA {0, 0, 640, 480} + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; + +/* Gain applied to Raw Bayer data */ +struct ccdc_gain { + unsigned short r_ye; + unsigned short gr_cy; + unsigned short gb_g; + unsigned short b_mg; +}; + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct ccdc_gain gain; + /* offset */ + unsigned int ccdc_offset; + /* horizontal flip enable */ + unsigned char horz_flip_enable; + /* + * enable to store the image in inverse order in memory + * (bottom to top) + */ + unsigned char image_invert_enable; + /* Configurable part of raw data */ + struct ccdc_config_params_raw config_params; +}; + +#endif +#endif /* DM355_CCDC_H */ diff --git a/include/media/ti-media/dm644x_ccdc.h b/include/media/ti-media/dm644x_ccdc.h new file mode 100644 index 00000000000..0e63d1cbf1f --- /dev/null +++ b/include/media/ti-media/dm644x_ccdc.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM644X_CCDC_H +#define _DM644X_CCDC_H +#include +#include + +/* enum for No of pixel per line to be avg. in Black Clamping*/ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gama width */ +enum ccdc_gama_width { + CCDC_GAMMA_BITS_15_6, + CCDC_GAMMA_BITS_14_5, + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 +}; + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gama Width Input */ + enum ccdc_gama_width gama_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + unsigned char enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is TRUE */ + unsigned short sgain; + /* only if bClampEnable is FALSE */ + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + char r; + /* Constant value to subtract from Gr component */ + char gr; + /* Constant value to subtract from Blue component */ + char b; + /* Constant value to subtract from Gb component */ + char gb; +}; + +/* structure for fault pixel correction */ +struct ccdc_fault_pixel { + /* Enable or Disable fault pixel correction */ + unsigned char enable; + /* Number of fault pixel */ + unsigned short fp_num; + /* Address of fault pixel table */ + unsigned int fpc_table_addr; +}; + +/* Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; + /* Structure for Fault Pixel Module Configuration */ + struct ccdc_fault_pixel fault_pxl; +}; + + +#ifdef __KERNEL__ +#include +/* Define to enable/disable video port */ +#define FP_NUM_BYTES 4 +/* Define for extra pixel/line and extra lines/frame */ +#define NUM_EXTRAPIXELS 8 +#define NUM_EXTRALINES 8 + +/* settings for commonly used video formats */ +#define CCDC_WIN_PAL {0, 0, 720, 576} +/* ntsc square pixel */ +#define CCDC_WIN_VGA {0, 0, (640 + NUM_EXTRAPIXELS), (480 + NUM_EXTRALINES)} + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* + * enable to store the image in inverse + * order in memory(bottom to top) + */ + unsigned char image_invert_enable; + /* configurable paramaters */ + struct ccdc_config_params_raw config_params; +}; + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; +#endif +#endif /* _DM644X_CCDC_H */ diff --git a/include/media/ti-media/vpfe_capture.h b/include/media/ti-media/vpfe_capture.h new file mode 100644 index 00000000000..44d1d35fb41 --- /dev/null +++ b/include/media/ti-media/vpfe_capture.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VPFE_CAPTURE_H +#define _VPFE_CAPTURE_H + +#ifdef __KERNEL__ + +/* Header files */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define VPFE_CAPTURE_NUM_DECODERS 5 + +/* Macros */ +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_pixel_format { + struct v4l2_fmtdesc fmtdesc; + /* bytes per pixel */ + int bpp; + /* decoder format */ + u32 subdev_pix_fmt; +}; + +struct vpfe_std_info { + int active_pixels; + int active_lines; + /* current frame format */ + int frame_format; +}; + +struct vpfe_route { + u32 input; + u32 output; +}; + +enum vpfe_subdev_id { + VPFE_SUBDEV_TVP5146 = 1, + VPFE_SUBDEV_MT9T031 = 2 +}; + +struct vpfe_subdev_info { + /* Sub device module name */ + char module_name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; + /* Is this a camera sub device ? */ + unsigned is_camera:1; + /* check if sub dev supports routing */ + unsigned can_route:1; + /* registered ? */ + unsigned registered:1; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* I2C Bus adapter no */ + int i2c_adapter_id; + /* information about each subdev */ + struct vpfe_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* ccdc name */ + char *ccdc; + /* setup function for the input path */ + int (*setup_input)(enum vpfe_subdev_id id); + /* Function for Clearing the interrupt */ + void (*clr_intr)(int vdint); + /* number of clocks */ + int num_clocks; + /* clocks used for vpfe capture */ + char *clocks[]; +}; + +struct vpfe_device { + /* V4l2 specific parameters */ + /* Identifies video device for this channel */ + struct video_device *video_dev; + /* sub devices */ + struct v4l2_subdev **sd; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* clock ptrs for vpfe capture */ + struct clk **clks; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* Indicates id of the field which is being displayed */ + u32 field_id; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* current interface type */ + struct vpfe_hw_if_param vpfe_if_params; + /* ptr to currently selected sub device */ + struct vpfe_subdev_info *current_subdev; + /* current input at the sub device */ + int current_input; + /* Keeps track of the information about the standard */ + struct vpfe_std_info std_info; + /* std index into std table */ + int std_index; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* number of buffers in fbuffers */ + u32 numbuffers; + /* List of buffer pointers for storing frames */ + u8 *fbuffers[VIDEO_MAX_FRAME]; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* + * used when IMP is chained to store the crop window which + * is different from the image window + */ + struct v4l2_rect crop; + /* Buffer queue used in video-buf */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Indicates whether streaming started */ + u8 started; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +/* File handle structure */ +struct vpfe_fh { + struct vpfe_device *vpfe_dev; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +struct vpfe_config_params { + u8 min_numbuffers; + u8 numbuffers; + u32 min_bufsize; + u32 device_bufsize; +}; + +#endif /* End of __KERNEL__ */ +/** + * VPFE_CMD_S_CCDC_RAW_PARAMS - EXPERIMENTAL IOCTL to set raw capture params + * This can be used to configure modules such as defect pixel correction, + * color space conversion, culling etc. This is an experimental ioctl that + * will change in future kernels. So use this ioctl with care ! + * TODO: This is to be split into multiple ioctls and also explore the + * possibility of extending the v4l2 api to include this + **/ +#define VPFE_CMD_S_CCDC_RAW_PARAMS _IOW('V', BASE_VIDIOC_PRIVATE + 1, \ + void *) +#endif /* _DAVINCI_VPFE_H */ diff --git a/include/media/ti-media/vpfe_types.h b/include/media/ti-media/vpfe_types.h new file mode 100644 index 00000000000..76fb74bad08 --- /dev/null +++ b/include/media/ti-media/vpfe_types.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPFE_TYPES_H +#define _VPFE_TYPES_H + +#ifdef __KERNEL__ + +enum vpfe_pin_pol { + VPFE_PINPOL_POSITIVE, + VPFE_PINPOL_NEGATIVE +}; + +enum vpfe_hw_if_type { + /* BT656 - 8 bit */ + VPFE_BT656, + /* BT1120 - 16 bit */ + VPFE_BT1120, + /* Raw Bayer */ + VPFE_RAW_BAYER, + /* YCbCr - 8 bit with external sync */ + VPFE_YCBCR_SYNC_8, + /* YCbCr - 16 bit with external sync */ + VPFE_YCBCR_SYNC_16, + /* BT656 - 10 bit */ + VPFE_BT656_10BIT +}; + +/* interface description */ +struct vpfe_hw_if_param { + enum vpfe_hw_if_type if_type; + enum vpfe_pin_pol hdpol; + enum vpfe_pin_pol vdpol; +}; + +#endif +#endif diff --git a/include/media/ti-media/vpss.h b/include/media/ti-media/vpss.h new file mode 100644 index 00000000000..fcdff745fae --- /dev/null +++ b/include/media/ti-media/vpss.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + * + * vpss - video processing subsystem module header file. + * + * Include this header file if a driver needs to configure vpss system + * module. It exports a set of library functions for video drivers to + * configure vpss system module functions such as clock enable/disable, + * vpss interrupt mux to arm, and other common vpss system module + * functions. + */ +#ifndef _VPSS_H +#define _VPSS_H + +/* selector for ccdc input selection on DM355 */ +enum vpss_ccdc_source_sel { + VPSS_CCDCIN, + VPSS_HSSIIN +}; + +/* Used for enable/diable VPSS Clock */ +enum vpss_clock_sel { + /* DM355/DM365 */ + VPSS_CCDC_CLOCK, + VPSS_IPIPE_CLOCK, + VPSS_H3A_CLOCK, + VPSS_CFALD_CLOCK, + /* + * When using VPSS_VENC_CLOCK_SEL in vpss_enable_clock() api + * following applies:- + * en = 0 selects ENC_CLK + * en = 1 selects ENC_CLK/2 + */ + VPSS_VENC_CLOCK_SEL, + VPSS_VPBE_CLOCK, +}; + +/* select input to ccdc on dm355 */ +int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel); +/* enable/disable a vpss clock, 0 - success, -1 - failure */ +int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en); + +/* wbl reset for dm644x */ +enum vpss_wbl_sel { + VPSS_PCR_AEW_WBL_0 = 16, + VPSS_PCR_AF_WBL_0, + VPSS_PCR_RSZ4_WBL_0, + VPSS_PCR_RSZ3_WBL_0, + VPSS_PCR_RSZ2_WBL_0, + VPSS_PCR_RSZ1_WBL_0, + VPSS_PCR_PREV_WBL_0, + VPSS_PCR_CCDC_WBL_O, +}; +int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel); +#endif diff --git a/include/media/tps61059.h b/include/media/tps61059.h new file mode 100644 index 00000000000..b35b9401d49 --- /dev/null +++ b/include/media/tps61059.h @@ -0,0 +1,35 @@ +/* + * tps61059.h - Register definitions for the TPS61059 Flash Chip. + * + * Copyright (C) 2009 Texas Instruments. + * + * Contributors: + * Pallavi Kulkarni + * Sergio Aguirre + * + * 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 TPS61059_H +#define TPS61059_H + +#include + +/** + * struct tps61059_platform_data - platform data values and access functions + * @power_set: Power state access function, zero is off, non-zero is on. + * @flash_on: Turn on the flash. + * @flash_off: Turn off the flash. + * @update_hw: Depending on the torch intensity, turn on/off torch. + * @priv_data_set: device private data (pointer) access function + */ +struct tps61059_platform_data { + void (*flash_on)(void); + void (*flash_off)(void); + void (*s_torch_intensity)(u32 value); + int (*priv_data_set)(void *); +}; + +#endif /* ifndef TPS61059_H */ diff --git a/include/media/tvp514x-int.h b/include/media/tvp514x-int.h new file mode 100644 index 00000000000..1e785d61e09 --- /dev/null +++ b/include/media/tvp514x-int.h @@ -0,0 +1,118 @@ +/* + * drivers/media/video/tvp514x.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _TVP514X_H +#define _TVP514X_H + +/* + * Other macros + */ +#define TVP514X_MODULE_NAME "tvp514x" + +#define TVP514X_XCLK_BT656 (27000000) + +/* Number of pixels and number of lines per frame for different standards */ +#define NTSC_NUM_ACTIVE_PIXELS (720) +#define NTSC_NUM_ACTIVE_LINES (480) +#define PAL_NUM_ACTIVE_PIXELS (720) +#define PAL_NUM_ACTIVE_LINES (576) + +/** + * enum tvp514x_input - enum for different decoder input pin + * configuration. + */ +enum tvp514x_input { + /* + * CVBS input selection + */ + INPUT_CVBS_VI1A = 0x0, + INPUT_CVBS_VI1B, + INPUT_CVBS_VI1C, + INPUT_CVBS_VI2A = 0x04, + INPUT_CVBS_VI2B, + INPUT_CVBS_VI2C, + INPUT_CVBS_VI3A = 0x08, + INPUT_CVBS_VI3B, + INPUT_CVBS_VI3C, + INPUT_CVBS_VI4A = 0x0C, + /* + * S-Video input selection + */ + INPUT_SVIDEO_VI2A_VI1A = 0x44, + INPUT_SVIDEO_VI2B_VI1B, + INPUT_SVIDEO_VI2C_VI1C, + INPUT_SVIDEO_VI2A_VI3A = 0x54, + INPUT_SVIDEO_VI2B_VI3B, + INPUT_SVIDEO_VI2C_VI3C, + INPUT_SVIDEO_VI4A_VI1A = 0x4C, + INPUT_SVIDEO_VI4A_VI1B, + INPUT_SVIDEO_VI4A_VI1C, + INPUT_SVIDEO_VI4A_VI3A = 0x5C, + INPUT_SVIDEO_VI4A_VI3B, + INPUT_SVIDEO_VI4A_VI3C, + + /* Need to add entries for + * RGB, YPbPr and SCART. + */ + INPUT_INVALID +}; + +/** + * enum tvp514x_output - enum for output format + * supported. + * + */ +enum tvp514x_output { + OUTPUT_10BIT_422_EMBEDDED_SYNC = 0, + OUTPUT_20BIT_422_SEPERATE_SYNC, + OUTPUT_10BIT_422_SEPERATE_SYNC = 3, + OUTPUT_INVALID +}; + +/** + * struct tvp514x_platform_data - Platform data values and access functions. + * @power_set: Power state access function, zero is off, non-zero is on. + * @ifparm: Interface parameters access function. + * @priv_data_set: Device private data (pointer) access function. + * @clk_polarity: Clock polarity of the current interface. + * @ hs_polarity: HSYNC Polarity configuration for current interface. + * @ vs_polarity: VSYNC Polarity configuration for current interface. + */ +struct tvp514x_platform_data { + char *master; + int (*ifparm)(struct v4l2_ifparm *p); + int (*power_set)(struct v4l2_int_device *s, enum v4l2_power power); + int (*priv_data_set)(struct v4l2_int_device *s, void *priv); + /* Interface control params */ + bool clk_polarity; + bool hs_polarity; + bool vs_polarity; +}; + + +#endif /* ifndef _TVP514X_H */ diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c new file mode 100644 index 00000000000..135901b2ea1 --- /dev/null +++ b/sound/soc/omap/am3517evm.c @@ -0,0 +1,202 @@ +/* + * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM + * + * Author: Anuj Aggarwal + * + * Based on sound/soc/omap/beagle.c by Steve Sakoman + * + * Copyright (C) 2009 Texas Instruments Incorporated + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-mcbsp.h" +#include "omap-pcm.h" + +#include "../codecs/tlv320aic23.h" + +#define CODEC_CLOCK 12000000 + +static int am3517evm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* Set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return ret; + } + + /* Set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return ret; + } + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + CODEC_CLOCK, SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n"); + return ret; + } + + snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops am3517evm_ops = { + .hw_params = am3517evm_hw_params, +}; + +/* am3517evm machine dapm widgets */ +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_HP("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_MIC("Mic In", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Line Out connected to LLOUT, RLOUT */ + {"Line Out", NULL, "LOUT"}, + {"Line Out", NULL, "ROUT"}, + + {"LLINEIN", NULL, "Line In"}, + {"RLINEIN", NULL, "Line In"}, + + {"MICIN", NULL, "Mic In"}, +}; + +static int am3517evm_aic23_init(struct snd_soc_codec *codec) +{ + /* Add am3517-evm specific widgets */ + snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + ARRAY_SIZE(tlv320aic23_dapm_widgets)); + + /* Set up davinci-evm specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + /* always connected */ + snd_soc_dapm_enable_pin(codec, "Line Out"); + snd_soc_dapm_enable_pin(codec, "Line In"); + snd_soc_dapm_enable_pin(codec, "Mic In"); + + snd_soc_dapm_sync(codec); + + return 0; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link am3517evm_dai = { + .name = "TLV320AIC23", + .stream_name = "AIC23", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &tlv320aic23_dai, + .init = am3517evm_aic23_init, + .ops = &am3517evm_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_am3517evm = { + .name = "am3517evm", + .platform = &omap_soc_platform, + .dai_link = &am3517evm_dai, + .num_links = 1, +}; + +/* Audio subsystem */ +static struct snd_soc_device am3517evm_snd_devdata = { + .card = &snd_soc_am3517evm, + .codec_dev = &soc_codec_dev_tlv320aic23, +}; + +static struct platform_device *am3517evm_snd_device; + +static int __init am3517evm_soc_init(void) +{ + int ret; + + if (!machine_is_omap3517evm()) { + pr_err("Not OMAP3517 / AM3517 EVM!\n"); + return -ENODEV; + } + pr_info("OMAP3517 / AM3517 EVM SoC init\n"); + + am3517evm_snd_device = platform_device_alloc("soc-audio", -1); + if (!am3517evm_snd_device) { + printk(KERN_ERR "Platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(am3517evm_snd_device, &am3517evm_snd_devdata); + am3517evm_snd_devdata.dev = &am3517evm_snd_device->dev; + *(unsigned int *)am3517evm_dai.cpu_dai->private_data = 0; /* McBSP1 */ + + ret = platform_device_add(am3517evm_snd_device); + if (ret) + goto err1; + + return 0; + +err1: + printk(KERN_ERR "Unable to add platform device\n"); + platform_device_put(am3517evm_snd_device); + + return ret; +} + +static void __exit am3517evm_soc_exit(void) +{ + platform_device_unregister(am3517evm_snd_device); +} + +module_init(am3517evm_soc_init); +module_exit(am3517evm_soc_exit); + +MODULE_AUTHOR("Anuj Aggarwal "); +MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c index 4fd9754d410..c5f1e683f9a 100644 --- a/sound/soc/omap/omap3beagle.c +++ b/sound/soc/omap/omap3beagle.c @@ -118,7 +118,7 @@ static int __init omap3beagle_soc_init(void) int ret; if (!machine_is_omap3_beagle() && !machine_is_ginger()) { - pr_debug("Not OMAP3 Beagle!\n"); + pr_debug("Not OMAP3 Beagle! Nor Devkit Nor Ginger\n"); return -ENODEV; } pr_info("OMAP3 Beagle SoC init\n"); -- 2.11.4.GIT