17b20f325SJacopo Mondi // SPDX-License-Identifier: GPL-2.0 2e0d76c38SJacopo Mondi /* 3e0d76c38SJacopo Mondi * tw9910 Video Driver 4e0d76c38SJacopo Mondi * 57b20f325SJacopo Mondi * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org> 67b20f325SJacopo Mondi * 7e0d76c38SJacopo Mondi * Copyright (C) 2008 Renesas Solutions Corp. 8e0d76c38SJacopo Mondi * Kuninori Morimoto <morimoto.kuninori@renesas.com> 9e0d76c38SJacopo Mondi * 10e0d76c38SJacopo Mondi * Based on ov772x driver, 11e0d76c38SJacopo Mondi * 12e0d76c38SJacopo Mondi * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com> 13e0d76c38SJacopo Mondi * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> 14e0d76c38SJacopo Mondi * Copyright (C) 2008 Magnus Damm 15e0d76c38SJacopo Mondi * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 16e0d76c38SJacopo Mondi */ 17e0d76c38SJacopo Mondi 187b20f325SJacopo Mondi #include <linux/clk.h> 197b20f325SJacopo Mondi #include <linux/gpio/consumer.h> 20e0d76c38SJacopo Mondi #include <linux/init.h> 21e0d76c38SJacopo Mondi #include <linux/module.h> 22e0d76c38SJacopo Mondi #include <linux/i2c.h> 23e0d76c38SJacopo Mondi #include <linux/slab.h> 24e0d76c38SJacopo Mondi #include <linux/kernel.h> 25e0d76c38SJacopo Mondi #include <linux/delay.h> 26e0d76c38SJacopo Mondi #include <linux/v4l2-mediabus.h> 27e0d76c38SJacopo Mondi #include <linux/videodev2.h> 28e0d76c38SJacopo Mondi 29e0d76c38SJacopo Mondi #include <media/i2c/tw9910.h> 30e0d76c38SJacopo Mondi #include <media/v4l2-subdev.h> 31e0d76c38SJacopo Mondi 32e0d76c38SJacopo Mondi #define GET_ID(val) ((val & 0xF8) >> 3) 33e0d76c38SJacopo Mondi #define GET_REV(val) (val & 0x07) 34e0d76c38SJacopo Mondi 35e0d76c38SJacopo Mondi /* 36e0d76c38SJacopo Mondi * register offset 37e0d76c38SJacopo Mondi */ 38e0d76c38SJacopo Mondi #define ID 0x00 /* Product ID Code Register */ 39e0d76c38SJacopo Mondi #define STATUS1 0x01 /* Chip Status Register I */ 40e0d76c38SJacopo Mondi #define INFORM 0x02 /* Input Format */ 41e0d76c38SJacopo Mondi #define OPFORM 0x03 /* Output Format Control Register */ 42e0d76c38SJacopo Mondi #define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */ 43e0d76c38SJacopo Mondi #define OUTCTR1 0x05 /* Output Control I */ 44e0d76c38SJacopo Mondi #define ACNTL1 0x06 /* Analog Control Register 1 */ 45e0d76c38SJacopo Mondi #define CROP_HI 0x07 /* Cropping Register, High */ 46e0d76c38SJacopo Mondi #define VDELAY_LO 0x08 /* Vertical Delay Register, Low */ 47e0d76c38SJacopo Mondi #define VACTIVE_LO 0x09 /* Vertical Active Register, Low */ 48e0d76c38SJacopo Mondi #define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */ 49e0d76c38SJacopo Mondi #define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */ 50e0d76c38SJacopo Mondi #define CNTRL1 0x0C /* Control Register I */ 51e0d76c38SJacopo Mondi #define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */ 52e0d76c38SJacopo Mondi #define SCALE_HI 0x0E /* Scaling Register, High */ 53e0d76c38SJacopo Mondi #define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */ 54e0d76c38SJacopo Mondi #define BRIGHT 0x10 /* BRIGHTNESS Control Register */ 55e0d76c38SJacopo Mondi #define CONTRAST 0x11 /* CONTRAST Control Register */ 56e0d76c38SJacopo Mondi #define SHARPNESS 0x12 /* SHARPNESS Control Register I */ 57e0d76c38SJacopo Mondi #define SAT_U 0x13 /* Chroma (U) Gain Register */ 58e0d76c38SJacopo Mondi #define SAT_V 0x14 /* Chroma (V) Gain Register */ 59e0d76c38SJacopo Mondi #define HUE 0x15 /* Hue Control Register */ 60e0d76c38SJacopo Mondi #define CORING1 0x17 61e0d76c38SJacopo Mondi #define CORING2 0x18 /* Coring and IF compensation */ 62e0d76c38SJacopo Mondi #define VBICNTL 0x19 /* VBI Control Register */ 63e0d76c38SJacopo Mondi #define ACNTL2 0x1A /* Analog Control 2 */ 64e0d76c38SJacopo Mondi #define OUTCTR2 0x1B /* Output Control 2 */ 65e0d76c38SJacopo Mondi #define SDT 0x1C /* Standard Selection */ 66e0d76c38SJacopo Mondi #define SDTR 0x1D /* Standard Recognition */ 67e0d76c38SJacopo Mondi #define TEST 0x1F /* Test Control Register */ 68e0d76c38SJacopo Mondi #define CLMPG 0x20 /* Clamping Gain */ 69e0d76c38SJacopo Mondi #define IAGC 0x21 /* Individual AGC Gain */ 70e0d76c38SJacopo Mondi #define AGCGAIN 0x22 /* AGC Gain */ 71e0d76c38SJacopo Mondi #define PEAKWT 0x23 /* White Peak Threshold */ 72e0d76c38SJacopo Mondi #define CLMPL 0x24 /* Clamp level */ 73e0d76c38SJacopo Mondi #define SYNCT 0x25 /* Sync Amplitude */ 74e0d76c38SJacopo Mondi #define MISSCNT 0x26 /* Sync Miss Count Register */ 75e0d76c38SJacopo Mondi #define PCLAMP 0x27 /* Clamp Position Register */ 76e0d76c38SJacopo Mondi #define VCNTL1 0x28 /* Vertical Control I */ 77e0d76c38SJacopo Mondi #define VCNTL2 0x29 /* Vertical Control II */ 78e0d76c38SJacopo Mondi #define CKILL 0x2A /* Color Killer Level Control */ 79e0d76c38SJacopo Mondi #define COMB 0x2B /* Comb Filter Control */ 80e0d76c38SJacopo Mondi #define LDLY 0x2C /* Luma Delay and H Filter Control */ 81e0d76c38SJacopo Mondi #define MISC1 0x2D /* Miscellaneous Control I */ 82e0d76c38SJacopo Mondi #define LOOP 0x2E /* LOOP Control Register */ 83e0d76c38SJacopo Mondi #define MISC2 0x2F /* Miscellaneous Control II */ 84e0d76c38SJacopo Mondi #define MVSN 0x30 /* Macrovision Detection */ 85e0d76c38SJacopo Mondi #define STATUS2 0x31 /* Chip STATUS II */ 86e0d76c38SJacopo Mondi #define HFREF 0x32 /* H monitor */ 87e0d76c38SJacopo Mondi #define CLMD 0x33 /* CLAMP MODE */ 88e0d76c38SJacopo Mondi #define IDCNTL 0x34 /* ID Detection Control */ 89e0d76c38SJacopo Mondi #define CLCNTL1 0x35 /* Clamp Control I */ 90e0d76c38SJacopo Mondi #define ANAPLLCTL 0x4C 91e0d76c38SJacopo Mondi #define VBIMIN 0x4D 92e0d76c38SJacopo Mondi #define HSLOWCTL 0x4E 93e0d76c38SJacopo Mondi #define WSS3 0x4F 94e0d76c38SJacopo Mondi #define FILLDATA 0x50 95e0d76c38SJacopo Mondi #define SDID 0x51 96e0d76c38SJacopo Mondi #define DID 0x52 97e0d76c38SJacopo Mondi #define WSS1 0x53 98e0d76c38SJacopo Mondi #define WSS2 0x54 99e0d76c38SJacopo Mondi #define VVBI 0x55 100e0d76c38SJacopo Mondi #define LCTL6 0x56 101e0d76c38SJacopo Mondi #define LCTL7 0x57 102e0d76c38SJacopo Mondi #define LCTL8 0x58 103e0d76c38SJacopo Mondi #define LCTL9 0x59 104e0d76c38SJacopo Mondi #define LCTL10 0x5A 105e0d76c38SJacopo Mondi #define LCTL11 0x5B 106e0d76c38SJacopo Mondi #define LCTL12 0x5C 107e0d76c38SJacopo Mondi #define LCTL13 0x5D 108e0d76c38SJacopo Mondi #define LCTL14 0x5E 109e0d76c38SJacopo Mondi #define LCTL15 0x5F 110e0d76c38SJacopo Mondi #define LCTL16 0x60 111e0d76c38SJacopo Mondi #define LCTL17 0x61 112e0d76c38SJacopo Mondi #define LCTL18 0x62 113e0d76c38SJacopo Mondi #define LCTL19 0x63 114e0d76c38SJacopo Mondi #define LCTL20 0x64 115e0d76c38SJacopo Mondi #define LCTL21 0x65 116e0d76c38SJacopo Mondi #define LCTL22 0x66 117e0d76c38SJacopo Mondi #define LCTL23 0x67 118e0d76c38SJacopo Mondi #define LCTL24 0x68 119e0d76c38SJacopo Mondi #define LCTL25 0x69 120e0d76c38SJacopo Mondi #define LCTL26 0x6A 121e0d76c38SJacopo Mondi #define HSBEGIN 0x6B 122e0d76c38SJacopo Mondi #define HSEND 0x6C 123e0d76c38SJacopo Mondi #define OVSDLY 0x6D 124e0d76c38SJacopo Mondi #define OVSEND 0x6E 125e0d76c38SJacopo Mondi #define VBIDELAY 0x6F 126e0d76c38SJacopo Mondi 127e0d76c38SJacopo Mondi /* 128e0d76c38SJacopo Mondi * register detail 129e0d76c38SJacopo Mondi */ 130e0d76c38SJacopo Mondi 131e0d76c38SJacopo Mondi /* INFORM */ 132e0d76c38SJacopo Mondi #define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */ 133e0d76c38SJacopo Mondi #define FC27_FF 0x00 /* 0 : Square pixel mode. */ 134e0d76c38SJacopo Mondi /* Must use 24.54MHz for 60Hz field rate */ 135e0d76c38SJacopo Mondi /* source or 29.5MHz for 50Hz field rate */ 136e0d76c38SJacopo Mondi #define IFSEL_S 0x10 /* 01 : S-video decoding */ 137e0d76c38SJacopo Mondi #define IFSEL_C 0x00 /* 00 : Composite video decoding */ 138e0d76c38SJacopo Mondi /* Y input video selection */ 139e0d76c38SJacopo Mondi #define YSEL_M0 0x00 /* 00 : Mux0 selected */ 140e0d76c38SJacopo Mondi #define YSEL_M1 0x04 /* 01 : Mux1 selected */ 141e0d76c38SJacopo Mondi #define YSEL_M2 0x08 /* 10 : Mux2 selected */ 142e0d76c38SJacopo Mondi #define YSEL_M3 0x10 /* 11 : Mux3 selected */ 143e0d76c38SJacopo Mondi 144e0d76c38SJacopo Mondi /* OPFORM */ 145e0d76c38SJacopo Mondi #define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */ 146e0d76c38SJacopo Mondi /* 1 : ITU-R-656 compatible data sequence format */ 147e0d76c38SJacopo Mondi #define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */ 148e0d76c38SJacopo Mondi /* 1 : 16-bit YCrCb 4:2:2 output format.*/ 149e0d76c38SJacopo Mondi #define LLCMODE 0x20 /* 1 : LLC output mode. */ 150e0d76c38SJacopo Mondi /* 0 : free-run output mode */ 151e0d76c38SJacopo Mondi #define AINC 0x10 /* Serial interface auto-indexing control */ 152e0d76c38SJacopo Mondi /* 0 : auto-increment */ 153e0d76c38SJacopo Mondi /* 1 : non-auto */ 154e0d76c38SJacopo Mondi #define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */ 155e0d76c38SJacopo Mondi /* 0 : Vertical out ctrl by HACTIVE and DVALID */ 156e0d76c38SJacopo Mondi #define OEN_TRI_SEL_MASK 0x07 157e0d76c38SJacopo Mondi #define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */ 158e0d76c38SJacopo Mondi #define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */ 159e0d76c38SJacopo Mondi #define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */ 160e0d76c38SJacopo Mondi 161e0d76c38SJacopo Mondi /* OUTCTR1 */ 162e0d76c38SJacopo Mondi #define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */ 163e0d76c38SJacopo Mondi #define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */ 164e0d76c38SJacopo Mondi /* VS pin output control */ 165e0d76c38SJacopo Mondi #define VSSL_VSYNC 0x00 /* 0 : VSYNC */ 166e0d76c38SJacopo Mondi #define VSSL_VACT 0x10 /* 1 : VACT */ 167e0d76c38SJacopo Mondi #define VSSL_FIELD 0x20 /* 2 : FIELD */ 168e0d76c38SJacopo Mondi #define VSSL_VVALID 0x30 /* 3 : VVALID */ 169e0d76c38SJacopo Mondi #define VSSL_ZERO 0x70 /* 7 : 0 */ 170e0d76c38SJacopo Mondi #define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */ 171e0d76c38SJacopo Mondi #define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/ 172e0d76c38SJacopo Mondi /* HS pin output control */ 173e0d76c38SJacopo Mondi #define HSSL_HACT 0x00 /* 0 : HACT */ 174e0d76c38SJacopo Mondi #define HSSL_HSYNC 0x01 /* 1 : HSYNC */ 175e0d76c38SJacopo Mondi #define HSSL_DVALID 0x02 /* 2 : DVALID */ 176e0d76c38SJacopo Mondi #define HSSL_HLOCK 0x03 /* 3 : HLOCK */ 177e0d76c38SJacopo Mondi #define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */ 178e0d76c38SJacopo Mondi #define HSSL_ZERO 0x07 /* 7 : 0 */ 179e0d76c38SJacopo Mondi 180e0d76c38SJacopo Mondi /* ACNTL1 */ 181e0d76c38SJacopo Mondi #define SRESET 0x80 /* resets the device to its default state 182e0d76c38SJacopo Mondi * but all register content remain unchanged. 183e0d76c38SJacopo Mondi * This bit is self-resetting. 184e0d76c38SJacopo Mondi */ 185e0d76c38SJacopo Mondi #define ACNTL1_PDN_MASK 0x0e 186e0d76c38SJacopo Mondi #define CLK_PDN 0x08 /* system clock power down */ 187e0d76c38SJacopo Mondi #define Y_PDN 0x04 /* Luma ADC power down */ 188e0d76c38SJacopo Mondi #define C_PDN 0x02 /* Chroma ADC power down */ 189e0d76c38SJacopo Mondi 190e0d76c38SJacopo Mondi /* ACNTL2 */ 191e0d76c38SJacopo Mondi #define ACNTL2_PDN_MASK 0x40 192e0d76c38SJacopo Mondi #define PLL_PDN 0x40 /* PLL power down */ 193e0d76c38SJacopo Mondi 194e0d76c38SJacopo Mondi /* VBICNTL */ 195e0d76c38SJacopo Mondi 196e0d76c38SJacopo Mondi /* RTSEL : control the real time signal output from the MPOUT pin */ 197e0d76c38SJacopo Mondi #define RTSEL_MASK 0x07 198e0d76c38SJacopo Mondi #define RTSEL_VLOSS 0x00 /* 0000 = Video loss */ 199e0d76c38SJacopo Mondi #define RTSEL_HLOCK 0x01 /* 0001 = H-lock */ 200e0d76c38SJacopo Mondi #define RTSEL_SLOCK 0x02 /* 0010 = S-lock */ 201e0d76c38SJacopo Mondi #define RTSEL_VLOCK 0x03 /* 0011 = V-lock */ 202e0d76c38SJacopo Mondi #define RTSEL_MONO 0x04 /* 0100 = MONO */ 203e0d76c38SJacopo Mondi #define RTSEL_DET50 0x05 /* 0101 = DET50 */ 204e0d76c38SJacopo Mondi #define RTSEL_FIELD 0x06 /* 0110 = FIELD */ 205e0d76c38SJacopo Mondi #define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ 206e0d76c38SJacopo Mondi 207e0d76c38SJacopo Mondi /* HSYNC start and end are constant for now */ 208e0d76c38SJacopo Mondi #define HSYNC_START 0x0260 209e0d76c38SJacopo Mondi #define HSYNC_END 0x0300 210e0d76c38SJacopo Mondi 211e0d76c38SJacopo Mondi /* 212e0d76c38SJacopo Mondi * structure 213e0d76c38SJacopo Mondi */ 214e0d76c38SJacopo Mondi 215e0d76c38SJacopo Mondi struct regval_list { 216e0d76c38SJacopo Mondi unsigned char reg_num; 217e0d76c38SJacopo Mondi unsigned char value; 218e0d76c38SJacopo Mondi }; 219e0d76c38SJacopo Mondi 220e0d76c38SJacopo Mondi struct tw9910_scale_ctrl { 221e0d76c38SJacopo Mondi char *name; 222e0d76c38SJacopo Mondi unsigned short width; 223e0d76c38SJacopo Mondi unsigned short height; 224e0d76c38SJacopo Mondi u16 hscale; 225e0d76c38SJacopo Mondi u16 vscale; 226e0d76c38SJacopo Mondi }; 227e0d76c38SJacopo Mondi 228e0d76c38SJacopo Mondi struct tw9910_priv { 229e0d76c38SJacopo Mondi struct v4l2_subdev subdev; 2307b20f325SJacopo Mondi struct clk *clk; 231e0d76c38SJacopo Mondi struct tw9910_video_info *info; 2327b20f325SJacopo Mondi struct gpio_desc *pdn_gpio; 2337b20f325SJacopo Mondi struct gpio_desc *rstb_gpio; 234e0d76c38SJacopo Mondi const struct tw9910_scale_ctrl *scale; 235e0d76c38SJacopo Mondi v4l2_std_id norm; 236e0d76c38SJacopo Mondi u32 revision; 237e0d76c38SJacopo Mondi }; 238e0d76c38SJacopo Mondi 239e0d76c38SJacopo Mondi static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { 240e0d76c38SJacopo Mondi { 241e0d76c38SJacopo Mondi .name = "NTSC SQ", 242e0d76c38SJacopo Mondi .width = 640, 243e0d76c38SJacopo Mondi .height = 480, 244e0d76c38SJacopo Mondi .hscale = 0x0100, 245e0d76c38SJacopo Mondi .vscale = 0x0100, 246e0d76c38SJacopo Mondi }, 247e0d76c38SJacopo Mondi { 248e0d76c38SJacopo Mondi .name = "NTSC CCIR601", 249e0d76c38SJacopo Mondi .width = 720, 250e0d76c38SJacopo Mondi .height = 480, 251e0d76c38SJacopo Mondi .hscale = 0x0100, 252e0d76c38SJacopo Mondi .vscale = 0x0100, 253e0d76c38SJacopo Mondi }, 254e0d76c38SJacopo Mondi { 255e0d76c38SJacopo Mondi .name = "NTSC SQ (CIF)", 256e0d76c38SJacopo Mondi .width = 320, 257e0d76c38SJacopo Mondi .height = 240, 258e0d76c38SJacopo Mondi .hscale = 0x0200, 259e0d76c38SJacopo Mondi .vscale = 0x0200, 260e0d76c38SJacopo Mondi }, 261e0d76c38SJacopo Mondi { 262e0d76c38SJacopo Mondi .name = "NTSC CCIR601 (CIF)", 263e0d76c38SJacopo Mondi .width = 360, 264e0d76c38SJacopo Mondi .height = 240, 265e0d76c38SJacopo Mondi .hscale = 0x0200, 266e0d76c38SJacopo Mondi .vscale = 0x0200, 267e0d76c38SJacopo Mondi }, 268e0d76c38SJacopo Mondi { 269e0d76c38SJacopo Mondi .name = "NTSC SQ (QCIF)", 270e0d76c38SJacopo Mondi .width = 160, 271e0d76c38SJacopo Mondi .height = 120, 272e0d76c38SJacopo Mondi .hscale = 0x0400, 273e0d76c38SJacopo Mondi .vscale = 0x0400, 274e0d76c38SJacopo Mondi }, 275e0d76c38SJacopo Mondi { 276e0d76c38SJacopo Mondi .name = "NTSC CCIR601 (QCIF)", 277e0d76c38SJacopo Mondi .width = 180, 278e0d76c38SJacopo Mondi .height = 120, 279e0d76c38SJacopo Mondi .hscale = 0x0400, 280e0d76c38SJacopo Mondi .vscale = 0x0400, 281e0d76c38SJacopo Mondi }, 282e0d76c38SJacopo Mondi }; 283e0d76c38SJacopo Mondi 284e0d76c38SJacopo Mondi static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { 285e0d76c38SJacopo Mondi { 286e0d76c38SJacopo Mondi .name = "PAL SQ", 287e0d76c38SJacopo Mondi .width = 768, 288e0d76c38SJacopo Mondi .height = 576, 289e0d76c38SJacopo Mondi .hscale = 0x0100, 290e0d76c38SJacopo Mondi .vscale = 0x0100, 291e0d76c38SJacopo Mondi }, 292e0d76c38SJacopo Mondi { 293e0d76c38SJacopo Mondi .name = "PAL CCIR601", 294e0d76c38SJacopo Mondi .width = 720, 295e0d76c38SJacopo Mondi .height = 576, 296e0d76c38SJacopo Mondi .hscale = 0x0100, 297e0d76c38SJacopo Mondi .vscale = 0x0100, 298e0d76c38SJacopo Mondi }, 299e0d76c38SJacopo Mondi { 300e0d76c38SJacopo Mondi .name = "PAL SQ (CIF)", 301e0d76c38SJacopo Mondi .width = 384, 302e0d76c38SJacopo Mondi .height = 288, 303e0d76c38SJacopo Mondi .hscale = 0x0200, 304e0d76c38SJacopo Mondi .vscale = 0x0200, 305e0d76c38SJacopo Mondi }, 306e0d76c38SJacopo Mondi { 307e0d76c38SJacopo Mondi .name = "PAL CCIR601 (CIF)", 308e0d76c38SJacopo Mondi .width = 360, 309e0d76c38SJacopo Mondi .height = 288, 310e0d76c38SJacopo Mondi .hscale = 0x0200, 311e0d76c38SJacopo Mondi .vscale = 0x0200, 312e0d76c38SJacopo Mondi }, 313e0d76c38SJacopo Mondi { 314e0d76c38SJacopo Mondi .name = "PAL SQ (QCIF)", 315e0d76c38SJacopo Mondi .width = 192, 316e0d76c38SJacopo Mondi .height = 144, 317e0d76c38SJacopo Mondi .hscale = 0x0400, 318e0d76c38SJacopo Mondi .vscale = 0x0400, 319e0d76c38SJacopo Mondi }, 320e0d76c38SJacopo Mondi { 321e0d76c38SJacopo Mondi .name = "PAL CCIR601 (QCIF)", 322e0d76c38SJacopo Mondi .width = 180, 323e0d76c38SJacopo Mondi .height = 144, 324e0d76c38SJacopo Mondi .hscale = 0x0400, 325e0d76c38SJacopo Mondi .vscale = 0x0400, 326e0d76c38SJacopo Mondi }, 327e0d76c38SJacopo Mondi }; 328e0d76c38SJacopo Mondi 329e0d76c38SJacopo Mondi /* 330e0d76c38SJacopo Mondi * general function 331e0d76c38SJacopo Mondi */ 332e0d76c38SJacopo Mondi static struct tw9910_priv *to_tw9910(const struct i2c_client *client) 333e0d76c38SJacopo Mondi { 334e0d76c38SJacopo Mondi return container_of(i2c_get_clientdata(client), struct tw9910_priv, 335e0d76c38SJacopo Mondi subdev); 336e0d76c38SJacopo Mondi } 337e0d76c38SJacopo Mondi 338e0d76c38SJacopo Mondi static int tw9910_mask_set(struct i2c_client *client, u8 command, 339e0d76c38SJacopo Mondi u8 mask, u8 set) 340e0d76c38SJacopo Mondi { 341e0d76c38SJacopo Mondi s32 val = i2c_smbus_read_byte_data(client, command); 342e0d76c38SJacopo Mondi if (val < 0) 343e0d76c38SJacopo Mondi return val; 344e0d76c38SJacopo Mondi 345e0d76c38SJacopo Mondi val &= ~mask; 346e0d76c38SJacopo Mondi val |= set & mask; 347e0d76c38SJacopo Mondi 348e0d76c38SJacopo Mondi return i2c_smbus_write_byte_data(client, command, val); 349e0d76c38SJacopo Mondi } 350e0d76c38SJacopo Mondi 351e0d76c38SJacopo Mondi static int tw9910_set_scale(struct i2c_client *client, 352e0d76c38SJacopo Mondi const struct tw9910_scale_ctrl *scale) 353e0d76c38SJacopo Mondi { 354e0d76c38SJacopo Mondi int ret; 355e0d76c38SJacopo Mondi 356e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, SCALE_HI, 357e0d76c38SJacopo Mondi (scale->vscale & 0x0F00) >> 4 | 358e0d76c38SJacopo Mondi (scale->hscale & 0x0F00) >> 8); 359e0d76c38SJacopo Mondi if (ret < 0) 360e0d76c38SJacopo Mondi return ret; 361e0d76c38SJacopo Mondi 362e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, HSCALE_LO, 363e0d76c38SJacopo Mondi scale->hscale & 0x00FF); 364e0d76c38SJacopo Mondi if (ret < 0) 365e0d76c38SJacopo Mondi return ret; 366e0d76c38SJacopo Mondi 367e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, VSCALE_LO, 368e0d76c38SJacopo Mondi scale->vscale & 0x00FF); 369e0d76c38SJacopo Mondi 370e0d76c38SJacopo Mondi return ret; 371e0d76c38SJacopo Mondi } 372e0d76c38SJacopo Mondi 373e0d76c38SJacopo Mondi static int tw9910_set_hsync(struct i2c_client *client) 374e0d76c38SJacopo Mondi { 375e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 376e0d76c38SJacopo Mondi int ret; 377e0d76c38SJacopo Mondi 378e0d76c38SJacopo Mondi /* bit 10 - 3 */ 379e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, HSBEGIN, 380e0d76c38SJacopo Mondi (HSYNC_START & 0x07F8) >> 3); 381e0d76c38SJacopo Mondi if (ret < 0) 382e0d76c38SJacopo Mondi return ret; 383e0d76c38SJacopo Mondi 384e0d76c38SJacopo Mondi /* bit 10 - 3 */ 385e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, HSEND, 386e0d76c38SJacopo Mondi (HSYNC_END & 0x07F8) >> 3); 387e0d76c38SJacopo Mondi if (ret < 0) 388e0d76c38SJacopo Mondi return ret; 389e0d76c38SJacopo Mondi 390e0d76c38SJacopo Mondi /* So far only revisions 0 and 1 have been seen */ 391e0d76c38SJacopo Mondi /* bit 2 - 0 */ 392e0d76c38SJacopo Mondi if (1 == priv->revision) 393e0d76c38SJacopo Mondi ret = tw9910_mask_set(client, HSLOWCTL, 0x77, 394e0d76c38SJacopo Mondi (HSYNC_START & 0x0007) << 4 | 395e0d76c38SJacopo Mondi (HSYNC_END & 0x0007)); 396e0d76c38SJacopo Mondi 397e0d76c38SJacopo Mondi return ret; 398e0d76c38SJacopo Mondi } 399e0d76c38SJacopo Mondi 400e0d76c38SJacopo Mondi static void tw9910_reset(struct i2c_client *client) 401e0d76c38SJacopo Mondi { 402e0d76c38SJacopo Mondi tw9910_mask_set(client, ACNTL1, SRESET, SRESET); 403e0d76c38SJacopo Mondi msleep(1); 404e0d76c38SJacopo Mondi } 405e0d76c38SJacopo Mondi 406e0d76c38SJacopo Mondi static int tw9910_power(struct i2c_client *client, int enable) 407e0d76c38SJacopo Mondi { 408e0d76c38SJacopo Mondi int ret; 409e0d76c38SJacopo Mondi u8 acntl1; 410e0d76c38SJacopo Mondi u8 acntl2; 411e0d76c38SJacopo Mondi 412e0d76c38SJacopo Mondi if (enable) { 413e0d76c38SJacopo Mondi acntl1 = 0; 414e0d76c38SJacopo Mondi acntl2 = 0; 415e0d76c38SJacopo Mondi } else { 416e0d76c38SJacopo Mondi acntl1 = CLK_PDN | Y_PDN | C_PDN; 417e0d76c38SJacopo Mondi acntl2 = PLL_PDN; 418e0d76c38SJacopo Mondi } 419e0d76c38SJacopo Mondi 420e0d76c38SJacopo Mondi ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1); 421e0d76c38SJacopo Mondi if (ret < 0) 422e0d76c38SJacopo Mondi return ret; 423e0d76c38SJacopo Mondi 424e0d76c38SJacopo Mondi return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2); 425e0d76c38SJacopo Mondi } 426e0d76c38SJacopo Mondi 427e0d76c38SJacopo Mondi static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm, 428e0d76c38SJacopo Mondi u32 width, u32 height) 429e0d76c38SJacopo Mondi { 430e0d76c38SJacopo Mondi const struct tw9910_scale_ctrl *scale; 431e0d76c38SJacopo Mondi const struct tw9910_scale_ctrl *ret = NULL; 432e0d76c38SJacopo Mondi __u32 diff = 0xffffffff, tmp; 433e0d76c38SJacopo Mondi int size, i; 434e0d76c38SJacopo Mondi 435e0d76c38SJacopo Mondi if (norm & V4L2_STD_NTSC) { 436e0d76c38SJacopo Mondi scale = tw9910_ntsc_scales; 437e0d76c38SJacopo Mondi size = ARRAY_SIZE(tw9910_ntsc_scales); 438e0d76c38SJacopo Mondi } else if (norm & V4L2_STD_PAL) { 439e0d76c38SJacopo Mondi scale = tw9910_pal_scales; 440e0d76c38SJacopo Mondi size = ARRAY_SIZE(tw9910_pal_scales); 441e0d76c38SJacopo Mondi } else { 442e0d76c38SJacopo Mondi return NULL; 443e0d76c38SJacopo Mondi } 444e0d76c38SJacopo Mondi 445e0d76c38SJacopo Mondi for (i = 0; i < size; i++) { 446e0d76c38SJacopo Mondi tmp = abs(width - scale[i].width) + 447e0d76c38SJacopo Mondi abs(height - scale[i].height); 448e0d76c38SJacopo Mondi if (tmp < diff) { 449e0d76c38SJacopo Mondi diff = tmp; 450e0d76c38SJacopo Mondi ret = scale + i; 451e0d76c38SJacopo Mondi } 452e0d76c38SJacopo Mondi } 453e0d76c38SJacopo Mondi 454e0d76c38SJacopo Mondi return ret; 455e0d76c38SJacopo Mondi } 456e0d76c38SJacopo Mondi 457e0d76c38SJacopo Mondi /* 458e0d76c38SJacopo Mondi * subdevice operations 459e0d76c38SJacopo Mondi */ 460e0d76c38SJacopo Mondi static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) 461e0d76c38SJacopo Mondi { 462e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 463e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 464e0d76c38SJacopo Mondi u8 val; 465e0d76c38SJacopo Mondi int ret; 466e0d76c38SJacopo Mondi 467e0d76c38SJacopo Mondi if (!enable) { 468e0d76c38SJacopo Mondi switch (priv->revision) { 469e0d76c38SJacopo Mondi case 0: 470e0d76c38SJacopo Mondi val = OEN_TRI_SEL_ALL_OFF_r0; 471e0d76c38SJacopo Mondi break; 472e0d76c38SJacopo Mondi case 1: 473e0d76c38SJacopo Mondi val = OEN_TRI_SEL_ALL_OFF_r1; 474e0d76c38SJacopo Mondi break; 475e0d76c38SJacopo Mondi default: 476e0d76c38SJacopo Mondi dev_err(&client->dev, "un-supported revision\n"); 477e0d76c38SJacopo Mondi return -EINVAL; 478e0d76c38SJacopo Mondi } 479e0d76c38SJacopo Mondi } else { 480e0d76c38SJacopo Mondi val = OEN_TRI_SEL_ALL_ON; 481e0d76c38SJacopo Mondi 482e0d76c38SJacopo Mondi if (!priv->scale) { 483e0d76c38SJacopo Mondi dev_err(&client->dev, "norm select error\n"); 484e0d76c38SJacopo Mondi return -EPERM; 485e0d76c38SJacopo Mondi } 486e0d76c38SJacopo Mondi 487e0d76c38SJacopo Mondi dev_dbg(&client->dev, "%s %dx%d\n", 488e0d76c38SJacopo Mondi priv->scale->name, 489e0d76c38SJacopo Mondi priv->scale->width, 490e0d76c38SJacopo Mondi priv->scale->height); 491e0d76c38SJacopo Mondi } 492e0d76c38SJacopo Mondi 493e0d76c38SJacopo Mondi ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val); 494e0d76c38SJacopo Mondi if (ret < 0) 495e0d76c38SJacopo Mondi return ret; 496e0d76c38SJacopo Mondi 497e0d76c38SJacopo Mondi return tw9910_power(client, enable); 498e0d76c38SJacopo Mondi } 499e0d76c38SJacopo Mondi 500e0d76c38SJacopo Mondi static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) 501e0d76c38SJacopo Mondi { 502e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 503e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 504e0d76c38SJacopo Mondi 505e0d76c38SJacopo Mondi *norm = priv->norm; 506e0d76c38SJacopo Mondi 507e0d76c38SJacopo Mondi return 0; 508e0d76c38SJacopo Mondi } 509e0d76c38SJacopo Mondi 510e0d76c38SJacopo Mondi static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) 511e0d76c38SJacopo Mondi { 512e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 513e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 514e0d76c38SJacopo Mondi const unsigned hact = 720; 515e0d76c38SJacopo Mondi const unsigned hdelay = 15; 516e0d76c38SJacopo Mondi unsigned vact; 517e0d76c38SJacopo Mondi unsigned vdelay; 518e0d76c38SJacopo Mondi int ret; 519e0d76c38SJacopo Mondi 520e0d76c38SJacopo Mondi if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) 521e0d76c38SJacopo Mondi return -EINVAL; 522e0d76c38SJacopo Mondi 523e0d76c38SJacopo Mondi priv->norm = norm; 524e0d76c38SJacopo Mondi if (norm & V4L2_STD_525_60) { 525e0d76c38SJacopo Mondi vact = 240; 526e0d76c38SJacopo Mondi vdelay = 18; 527e0d76c38SJacopo Mondi ret = tw9910_mask_set(client, VVBI, 0x10, 0x10); 528e0d76c38SJacopo Mondi } else { 529e0d76c38SJacopo Mondi vact = 288; 530e0d76c38SJacopo Mondi vdelay = 24; 531e0d76c38SJacopo Mondi ret = tw9910_mask_set(client, VVBI, 0x10, 0x00); 532e0d76c38SJacopo Mondi } 533e0d76c38SJacopo Mondi if (!ret) 534e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, CROP_HI, 535e0d76c38SJacopo Mondi ((vdelay >> 2) & 0xc0) | 536e0d76c38SJacopo Mondi ((vact >> 4) & 0x30) | 537e0d76c38SJacopo Mondi ((hdelay >> 6) & 0x0c) | 538e0d76c38SJacopo Mondi ((hact >> 8) & 0x03)); 539e0d76c38SJacopo Mondi if (!ret) 540e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, VDELAY_LO, 541e0d76c38SJacopo Mondi vdelay & 0xff); 542e0d76c38SJacopo Mondi if (!ret) 543e0d76c38SJacopo Mondi ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, 544e0d76c38SJacopo Mondi vact & 0xff); 545e0d76c38SJacopo Mondi 546e0d76c38SJacopo Mondi return ret; 547e0d76c38SJacopo Mondi } 548e0d76c38SJacopo Mondi 549e0d76c38SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG 550e0d76c38SJacopo Mondi static int tw9910_g_register(struct v4l2_subdev *sd, 551e0d76c38SJacopo Mondi struct v4l2_dbg_register *reg) 552e0d76c38SJacopo Mondi { 553e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 554e0d76c38SJacopo Mondi int ret; 555e0d76c38SJacopo Mondi 556e0d76c38SJacopo Mondi if (reg->reg > 0xff) 557e0d76c38SJacopo Mondi return -EINVAL; 558e0d76c38SJacopo Mondi 559e0d76c38SJacopo Mondi reg->size = 1; 560e0d76c38SJacopo Mondi ret = i2c_smbus_read_byte_data(client, reg->reg); 561e0d76c38SJacopo Mondi if (ret < 0) 562e0d76c38SJacopo Mondi return ret; 563e0d76c38SJacopo Mondi 564e0d76c38SJacopo Mondi /* 565e0d76c38SJacopo Mondi * ret = int 566e0d76c38SJacopo Mondi * reg->val = __u64 567e0d76c38SJacopo Mondi */ 568e0d76c38SJacopo Mondi reg->val = (__u64)ret; 569e0d76c38SJacopo Mondi 570e0d76c38SJacopo Mondi return 0; 571e0d76c38SJacopo Mondi } 572e0d76c38SJacopo Mondi 573e0d76c38SJacopo Mondi static int tw9910_s_register(struct v4l2_subdev *sd, 574e0d76c38SJacopo Mondi const struct v4l2_dbg_register *reg) 575e0d76c38SJacopo Mondi { 576e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 577e0d76c38SJacopo Mondi 578e0d76c38SJacopo Mondi if (reg->reg > 0xff || 579e0d76c38SJacopo Mondi reg->val > 0xff) 580e0d76c38SJacopo Mondi return -EINVAL; 581e0d76c38SJacopo Mondi 582e0d76c38SJacopo Mondi return i2c_smbus_write_byte_data(client, reg->reg, reg->val); 583e0d76c38SJacopo Mondi } 584e0d76c38SJacopo Mondi #endif 585e0d76c38SJacopo Mondi 5867b20f325SJacopo Mondi static int tw9910_power_on(struct tw9910_priv *priv) 5877b20f325SJacopo Mondi { 5887b20f325SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 5897b20f325SJacopo Mondi int ret; 5907b20f325SJacopo Mondi 5917b20f325SJacopo Mondi if (priv->clk) { 5927b20f325SJacopo Mondi ret = clk_prepare_enable(priv->clk); 5937b20f325SJacopo Mondi if (ret) 5947b20f325SJacopo Mondi return ret; 5957b20f325SJacopo Mondi } 5967b20f325SJacopo Mondi 5977b20f325SJacopo Mondi if (priv->pdn_gpio) { 5987b20f325SJacopo Mondi gpiod_set_value(priv->pdn_gpio, 0); 5997b20f325SJacopo Mondi usleep_range(500, 1000); 6007b20f325SJacopo Mondi } 6017b20f325SJacopo Mondi 6027b20f325SJacopo Mondi /* 6037b20f325SJacopo Mondi * FIXME: The reset signal is connected to a shared GPIO on some 6047b20f325SJacopo Mondi * platforms (namely the SuperH Migo-R). Until a framework becomes 6057b20f325SJacopo Mondi * available to handle this cleanly, request the GPIO temporarily 6067b20f325SJacopo Mondi * to avoid conflicts. 6077b20f325SJacopo Mondi */ 6087b20f325SJacopo Mondi priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb", 6097b20f325SJacopo Mondi GPIOD_OUT_LOW); 6107b20f325SJacopo Mondi if (IS_ERR(priv->rstb_gpio)) { 6117b20f325SJacopo Mondi dev_info(&client->dev, "Unable to get GPIO \"rstb\""); 6127b20f325SJacopo Mondi return PTR_ERR(priv->rstb_gpio); 6137b20f325SJacopo Mondi } 6147b20f325SJacopo Mondi 6157b20f325SJacopo Mondi if (priv->rstb_gpio) { 6167b20f325SJacopo Mondi gpiod_set_value(priv->rstb_gpio, 1); 6177b20f325SJacopo Mondi usleep_range(500, 1000); 6187b20f325SJacopo Mondi gpiod_set_value(priv->rstb_gpio, 0); 6197b20f325SJacopo Mondi usleep_range(500, 1000); 6207b20f325SJacopo Mondi 6217b20f325SJacopo Mondi gpiod_put(priv->rstb_gpio); 6227b20f325SJacopo Mondi } 6237b20f325SJacopo Mondi 6247b20f325SJacopo Mondi return 0; 6257b20f325SJacopo Mondi } 6267b20f325SJacopo Mondi 6277b20f325SJacopo Mondi static int tw9910_power_off(struct tw9910_priv *priv) 6287b20f325SJacopo Mondi { 6297b20f325SJacopo Mondi clk_disable_unprepare(priv->clk); 6307b20f325SJacopo Mondi 6317b20f325SJacopo Mondi if (priv->pdn_gpio) { 6327b20f325SJacopo Mondi gpiod_set_value(priv->pdn_gpio, 1); 6337b20f325SJacopo Mondi usleep_range(500, 1000); 6347b20f325SJacopo Mondi } 6357b20f325SJacopo Mondi 6367b20f325SJacopo Mondi return 0; 6377b20f325SJacopo Mondi } 6387b20f325SJacopo Mondi 639e0d76c38SJacopo Mondi static int tw9910_s_power(struct v4l2_subdev *sd, int on) 640e0d76c38SJacopo Mondi { 641e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 642e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 643e0d76c38SJacopo Mondi 6447b20f325SJacopo Mondi return on ? tw9910_power_on(priv) : 6457b20f325SJacopo Mondi tw9910_power_off(priv); 646e0d76c38SJacopo Mondi } 647e0d76c38SJacopo Mondi 648e0d76c38SJacopo Mondi static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) 649e0d76c38SJacopo Mondi { 650e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 651e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 652e0d76c38SJacopo Mondi int ret = -EINVAL; 653e0d76c38SJacopo Mondi u8 val; 654e0d76c38SJacopo Mondi 655e0d76c38SJacopo Mondi /* 656e0d76c38SJacopo Mondi * select suitable norm 657e0d76c38SJacopo Mondi */ 658e0d76c38SJacopo Mondi priv->scale = tw9910_select_norm(priv->norm, *width, *height); 659e0d76c38SJacopo Mondi if (!priv->scale) 660e0d76c38SJacopo Mondi goto tw9910_set_fmt_error; 661e0d76c38SJacopo Mondi 662e0d76c38SJacopo Mondi /* 663e0d76c38SJacopo Mondi * reset hardware 664e0d76c38SJacopo Mondi */ 665e0d76c38SJacopo Mondi tw9910_reset(client); 666e0d76c38SJacopo Mondi 667e0d76c38SJacopo Mondi /* 668e0d76c38SJacopo Mondi * set bus width 669e0d76c38SJacopo Mondi */ 670e0d76c38SJacopo Mondi val = 0x00; 6717b20f325SJacopo Mondi if (priv->info->buswidth == 16) 672e0d76c38SJacopo Mondi val = LEN; 673e0d76c38SJacopo Mondi 674e0d76c38SJacopo Mondi ret = tw9910_mask_set(client, OPFORM, LEN, val); 675e0d76c38SJacopo Mondi if (ret < 0) 676e0d76c38SJacopo Mondi goto tw9910_set_fmt_error; 677e0d76c38SJacopo Mondi 678e0d76c38SJacopo Mondi /* 679e0d76c38SJacopo Mondi * select MPOUT behavior 680e0d76c38SJacopo Mondi */ 681e0d76c38SJacopo Mondi switch (priv->info->mpout) { 682e0d76c38SJacopo Mondi case TW9910_MPO_VLOSS: 683e0d76c38SJacopo Mondi val = RTSEL_VLOSS; break; 684e0d76c38SJacopo Mondi case TW9910_MPO_HLOCK: 685e0d76c38SJacopo Mondi val = RTSEL_HLOCK; break; 686e0d76c38SJacopo Mondi case TW9910_MPO_SLOCK: 687e0d76c38SJacopo Mondi val = RTSEL_SLOCK; break; 688e0d76c38SJacopo Mondi case TW9910_MPO_VLOCK: 689e0d76c38SJacopo Mondi val = RTSEL_VLOCK; break; 690e0d76c38SJacopo Mondi case TW9910_MPO_MONO: 691e0d76c38SJacopo Mondi val = RTSEL_MONO; break; 692e0d76c38SJacopo Mondi case TW9910_MPO_DET50: 693e0d76c38SJacopo Mondi val = RTSEL_DET50; break; 694e0d76c38SJacopo Mondi case TW9910_MPO_FIELD: 695e0d76c38SJacopo Mondi val = RTSEL_FIELD; break; 696e0d76c38SJacopo Mondi case TW9910_MPO_RTCO: 697e0d76c38SJacopo Mondi val = RTSEL_RTCO; break; 698e0d76c38SJacopo Mondi default: 699e0d76c38SJacopo Mondi val = 0; 700e0d76c38SJacopo Mondi } 701e0d76c38SJacopo Mondi 702e0d76c38SJacopo Mondi ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val); 703e0d76c38SJacopo Mondi if (ret < 0) 704e0d76c38SJacopo Mondi goto tw9910_set_fmt_error; 705e0d76c38SJacopo Mondi 706e0d76c38SJacopo Mondi /* 707e0d76c38SJacopo Mondi * set scale 708e0d76c38SJacopo Mondi */ 709e0d76c38SJacopo Mondi ret = tw9910_set_scale(client, priv->scale); 710e0d76c38SJacopo Mondi if (ret < 0) 711e0d76c38SJacopo Mondi goto tw9910_set_fmt_error; 712e0d76c38SJacopo Mondi 713e0d76c38SJacopo Mondi /* 714e0d76c38SJacopo Mondi * set hsync 715e0d76c38SJacopo Mondi */ 716e0d76c38SJacopo Mondi ret = tw9910_set_hsync(client); 717e0d76c38SJacopo Mondi if (ret < 0) 718e0d76c38SJacopo Mondi goto tw9910_set_fmt_error; 719e0d76c38SJacopo Mondi 720e0d76c38SJacopo Mondi *width = priv->scale->width; 721e0d76c38SJacopo Mondi *height = priv->scale->height; 722e0d76c38SJacopo Mondi 723e0d76c38SJacopo Mondi return ret; 724e0d76c38SJacopo Mondi 725e0d76c38SJacopo Mondi tw9910_set_fmt_error: 726e0d76c38SJacopo Mondi 727e0d76c38SJacopo Mondi tw9910_reset(client); 728e0d76c38SJacopo Mondi priv->scale = NULL; 729e0d76c38SJacopo Mondi 730e0d76c38SJacopo Mondi return ret; 731e0d76c38SJacopo Mondi } 732e0d76c38SJacopo Mondi 733e0d76c38SJacopo Mondi static int tw9910_get_selection(struct v4l2_subdev *sd, 734e0d76c38SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 735e0d76c38SJacopo Mondi struct v4l2_subdev_selection *sel) 736e0d76c38SJacopo Mondi { 737e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 738e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 739e0d76c38SJacopo Mondi 740e0d76c38SJacopo Mondi if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 741e0d76c38SJacopo Mondi return -EINVAL; 742e0d76c38SJacopo Mondi /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */ 743e0d76c38SJacopo Mondi if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS) 744e0d76c38SJacopo Mondi return -EINVAL; 745e0d76c38SJacopo Mondi 746e0d76c38SJacopo Mondi sel->r.left = 0; 747e0d76c38SJacopo Mondi sel->r.top = 0; 748e0d76c38SJacopo Mondi if (priv->norm & V4L2_STD_NTSC) { 749e0d76c38SJacopo Mondi sel->r.width = 640; 750e0d76c38SJacopo Mondi sel->r.height = 480; 751e0d76c38SJacopo Mondi } else { 752e0d76c38SJacopo Mondi sel->r.width = 768; 753e0d76c38SJacopo Mondi sel->r.height = 576; 754e0d76c38SJacopo Mondi } 755e0d76c38SJacopo Mondi return 0; 756e0d76c38SJacopo Mondi } 757e0d76c38SJacopo Mondi 758e0d76c38SJacopo Mondi static int tw9910_get_fmt(struct v4l2_subdev *sd, 759e0d76c38SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 760e0d76c38SJacopo Mondi struct v4l2_subdev_format *format) 761e0d76c38SJacopo Mondi { 762e0d76c38SJacopo Mondi struct v4l2_mbus_framefmt *mf = &format->format; 763e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 764e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 765e0d76c38SJacopo Mondi 766e0d76c38SJacopo Mondi if (format->pad) 767e0d76c38SJacopo Mondi return -EINVAL; 768e0d76c38SJacopo Mondi 769e0d76c38SJacopo Mondi if (!priv->scale) { 770e0d76c38SJacopo Mondi priv->scale = tw9910_select_norm(priv->norm, 640, 480); 771e0d76c38SJacopo Mondi if (!priv->scale) 772e0d76c38SJacopo Mondi return -EINVAL; 773e0d76c38SJacopo Mondi } 774e0d76c38SJacopo Mondi 775e0d76c38SJacopo Mondi mf->width = priv->scale->width; 776e0d76c38SJacopo Mondi mf->height = priv->scale->height; 777e0d76c38SJacopo Mondi mf->code = MEDIA_BUS_FMT_UYVY8_2X8; 778e0d76c38SJacopo Mondi mf->colorspace = V4L2_COLORSPACE_SMPTE170M; 779e0d76c38SJacopo Mondi mf->field = V4L2_FIELD_INTERLACED_BT; 780e0d76c38SJacopo Mondi 781e0d76c38SJacopo Mondi return 0; 782e0d76c38SJacopo Mondi } 783e0d76c38SJacopo Mondi 784e0d76c38SJacopo Mondi static int tw9910_s_fmt(struct v4l2_subdev *sd, 785e0d76c38SJacopo Mondi struct v4l2_mbus_framefmt *mf) 786e0d76c38SJacopo Mondi { 787e0d76c38SJacopo Mondi u32 width = mf->width, height = mf->height; 788e0d76c38SJacopo Mondi int ret; 789e0d76c38SJacopo Mondi 790e0d76c38SJacopo Mondi WARN_ON(mf->field != V4L2_FIELD_ANY && 791e0d76c38SJacopo Mondi mf->field != V4L2_FIELD_INTERLACED_BT); 792e0d76c38SJacopo Mondi 793e0d76c38SJacopo Mondi /* 794e0d76c38SJacopo Mondi * check color format 795e0d76c38SJacopo Mondi */ 796e0d76c38SJacopo Mondi if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8) 797e0d76c38SJacopo Mondi return -EINVAL; 798e0d76c38SJacopo Mondi 799e0d76c38SJacopo Mondi mf->colorspace = V4L2_COLORSPACE_SMPTE170M; 800e0d76c38SJacopo Mondi 801e0d76c38SJacopo Mondi ret = tw9910_set_frame(sd, &width, &height); 802e0d76c38SJacopo Mondi if (!ret) { 803e0d76c38SJacopo Mondi mf->width = width; 804e0d76c38SJacopo Mondi mf->height = height; 805e0d76c38SJacopo Mondi } 806e0d76c38SJacopo Mondi return ret; 807e0d76c38SJacopo Mondi } 808e0d76c38SJacopo Mondi 809e0d76c38SJacopo Mondi static int tw9910_set_fmt(struct v4l2_subdev *sd, 810e0d76c38SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 811e0d76c38SJacopo Mondi struct v4l2_subdev_format *format) 812e0d76c38SJacopo Mondi { 813e0d76c38SJacopo Mondi struct v4l2_mbus_framefmt *mf = &format->format; 814e0d76c38SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 815e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 816e0d76c38SJacopo Mondi const struct tw9910_scale_ctrl *scale; 817e0d76c38SJacopo Mondi 818e0d76c38SJacopo Mondi if (format->pad) 819e0d76c38SJacopo Mondi return -EINVAL; 820e0d76c38SJacopo Mondi 821e0d76c38SJacopo Mondi if (V4L2_FIELD_ANY == mf->field) { 822e0d76c38SJacopo Mondi mf->field = V4L2_FIELD_INTERLACED_BT; 823e0d76c38SJacopo Mondi } else if (V4L2_FIELD_INTERLACED_BT != mf->field) { 824e0d76c38SJacopo Mondi dev_err(&client->dev, "Field type %d invalid.\n", mf->field); 825e0d76c38SJacopo Mondi return -EINVAL; 826e0d76c38SJacopo Mondi } 827e0d76c38SJacopo Mondi 828e0d76c38SJacopo Mondi mf->code = MEDIA_BUS_FMT_UYVY8_2X8; 829e0d76c38SJacopo Mondi mf->colorspace = V4L2_COLORSPACE_SMPTE170M; 830e0d76c38SJacopo Mondi 831e0d76c38SJacopo Mondi /* 832e0d76c38SJacopo Mondi * select suitable norm 833e0d76c38SJacopo Mondi */ 834e0d76c38SJacopo Mondi scale = tw9910_select_norm(priv->norm, mf->width, mf->height); 835e0d76c38SJacopo Mondi if (!scale) 836e0d76c38SJacopo Mondi return -EINVAL; 837e0d76c38SJacopo Mondi 838e0d76c38SJacopo Mondi mf->width = scale->width; 839e0d76c38SJacopo Mondi mf->height = scale->height; 840e0d76c38SJacopo Mondi 841e0d76c38SJacopo Mondi if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) 842e0d76c38SJacopo Mondi return tw9910_s_fmt(sd, mf); 843e0d76c38SJacopo Mondi cfg->try_fmt = *mf; 844e0d76c38SJacopo Mondi return 0; 845e0d76c38SJacopo Mondi } 846e0d76c38SJacopo Mondi 847e0d76c38SJacopo Mondi static int tw9910_video_probe(struct i2c_client *client) 848e0d76c38SJacopo Mondi { 849e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 850e0d76c38SJacopo Mondi s32 id; 851e0d76c38SJacopo Mondi int ret; 852e0d76c38SJacopo Mondi 853e0d76c38SJacopo Mondi /* 854e0d76c38SJacopo Mondi * tw9910 only use 8 or 16 bit bus width 855e0d76c38SJacopo Mondi */ 8567b20f325SJacopo Mondi if (priv->info->buswidth != 16 && priv->info->buswidth != 8) { 857e0d76c38SJacopo Mondi dev_err(&client->dev, "bus width error\n"); 858e0d76c38SJacopo Mondi return -ENODEV; 859e0d76c38SJacopo Mondi } 860e0d76c38SJacopo Mondi 861e0d76c38SJacopo Mondi ret = tw9910_s_power(&priv->subdev, 1); 862e0d76c38SJacopo Mondi if (ret < 0) 863e0d76c38SJacopo Mondi return ret; 864e0d76c38SJacopo Mondi 865e0d76c38SJacopo Mondi /* 866e0d76c38SJacopo Mondi * check and show Product ID 867e0d76c38SJacopo Mondi * So far only revisions 0 and 1 have been seen 868e0d76c38SJacopo Mondi */ 869e0d76c38SJacopo Mondi id = i2c_smbus_read_byte_data(client, ID); 870e0d76c38SJacopo Mondi priv->revision = GET_REV(id); 871e0d76c38SJacopo Mondi id = GET_ID(id); 872e0d76c38SJacopo Mondi 873e0d76c38SJacopo Mondi if (0x0B != id || 874e0d76c38SJacopo Mondi 0x01 < priv->revision) { 875e0d76c38SJacopo Mondi dev_err(&client->dev, 876e0d76c38SJacopo Mondi "Product ID error %x:%x\n", 877e0d76c38SJacopo Mondi id, priv->revision); 878e0d76c38SJacopo Mondi ret = -ENODEV; 879e0d76c38SJacopo Mondi goto done; 880e0d76c38SJacopo Mondi } 881e0d76c38SJacopo Mondi 882e0d76c38SJacopo Mondi dev_info(&client->dev, 883e0d76c38SJacopo Mondi "tw9910 Product ID %0x:%0x\n", id, priv->revision); 884e0d76c38SJacopo Mondi 885e0d76c38SJacopo Mondi priv->norm = V4L2_STD_NTSC; 886e0d76c38SJacopo Mondi priv->scale = &tw9910_ntsc_scales[0]; 887e0d76c38SJacopo Mondi 888e0d76c38SJacopo Mondi done: 889e0d76c38SJacopo Mondi tw9910_s_power(&priv->subdev, 0); 890e0d76c38SJacopo Mondi return ret; 891e0d76c38SJacopo Mondi } 892e0d76c38SJacopo Mondi 893e0d76c38SJacopo Mondi static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { 894e0d76c38SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG 895e0d76c38SJacopo Mondi .g_register = tw9910_g_register, 896e0d76c38SJacopo Mondi .s_register = tw9910_s_register, 897e0d76c38SJacopo Mondi #endif 898e0d76c38SJacopo Mondi .s_power = tw9910_s_power, 899e0d76c38SJacopo Mondi }; 900e0d76c38SJacopo Mondi 901e0d76c38SJacopo Mondi static int tw9910_enum_mbus_code(struct v4l2_subdev *sd, 902e0d76c38SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 903e0d76c38SJacopo Mondi struct v4l2_subdev_mbus_code_enum *code) 904e0d76c38SJacopo Mondi { 905e0d76c38SJacopo Mondi if (code->pad || code->index) 906e0d76c38SJacopo Mondi return -EINVAL; 907e0d76c38SJacopo Mondi 908e0d76c38SJacopo Mondi code->code = MEDIA_BUS_FMT_UYVY8_2X8; 909e0d76c38SJacopo Mondi return 0; 910e0d76c38SJacopo Mondi } 911e0d76c38SJacopo Mondi 912e0d76c38SJacopo Mondi static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) 913e0d76c38SJacopo Mondi { 914e0d76c38SJacopo Mondi *norm = V4L2_STD_NTSC | V4L2_STD_PAL; 915e0d76c38SJacopo Mondi return 0; 916e0d76c38SJacopo Mondi } 917e0d76c38SJacopo Mondi 918e0d76c38SJacopo Mondi static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { 919e0d76c38SJacopo Mondi .s_std = tw9910_s_std, 920e0d76c38SJacopo Mondi .g_std = tw9910_g_std, 921e0d76c38SJacopo Mondi .s_stream = tw9910_s_stream, 922e0d76c38SJacopo Mondi .g_tvnorms = tw9910_g_tvnorms, 923e0d76c38SJacopo Mondi }; 924e0d76c38SJacopo Mondi 925e0d76c38SJacopo Mondi static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = { 926e0d76c38SJacopo Mondi .enum_mbus_code = tw9910_enum_mbus_code, 927e0d76c38SJacopo Mondi .get_selection = tw9910_get_selection, 928e0d76c38SJacopo Mondi .get_fmt = tw9910_get_fmt, 929e0d76c38SJacopo Mondi .set_fmt = tw9910_set_fmt, 930e0d76c38SJacopo Mondi }; 931e0d76c38SJacopo Mondi 932e0d76c38SJacopo Mondi static const struct v4l2_subdev_ops tw9910_subdev_ops = { 933e0d76c38SJacopo Mondi .core = &tw9910_subdev_core_ops, 934e0d76c38SJacopo Mondi .video = &tw9910_subdev_video_ops, 935e0d76c38SJacopo Mondi .pad = &tw9910_subdev_pad_ops, 936e0d76c38SJacopo Mondi }; 937e0d76c38SJacopo Mondi 938e0d76c38SJacopo Mondi /* 939e0d76c38SJacopo Mondi * i2c_driver function 940e0d76c38SJacopo Mondi */ 941e0d76c38SJacopo Mondi 942e0d76c38SJacopo Mondi static int tw9910_probe(struct i2c_client *client, 943e0d76c38SJacopo Mondi const struct i2c_device_id *did) 944e0d76c38SJacopo Mondi 945e0d76c38SJacopo Mondi { 946e0d76c38SJacopo Mondi struct tw9910_priv *priv; 947e0d76c38SJacopo Mondi struct tw9910_video_info *info; 948e0d76c38SJacopo Mondi struct i2c_adapter *adapter = 949e0d76c38SJacopo Mondi to_i2c_adapter(client->dev.parent); 950e0d76c38SJacopo Mondi int ret; 951e0d76c38SJacopo Mondi 9527b20f325SJacopo Mondi if (!client->dev.platform_data) { 953e0d76c38SJacopo Mondi dev_err(&client->dev, "TW9910: missing platform data!\n"); 954e0d76c38SJacopo Mondi return -EINVAL; 955e0d76c38SJacopo Mondi } 956e0d76c38SJacopo Mondi 9577b20f325SJacopo Mondi info = client->dev.platform_data; 958e0d76c38SJacopo Mondi 959e0d76c38SJacopo Mondi if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { 960e0d76c38SJacopo Mondi dev_err(&client->dev, 961e0d76c38SJacopo Mondi "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n"); 962e0d76c38SJacopo Mondi return -EIO; 963e0d76c38SJacopo Mondi } 964e0d76c38SJacopo Mondi 965e0d76c38SJacopo Mondi priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 966e0d76c38SJacopo Mondi if (!priv) 967e0d76c38SJacopo Mondi return -ENOMEM; 968e0d76c38SJacopo Mondi 969e0d76c38SJacopo Mondi priv->info = info; 970e0d76c38SJacopo Mondi 971e0d76c38SJacopo Mondi v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); 972e0d76c38SJacopo Mondi 9737b20f325SJacopo Mondi priv->clk = clk_get(&client->dev, "xti"); 9747b20f325SJacopo Mondi if (PTR_ERR(priv->clk) == -ENOENT) { 9757b20f325SJacopo Mondi priv->clk = NULL; 9767b20f325SJacopo Mondi } else if (IS_ERR(priv->clk)) { 9777b20f325SJacopo Mondi dev_err(&client->dev, "Unable to get xti clock\n"); 978e0d76c38SJacopo Mondi return PTR_ERR(priv->clk); 9797b20f325SJacopo Mondi } 9807b20f325SJacopo Mondi 9817b20f325SJacopo Mondi priv->pdn_gpio = gpiod_get_optional(&client->dev, "pdn", 9827b20f325SJacopo Mondi GPIOD_OUT_HIGH); 9837b20f325SJacopo Mondi if (IS_ERR(priv->pdn_gpio)) { 9847b20f325SJacopo Mondi dev_info(&client->dev, "Unable to get GPIO \"pdn\""); 9857b20f325SJacopo Mondi ret = PTR_ERR(priv->pdn_gpio); 9867b20f325SJacopo Mondi goto error_clk_put; 9877b20f325SJacopo Mondi } 988e0d76c38SJacopo Mondi 989e0d76c38SJacopo Mondi ret = tw9910_video_probe(client); 990e0d76c38SJacopo Mondi if (ret < 0) 9917b20f325SJacopo Mondi goto error_gpio_put; 9927b20f325SJacopo Mondi 9937b20f325SJacopo Mondi ret = v4l2_async_register_subdev(&priv->subdev); 9947b20f325SJacopo Mondi if (ret) 9957b20f325SJacopo Mondi goto error_gpio_put; 9967b20f325SJacopo Mondi 9977b20f325SJacopo Mondi return ret; 9987b20f325SJacopo Mondi 9997b20f325SJacopo Mondi error_gpio_put: 10007b20f325SJacopo Mondi if (priv->pdn_gpio) 10017b20f325SJacopo Mondi gpiod_put(priv->pdn_gpio); 10027b20f325SJacopo Mondi error_clk_put: 10037b20f325SJacopo Mondi clk_put(priv->clk); 1004e0d76c38SJacopo Mondi 1005e0d76c38SJacopo Mondi return ret; 1006e0d76c38SJacopo Mondi } 1007e0d76c38SJacopo Mondi 1008e0d76c38SJacopo Mondi static int tw9910_remove(struct i2c_client *client) 1009e0d76c38SJacopo Mondi { 1010e0d76c38SJacopo Mondi struct tw9910_priv *priv = to_tw9910(client); 10117b20f325SJacopo Mondi 10127b20f325SJacopo Mondi if (priv->pdn_gpio) 10137b20f325SJacopo Mondi gpiod_put(priv->pdn_gpio); 10147b20f325SJacopo Mondi clk_put(priv->clk); 10157b20f325SJacopo Mondi v4l2_device_unregister_subdev(&priv->subdev); 10167b20f325SJacopo Mondi 1017e0d76c38SJacopo Mondi return 0; 1018e0d76c38SJacopo Mondi } 1019e0d76c38SJacopo Mondi 1020e0d76c38SJacopo Mondi static const struct i2c_device_id tw9910_id[] = { 1021e0d76c38SJacopo Mondi { "tw9910", 0 }, 1022e0d76c38SJacopo Mondi { } 1023e0d76c38SJacopo Mondi }; 1024e0d76c38SJacopo Mondi MODULE_DEVICE_TABLE(i2c, tw9910_id); 1025e0d76c38SJacopo Mondi 1026e0d76c38SJacopo Mondi static struct i2c_driver tw9910_i2c_driver = { 1027e0d76c38SJacopo Mondi .driver = { 1028e0d76c38SJacopo Mondi .name = "tw9910", 1029e0d76c38SJacopo Mondi }, 1030e0d76c38SJacopo Mondi .probe = tw9910_probe, 1031e0d76c38SJacopo Mondi .remove = tw9910_remove, 1032e0d76c38SJacopo Mondi .id_table = tw9910_id, 1033e0d76c38SJacopo Mondi }; 1034e0d76c38SJacopo Mondi 1035e0d76c38SJacopo Mondi module_i2c_driver(tw9910_i2c_driver); 1036e0d76c38SJacopo Mondi 10377b20f325SJacopo Mondi MODULE_DESCRIPTION("V4L2 driver for TW9910 video decoder"); 1038e0d76c38SJacopo Mondi MODULE_AUTHOR("Kuninori Morimoto"); 1039e0d76c38SJacopo Mondi MODULE_LICENSE("GPL v2"); 1040