18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * saa7110 - Philips SAA7110(A) video decoder driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1998 Pauline Middelink <middelin@polyware.nl> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net> 88c2ecf20Sopenharmony_ci * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> 98c2ecf20Sopenharmony_ci * - some corrections for Pinnacle Systems Inc. DC10plus card. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net> 128c2ecf20Sopenharmony_ci * - moved over to linux>=2.4.x i2c protocol (1/1/2003) 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/wait.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci#include <linux/i2c.h> 238c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 258c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); 288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pauline Middelink"); 298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int debug; 338c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-1)"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ 378c2ecf20Sopenharmony_ci#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define SAA7110_NR_REG 0x35 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct saa7110 { 428c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 438c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 448c2ecf20Sopenharmony_ci u8 reg[SAA7110_NR_REG]; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci v4l2_std_id norm; 478c2ecf20Sopenharmony_ci int input; 488c2ecf20Sopenharmony_ci int enable; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci wait_queue_head_t wq; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci return container_of(sd, struct saa7110, sd); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct saa7110, hdl)->sd; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 648c2ecf20Sopenharmony_ci/* I2C support functions */ 658c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 708c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci decoder->reg[reg] = value; 738c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, reg, value); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 798c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 808c2ecf20Sopenharmony_ci int ret = -1; 818c2ecf20Sopenharmony_ci u8 reg = *data; /* first register to write to */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Sanity check */ 848c2ecf20Sopenharmony_ci if (reg + (len - 1) > SAA7110_NR_REG) 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* the saa7110 has an autoincrement function, use it if 888c2ecf20Sopenharmony_ci * the adapter understands raw I2C */ 898c2ecf20Sopenharmony_ci if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 908c2ecf20Sopenharmony_ci ret = i2c_master_send(client, data, len); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Cache the written data */ 938c2ecf20Sopenharmony_ci memcpy(decoder->reg + reg, data + 1, len - 1); 948c2ecf20Sopenharmony_ci } else { 958c2ecf20Sopenharmony_ci for (++data, --len; len; len--) { 968c2ecf20Sopenharmony_ci ret = saa7110_write(sd, reg++, *data++); 978c2ecf20Sopenharmony_ci if (ret < 0) 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic inline int saa7110_read(struct v4l2_subdev *sd) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return i2c_smbus_read_byte(client); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 1138c2ecf20Sopenharmony_ci/* SAA7110 functions */ 1148c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define FRESP_06H_COMPST 0x03 /*0x13*/ 1178c2ecf20Sopenharmony_ci#define FRESP_06H_SVIDEO 0x83 /*0xC0*/ 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int saa7110_selmux(struct v4l2_subdev *sd, int chan) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci static const unsigned char modes[9][8] = { 1238c2ecf20Sopenharmony_ci /* mode 0 */ 1248c2ecf20Sopenharmony_ci {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, 1258c2ecf20Sopenharmony_ci 0x44, 0x75, 0x16}, 1268c2ecf20Sopenharmony_ci /* mode 1 */ 1278c2ecf20Sopenharmony_ci {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, 1288c2ecf20Sopenharmony_ci 0x44, 0x75, 0x16}, 1298c2ecf20Sopenharmony_ci /* mode 2 */ 1308c2ecf20Sopenharmony_ci {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, 1318c2ecf20Sopenharmony_ci 0x60, 0xB5, 0x05}, 1328c2ecf20Sopenharmony_ci /* mode 3 */ 1338c2ecf20Sopenharmony_ci {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, 1348c2ecf20Sopenharmony_ci 0x60, 0xB5, 0x05}, 1358c2ecf20Sopenharmony_ci /* mode 4 */ 1368c2ecf20Sopenharmony_ci {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, 1378c2ecf20Sopenharmony_ci 0x60, 0xB5, 0x03}, 1388c2ecf20Sopenharmony_ci /* mode 5 */ 1398c2ecf20Sopenharmony_ci {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, 1408c2ecf20Sopenharmony_ci 0x60, 0xB5, 0x03}, 1418c2ecf20Sopenharmony_ci /* mode 6 */ 1428c2ecf20Sopenharmony_ci {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, 1438c2ecf20Sopenharmony_ci 0x44, 0x75, 0x12}, 1448c2ecf20Sopenharmony_ci /* mode 7 */ 1458c2ecf20Sopenharmony_ci {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, 1468c2ecf20Sopenharmony_ci 0x60, 0xB5, 0x14}, 1478c2ecf20Sopenharmony_ci /* mode 8 */ 1488c2ecf20Sopenharmony_ci {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, 1498c2ecf20Sopenharmony_ci 0x44, 0x75, 0x21} 1508c2ecf20Sopenharmony_ci }; 1518c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 1528c2ecf20Sopenharmony_ci const unsigned char *ptr = modes[chan]; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */ 1558c2ecf20Sopenharmony_ci saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */ 1568c2ecf20Sopenharmony_ci saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */ 1578c2ecf20Sopenharmony_ci saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */ 1588c2ecf20Sopenharmony_ci saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */ 1598c2ecf20Sopenharmony_ci saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */ 1608c2ecf20Sopenharmony_ci saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */ 1618c2ecf20Sopenharmony_ci saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */ 1628c2ecf20Sopenharmony_ci decoder->input = chan; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const unsigned char initseq[1 + SAA7110_NR_REG] = { 1688c2ecf20Sopenharmony_ci 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, 1698c2ecf20Sopenharmony_ci /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, 1708c2ecf20Sopenharmony_ci /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, 1718c2ecf20Sopenharmony_ci /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1728c2ecf20Sopenharmony_ci /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, 1738c2ecf20Sopenharmony_ci /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, 1748c2ecf20Sopenharmony_ci /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic v4l2_std_id determine_norm(struct v4l2_subdev *sd) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 1808c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 1818c2ecf20Sopenharmony_ci int status; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* mode changed, start automatic detection */ 1848c2ecf20Sopenharmony_ci saa7110_write_block(sd, initseq, sizeof(initseq)); 1858c2ecf20Sopenharmony_ci saa7110_selmux(sd, decoder->input); 1868c2ecf20Sopenharmony_ci prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); 1878c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(250)); 1888c2ecf20Sopenharmony_ci finish_wait(&decoder->wq, &wait); 1898c2ecf20Sopenharmony_ci status = saa7110_read(sd); 1908c2ecf20Sopenharmony_ci if (status & 0x40) { 1918c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status); 1928c2ecf20Sopenharmony_ci return V4L2_STD_UNKNOWN; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if ((status & 3) == 0) { 1958c2ecf20Sopenharmony_ci saa7110_write(sd, 0x06, 0x83); 1968c2ecf20Sopenharmony_ci if (status & 0x20) { 1978c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status); 1988c2ecf20Sopenharmony_ci /*saa7110_write(sd,0x2E,0x81);*/ 1998c2ecf20Sopenharmony_ci return V4L2_STD_NTSC; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status); 2028c2ecf20Sopenharmony_ci /*saa7110_write(sd,0x2E,0x9A);*/ 2038c2ecf20Sopenharmony_ci return V4L2_STD_PAL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci /*saa7110_write(sd,0x06,0x03);*/ 2068c2ecf20Sopenharmony_ci if (status & 0x20) { /* 60Hz */ 2078c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status); 2088c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x86); 2098c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0F, 0x50); 2108c2ecf20Sopenharmony_ci saa7110_write(sd, 0x11, 0x2C); 2118c2ecf20Sopenharmony_ci /*saa7110_write(sd,0x2E,0x81);*/ 2128c2ecf20Sopenharmony_ci return V4L2_STD_NTSC; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 50Hz -> PAL/SECAM */ 2168c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x86); 2178c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0F, 0x10); 2188c2ecf20Sopenharmony_ci saa7110_write(sd, 0x11, 0x59); 2198c2ecf20Sopenharmony_ci /*saa7110_write(sd,0x2E,0x9A);*/ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); 2228c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(250)); 2238c2ecf20Sopenharmony_ci finish_wait(&decoder->wq, &wait); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci status = saa7110_read(sd); 2268c2ecf20Sopenharmony_ci if ((status & 0x03) == 0x01) { 2278c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status); 2288c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x87); 2298c2ecf20Sopenharmony_ci return V4L2_STD_SECAM; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status); 2328c2ecf20Sopenharmony_ci return V4L2_STD_PAL; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 2388c2ecf20Sopenharmony_ci int res = V4L2_IN_ST_NO_SIGNAL; 2398c2ecf20Sopenharmony_ci int status = saa7110_read(sd); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", 2428c2ecf20Sopenharmony_ci status, (unsigned long long)decoder->norm); 2438c2ecf20Sopenharmony_ci if (!(status & 0x40)) 2448c2ecf20Sopenharmony_ci res = 0; 2458c2ecf20Sopenharmony_ci if (!(status & 0x03)) 2468c2ecf20Sopenharmony_ci res |= V4L2_IN_ST_NO_COLOR; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci *pstatus = res; 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci *std &= determine_norm(sd); 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (decoder->norm != std) { 2638c2ecf20Sopenharmony_ci decoder->norm = std; 2648c2ecf20Sopenharmony_ci /*saa7110_write(sd, 0x06, 0x03);*/ 2658c2ecf20Sopenharmony_ci if (std & V4L2_STD_NTSC) { 2668c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x86); 2678c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0F, 0x50); 2688c2ecf20Sopenharmony_ci saa7110_write(sd, 0x11, 0x2C); 2698c2ecf20Sopenharmony_ci /*saa7110_write(sd, 0x2E, 0x81);*/ 2708c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "switched to NTSC\n"); 2718c2ecf20Sopenharmony_ci } else if (std & V4L2_STD_PAL) { 2728c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x86); 2738c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0F, 0x10); 2748c2ecf20Sopenharmony_ci saa7110_write(sd, 0x11, 0x59); 2758c2ecf20Sopenharmony_ci /*saa7110_write(sd, 0x2E, 0x9A);*/ 2768c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "switched to PAL\n"); 2778c2ecf20Sopenharmony_ci } else if (std & V4L2_STD_SECAM) { 2788c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x87); 2798c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0F, 0x10); 2808c2ecf20Sopenharmony_ci saa7110_write(sd, 0x11, 0x59); 2818c2ecf20Sopenharmony_ci /*saa7110_write(sd, 0x2E, 0x9A);*/ 2828c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "switched to SECAM\n"); 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci return -EINVAL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int saa7110_s_routing(struct v4l2_subdev *sd, 2918c2ecf20Sopenharmony_ci u32 input, u32 output, u32 config) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (input >= SAA7110_MAX_INPUT) { 2968c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "input=%d not available\n", input); 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci if (decoder->input != input) { 3008c2ecf20Sopenharmony_ci saa7110_selmux(sd, input); 3018c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "switched to input=%d\n", input); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int saa7110_s_stream(struct v4l2_subdev *sd, int enable) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (decoder->enable != enable) { 3118c2ecf20Sopenharmony_ci decoder->enable = enable; 3128c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80); 3138c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off"); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int saa7110_s_ctrl(struct v4l2_ctrl *ctrl) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci switch (ctrl->id) { 3238c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 3248c2ecf20Sopenharmony_ci saa7110_write(sd, 0x19, ctrl->val); 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 3278c2ecf20Sopenharmony_ci saa7110_write(sd, 0x13, ctrl->val); 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 3308c2ecf20Sopenharmony_ci saa7110_write(sd, 0x12, ctrl->val); 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci case V4L2_CID_HUE: 3338c2ecf20Sopenharmony_ci saa7110_write(sd, 0x07, ctrl->val); 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci default: 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops saa7110_ctrl_ops = { 3448c2ecf20Sopenharmony_ci .s_ctrl = saa7110_s_ctrl, 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops saa7110_video_ops = { 3488c2ecf20Sopenharmony_ci .s_std = saa7110_s_std, 3498c2ecf20Sopenharmony_ci .s_routing = saa7110_s_routing, 3508c2ecf20Sopenharmony_ci .s_stream = saa7110_s_stream, 3518c2ecf20Sopenharmony_ci .querystd = saa7110_querystd, 3528c2ecf20Sopenharmony_ci .g_input_status = saa7110_g_input_status, 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops saa7110_ops = { 3568c2ecf20Sopenharmony_ci .video = &saa7110_video_ops, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int saa7110_probe(struct i2c_client *client, 3628c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct saa7110 *decoder; 3658c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 3668c2ecf20Sopenharmony_ci int rv; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Check if the adapter supports the needed features */ 3698c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 3708c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) 3718c2ecf20Sopenharmony_ci return -ENODEV; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci v4l_info(client, "chip found @ 0x%x (%s)\n", 3748c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL); 3778c2ecf20Sopenharmony_ci if (!decoder) 3788c2ecf20Sopenharmony_ci return -ENOMEM; 3798c2ecf20Sopenharmony_ci sd = &decoder->sd; 3808c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &saa7110_ops); 3818c2ecf20Sopenharmony_ci decoder->norm = V4L2_STD_PAL; 3828c2ecf20Sopenharmony_ci decoder->input = 0; 3838c2ecf20Sopenharmony_ci decoder->enable = 1; 3848c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&decoder->hdl, 2); 3858c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, 3868c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); 3878c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, 3888c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, 0, 127, 1, 64); 3898c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, 3908c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, 0, 127, 1, 64); 3918c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, 3928c2ecf20Sopenharmony_ci V4L2_CID_HUE, -128, 127, 1, 0); 3938c2ecf20Sopenharmony_ci sd->ctrl_handler = &decoder->hdl; 3948c2ecf20Sopenharmony_ci if (decoder->hdl.error) { 3958c2ecf20Sopenharmony_ci int err = decoder->hdl.error; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&decoder->hdl); 3988c2ecf20Sopenharmony_ci return err; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&decoder->hdl); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci init_waitqueue_head(&decoder->wq); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci rv = saa7110_write_block(sd, initseq, sizeof(initseq)); 4058c2ecf20Sopenharmony_ci if (rv < 0) { 4068c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "init status %d\n", rv); 4078c2ecf20Sopenharmony_ci } else { 4088c2ecf20Sopenharmony_ci int ver, status; 4098c2ecf20Sopenharmony_ci saa7110_write(sd, 0x21, 0x10); 4108c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0e, 0x18); 4118c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x04); 4128c2ecf20Sopenharmony_ci ver = saa7110_read(sd); 4138c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x06); 4148c2ecf20Sopenharmony_ci /*mdelay(150);*/ 4158c2ecf20Sopenharmony_ci status = saa7110_read(sd); 4168c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n", 4178c2ecf20Sopenharmony_ci ver, status); 4188c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0D, 0x86); 4198c2ecf20Sopenharmony_ci saa7110_write(sd, 0x0F, 0x10); 4208c2ecf20Sopenharmony_ci saa7110_write(sd, 0x11, 0x59); 4218c2ecf20Sopenharmony_ci /*saa7110_write(sd, 0x2E, 0x9A);*/ 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /*saa7110_selmux(sd,0);*/ 4258c2ecf20Sopenharmony_ci /*determine_norm(sd);*/ 4268c2ecf20Sopenharmony_ci /* setup and implicit mode 0 select has been performed */ 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int saa7110_remove(struct i2c_client *client) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 4348c2ecf20Sopenharmony_ci struct saa7110 *decoder = to_saa7110(sd); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 4378c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&decoder->hdl); 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic const struct i2c_device_id saa7110_id[] = { 4448c2ecf20Sopenharmony_ci { "saa7110", 0 }, 4458c2ecf20Sopenharmony_ci { } 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, saa7110_id); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic struct i2c_driver saa7110_driver = { 4508c2ecf20Sopenharmony_ci .driver = { 4518c2ecf20Sopenharmony_ci .name = "saa7110", 4528c2ecf20Sopenharmony_ci }, 4538c2ecf20Sopenharmony_ci .probe = saa7110_probe, 4548c2ecf20Sopenharmony_ci .remove = saa7110_remove, 4558c2ecf20Sopenharmony_ci .id_table = saa7110_id, 4568c2ecf20Sopenharmony_ci}; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cimodule_i2c_driver(saa7110_driver); 459