18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Driver for simple i2c audio chips. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2000 Gerd Knorr 58c2ecf20Sopenharmony_ci * based on code by: 68c2ecf20Sopenharmony_ci * Eric Sandeen (eric_sandeen@bigfoot.com) 78c2ecf20Sopenharmony_ci * Steve VanDeBogart (vandebo@uclink.berkeley.edu) 88c2ecf20Sopenharmony_ci * Greg Alexander (galexand@acm.org) 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * For the TDA9875 part: 118c2ecf20Sopenharmony_ci * Copyright (c) 2000 Guillaume Delvit based on Gerd Knorr source 128c2ecf20Sopenharmony_ci * and Eric Sandeen 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright(c) 2005-2008 Mauro Carvalho Chehab 158c2ecf20Sopenharmony_ci * - Some cleanups, code fixes, etc 168c2ecf20Sopenharmony_ci * - Convert it to V4L2 API 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This code is placed under the terms of the GNU General Public License 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * OPTIONS: 218c2ecf20Sopenharmony_ci * debug - set to 1 if you'd like to see debug messages 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/sched.h> 288c2ecf20Sopenharmony_ci#include <linux/string.h> 298c2ecf20Sopenharmony_ci#include <linux/timer.h> 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/errno.h> 328c2ecf20Sopenharmony_ci#include <linux/slab.h> 338c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 348c2ecf20Sopenharmony_ci#include <linux/i2c.h> 358c2ecf20Sopenharmony_ci#include <linux/init.h> 368c2ecf20Sopenharmony_ci#include <linux/kthread.h> 378c2ecf20Sopenharmony_ci#include <linux/freezer.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <media/i2c/tvaudio.h> 408c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 418c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 448c2ecf20Sopenharmony_ci/* insmod args */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int debug; /* insmod parameter */ 478c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); 508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr"); 518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define UNSET (-1U) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 568c2ecf20Sopenharmony_ci/* our structs */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define MAXREGS 256 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct CHIPSTATE; 618c2ecf20Sopenharmony_citypedef int (*getvalue)(int); 628c2ecf20Sopenharmony_citypedef int (*checkit)(struct CHIPSTATE*); 638c2ecf20Sopenharmony_citypedef int (*initialize)(struct CHIPSTATE*); 648c2ecf20Sopenharmony_citypedef int (*getrxsubchans)(struct CHIPSTATE *); 658c2ecf20Sopenharmony_citypedef void (*setaudmode)(struct CHIPSTATE*, int mode); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* i2c command */ 688c2ecf20Sopenharmony_citypedef struct AUDIOCMD { 698c2ecf20Sopenharmony_ci int count; /* # of bytes to send */ 708c2ecf20Sopenharmony_ci unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */ 718c2ecf20Sopenharmony_ci} audiocmd; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* chip description */ 748c2ecf20Sopenharmony_cistruct CHIPDESC { 758c2ecf20Sopenharmony_ci char *name; /* chip name */ 768c2ecf20Sopenharmony_ci int addr_lo, addr_hi; /* i2c address range */ 778c2ecf20Sopenharmony_ci int registers; /* # of registers */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci int *insmodopt; 808c2ecf20Sopenharmony_ci checkit checkit; 818c2ecf20Sopenharmony_ci initialize initialize; 828c2ecf20Sopenharmony_ci int flags; 838c2ecf20Sopenharmony_ci#define CHIP_HAS_VOLUME 1 848c2ecf20Sopenharmony_ci#define CHIP_HAS_BASSTREBLE 2 858c2ecf20Sopenharmony_ci#define CHIP_HAS_INPUTSEL 4 868c2ecf20Sopenharmony_ci#define CHIP_NEED_CHECKMODE 8 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* various i2c command sequences */ 898c2ecf20Sopenharmony_ci audiocmd init; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* which register has which value */ 928c2ecf20Sopenharmony_ci int leftreg, rightreg, treblereg, bassreg; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* initialize with (defaults to 65535/32768/32768 */ 958c2ecf20Sopenharmony_ci int volinit, trebleinit, bassinit; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* functions to convert the values (v4l -> chip) */ 988c2ecf20Sopenharmony_ci getvalue volfunc, treblefunc, bassfunc; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* get/set mode */ 1018c2ecf20Sopenharmony_ci getrxsubchans getrxsubchans; 1028c2ecf20Sopenharmony_ci setaudmode setaudmode; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* input switch register + values for v4l inputs */ 1058c2ecf20Sopenharmony_ci int inputreg; 1068c2ecf20Sopenharmony_ci int inputmap[4]; 1078c2ecf20Sopenharmony_ci int inputmute; 1088c2ecf20Sopenharmony_ci int inputmask; 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* current state of the chip */ 1128c2ecf20Sopenharmony_cistruct CHIPSTATE { 1138c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 1148c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 1158c2ecf20Sopenharmony_ci struct { 1168c2ecf20Sopenharmony_ci /* volume/balance cluster */ 1178c2ecf20Sopenharmony_ci struct v4l2_ctrl *volume; 1188c2ecf20Sopenharmony_ci struct v4l2_ctrl *balance; 1198c2ecf20Sopenharmony_ci }; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* chip-specific description - should point to 1228c2ecf20Sopenharmony_ci an entry at CHIPDESC table */ 1238c2ecf20Sopenharmony_ci struct CHIPDESC *desc; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* shadow register set */ 1268c2ecf20Sopenharmony_ci audiocmd shadow; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* current settings */ 1298c2ecf20Sopenharmony_ci u16 muted; 1308c2ecf20Sopenharmony_ci int prevmode; 1318c2ecf20Sopenharmony_ci int radio; 1328c2ecf20Sopenharmony_ci int input; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* thread */ 1358c2ecf20Sopenharmony_ci struct task_struct *thread; 1368c2ecf20Sopenharmony_ci struct timer_list wt; 1378c2ecf20Sopenharmony_ci int audmode; 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci return container_of(sd, struct CHIPSTATE, sd); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct CHIPSTATE, hdl)->sd; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 1528c2ecf20Sopenharmony_ci/* i2c I/O functions */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int chip_write(struct CHIPSTATE *chip, int subaddr, int val) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 1578c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(sd); 1588c2ecf20Sopenharmony_ci unsigned char buffer[2]; 1598c2ecf20Sopenharmony_ci int rc; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (subaddr < 0) { 1628c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); 1638c2ecf20Sopenharmony_ci chip->shadow.bytes[1] = val; 1648c2ecf20Sopenharmony_ci buffer[0] = val; 1658c2ecf20Sopenharmony_ci rc = i2c_master_send(c, buffer, 1); 1668c2ecf20Sopenharmony_ci if (rc != 1) { 1678c2ecf20Sopenharmony_ci v4l2_warn(sd, "I/O error (write 0x%x)\n", val); 1688c2ecf20Sopenharmony_ci if (rc < 0) 1698c2ecf20Sopenharmony_ci return rc; 1708c2ecf20Sopenharmony_ci return -EIO; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } else { 1738c2ecf20Sopenharmony_ci if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { 1748c2ecf20Sopenharmony_ci v4l2_info(sd, 1758c2ecf20Sopenharmony_ci "Tried to access a non-existent register: %d\n", 1768c2ecf20Sopenharmony_ci subaddr); 1778c2ecf20Sopenharmony_ci return -EINVAL; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", 1818c2ecf20Sopenharmony_ci subaddr, val); 1828c2ecf20Sopenharmony_ci chip->shadow.bytes[subaddr+1] = val; 1838c2ecf20Sopenharmony_ci buffer[0] = subaddr; 1848c2ecf20Sopenharmony_ci buffer[1] = val; 1858c2ecf20Sopenharmony_ci rc = i2c_master_send(c, buffer, 2); 1868c2ecf20Sopenharmony_ci if (rc != 2) { 1878c2ecf20Sopenharmony_ci v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", 1888c2ecf20Sopenharmony_ci subaddr, val); 1898c2ecf20Sopenharmony_ci if (rc < 0) 1908c2ecf20Sopenharmony_ci return rc; 1918c2ecf20Sopenharmony_ci return -EIO; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int chip_write_masked(struct CHIPSTATE *chip, 1988c2ecf20Sopenharmony_ci int subaddr, int val, int mask) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (mask != 0) { 2038c2ecf20Sopenharmony_ci if (subaddr < 0) { 2048c2ecf20Sopenharmony_ci val = (chip->shadow.bytes[1] & ~mask) | (val & mask); 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { 2078c2ecf20Sopenharmony_ci v4l2_info(sd, 2088c2ecf20Sopenharmony_ci "Tried to access a non-existent register: %d\n", 2098c2ecf20Sopenharmony_ci subaddr); 2108c2ecf20Sopenharmony_ci return -EINVAL; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci return chip_write(chip, subaddr, val); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int chip_read(struct CHIPSTATE *chip) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 2228c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(sd); 2238c2ecf20Sopenharmony_ci unsigned char buffer; 2248c2ecf20Sopenharmony_ci int rc; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci rc = i2c_master_recv(c, &buffer, 1); 2278c2ecf20Sopenharmony_ci if (rc != 1) { 2288c2ecf20Sopenharmony_ci v4l2_warn(sd, "I/O error (read)\n"); 2298c2ecf20Sopenharmony_ci if (rc < 0) 2308c2ecf20Sopenharmony_ci return rc; 2318c2ecf20Sopenharmony_ci return -EIO; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); 2348c2ecf20Sopenharmony_ci return buffer; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int chip_read2(struct CHIPSTATE *chip, int subaddr) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 2408c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(sd); 2418c2ecf20Sopenharmony_ci int rc; 2428c2ecf20Sopenharmony_ci unsigned char write[1]; 2438c2ecf20Sopenharmony_ci unsigned char read[1]; 2448c2ecf20Sopenharmony_ci struct i2c_msg msgs[2] = { 2458c2ecf20Sopenharmony_ci { 2468c2ecf20Sopenharmony_ci .addr = c->addr, 2478c2ecf20Sopenharmony_ci .len = 1, 2488c2ecf20Sopenharmony_ci .buf = write 2498c2ecf20Sopenharmony_ci }, 2508c2ecf20Sopenharmony_ci { 2518c2ecf20Sopenharmony_ci .addr = c->addr, 2528c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 2538c2ecf20Sopenharmony_ci .len = 1, 2548c2ecf20Sopenharmony_ci .buf = read 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci }; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci write[0] = subaddr; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci rc = i2c_transfer(c->adapter, msgs, 2); 2618c2ecf20Sopenharmony_ci if (rc != 2) { 2628c2ecf20Sopenharmony_ci v4l2_warn(sd, "I/O error (read2)\n"); 2638c2ecf20Sopenharmony_ci if (rc < 0) 2648c2ecf20Sopenharmony_ci return rc; 2658c2ecf20Sopenharmony_ci return -EIO; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", 2688c2ecf20Sopenharmony_ci subaddr, read[0]); 2698c2ecf20Sopenharmony_ci return read[0]; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 2758c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(sd); 2768c2ecf20Sopenharmony_ci int i, rc; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (0 == cmd->count) 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { 2828c2ecf20Sopenharmony_ci v4l2_info(sd, 2838c2ecf20Sopenharmony_ci "Tried to access a non-existent register range: %d to %d\n", 2848c2ecf20Sopenharmony_ci cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); 2858c2ecf20Sopenharmony_ci return -EINVAL; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* FIXME: it seems that the shadow bytes are wrong below !*/ 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* update our shadow register set; print bytes if (debug > 0) */ 2918c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", 2928c2ecf20Sopenharmony_ci name, cmd->bytes[0]); 2938c2ecf20Sopenharmony_ci for (i = 1; i < cmd->count; i++) { 2948c2ecf20Sopenharmony_ci if (debug) 2958c2ecf20Sopenharmony_ci printk(KERN_CONT " 0x%x", cmd->bytes[i]); 2968c2ecf20Sopenharmony_ci chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci if (debug) 2998c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* send data to the chip */ 3028c2ecf20Sopenharmony_ci rc = i2c_master_send(c, cmd->bytes, cmd->count); 3038c2ecf20Sopenharmony_ci if (rc != cmd->count) { 3048c2ecf20Sopenharmony_ci v4l2_warn(sd, "I/O error (%s)\n", name); 3058c2ecf20Sopenharmony_ci if (rc < 0) 3068c2ecf20Sopenharmony_ci return rc; 3078c2ecf20Sopenharmony_ci return -EIO; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 3138c2ecf20Sopenharmony_ci/* kernel thread for doing i2c stuff asyncronly 3148c2ecf20Sopenharmony_ci * right now it is used only to check the audio mode (mono/stereo/whatever) 3158c2ecf20Sopenharmony_ci * some time after switching to another TV channel, then turn on stereo 3168c2ecf20Sopenharmony_ci * if available, ... 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void chip_thread_wake(struct timer_list *t) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = from_timer(chip, t, wt); 3228c2ecf20Sopenharmony_ci wake_up_process(chip->thread); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int chip_thread(void *data) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = data; 3288c2ecf20Sopenharmony_ci struct CHIPDESC *desc = chip->desc; 3298c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 3308c2ecf20Sopenharmony_ci int mode, selected; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "thread started\n"); 3338c2ecf20Sopenharmony_ci set_freezable(); 3348c2ecf20Sopenharmony_ci for (;;) { 3358c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 3368c2ecf20Sopenharmony_ci if (!kthread_should_stop()) 3378c2ecf20Sopenharmony_ci schedule(); 3388c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 3398c2ecf20Sopenharmony_ci try_to_freeze(); 3408c2ecf20Sopenharmony_ci if (kthread_should_stop()) 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "thread wakeup\n"); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* don't do anything for radio */ 3458c2ecf20Sopenharmony_ci if (chip->radio) 3468c2ecf20Sopenharmony_ci continue; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* have a look what's going on */ 3498c2ecf20Sopenharmony_ci mode = desc->getrxsubchans(chip); 3508c2ecf20Sopenharmony_ci if (mode == chip->prevmode) 3518c2ecf20Sopenharmony_ci continue; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* chip detected a new audio mode - set it */ 3548c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "thread checkmode\n"); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci chip->prevmode = mode; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_MONO; 3598c2ecf20Sopenharmony_ci switch (chip->audmode) { 3608c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 3618c2ecf20Sopenharmony_ci if (mode & V4L2_TUNER_SUB_LANG1) 3628c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_LANG1; 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 3658c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 3668c2ecf20Sopenharmony_ci if (mode & V4L2_TUNER_SUB_LANG1) 3678c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_LANG1; 3688c2ecf20Sopenharmony_ci else if (mode & V4L2_TUNER_SUB_STEREO) 3698c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_STEREO; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 3728c2ecf20Sopenharmony_ci if (mode & V4L2_TUNER_SUB_LANG2) 3738c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_LANG2; 3748c2ecf20Sopenharmony_ci else if (mode & V4L2_TUNER_SUB_STEREO) 3758c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_STEREO; 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 3788c2ecf20Sopenharmony_ci if (mode & V4L2_TUNER_SUB_LANG2) 3798c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_LANG1_LANG2; 3808c2ecf20Sopenharmony_ci else if (mode & V4L2_TUNER_SUB_STEREO) 3818c2ecf20Sopenharmony_ci selected = V4L2_TUNER_MODE_STEREO; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci desc->setaudmode(chip, selected); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* schedule next check */ 3868c2ecf20Sopenharmony_ci mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "thread exiting\n"); 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 3948c2ecf20Sopenharmony_ci/* audio chip descriptions - defines+functions for tda9840 */ 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci#define TDA9840_SW 0x00 3978c2ecf20Sopenharmony_ci#define TDA9840_LVADJ 0x02 3988c2ecf20Sopenharmony_ci#define TDA9840_STADJ 0x03 3998c2ecf20Sopenharmony_ci#define TDA9840_TEST 0x04 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci#define TDA9840_MONO 0x10 4028c2ecf20Sopenharmony_ci#define TDA9840_STEREO 0x2a 4038c2ecf20Sopenharmony_ci#define TDA9840_DUALA 0x12 4048c2ecf20Sopenharmony_ci#define TDA9840_DUALB 0x1e 4058c2ecf20Sopenharmony_ci#define TDA9840_DUALAB 0x1a 4068c2ecf20Sopenharmony_ci#define TDA9840_DUALBA 0x16 4078c2ecf20Sopenharmony_ci#define TDA9840_EXTERNAL 0x7a 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */ 4108c2ecf20Sopenharmony_ci#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */ 4118c2ecf20Sopenharmony_ci#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */ 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ 4148c2ecf20Sopenharmony_ci#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int tda9840_getrxsubchans(struct CHIPSTATE *chip) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 4198c2ecf20Sopenharmony_ci int val, mode; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_MONO; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci val = chip_read(chip); 4248c2ecf20Sopenharmony_ci if (val < 0) 4258c2ecf20Sopenharmony_ci return mode; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (val & TDA9840_DS_DUAL) 4288c2ecf20Sopenharmony_ci mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 4298c2ecf20Sopenharmony_ci if (val & TDA9840_ST_STEREO) 4308c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_STEREO; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 4338c2ecf20Sopenharmony_ci "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n", 4348c2ecf20Sopenharmony_ci val, mode); 4358c2ecf20Sopenharmony_ci return mode; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci int update = 1; 4418c2ecf20Sopenharmony_ci int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci switch (mode) { 4448c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 4458c2ecf20Sopenharmony_ci t |= TDA9840_MONO; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 4488c2ecf20Sopenharmony_ci t |= TDA9840_STEREO; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 4518c2ecf20Sopenharmony_ci t |= TDA9840_DUALA; 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 4548c2ecf20Sopenharmony_ci t |= TDA9840_DUALB; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 4578c2ecf20Sopenharmony_ci t |= TDA9840_DUALAB; 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci default: 4608c2ecf20Sopenharmony_ci update = 0; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (update) 4648c2ecf20Sopenharmony_ci chip_write(chip, TDA9840_SW, t); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int tda9840_checkit(struct CHIPSTATE *chip) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci int rc; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci rc = chip_read(chip); 4728c2ecf20Sopenharmony_ci if (rc < 0) 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* lower 5 bits should be 0 */ 4778c2ecf20Sopenharmony_ci return ((rc & 0x1f) == 0) ? 1 : 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 4818c2ecf20Sopenharmony_ci/* audio chip descriptions - defines+functions for tda985x */ 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/* subaddresses for TDA9855 */ 4848c2ecf20Sopenharmony_ci#define TDA9855_VR 0x00 /* Volume, right */ 4858c2ecf20Sopenharmony_ci#define TDA9855_VL 0x01 /* Volume, left */ 4868c2ecf20Sopenharmony_ci#define TDA9855_BA 0x02 /* Bass */ 4878c2ecf20Sopenharmony_ci#define TDA9855_TR 0x03 /* Treble */ 4888c2ecf20Sopenharmony_ci#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci/* subaddresses for TDA9850 */ 4918c2ecf20Sopenharmony_ci#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */ 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* subaddesses for both chips */ 4948c2ecf20Sopenharmony_ci#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */ 4958c2ecf20Sopenharmony_ci#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */ 4968c2ecf20Sopenharmony_ci#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */ 4978c2ecf20Sopenharmony_ci#define TDA985x_A1 0x08 /* Alignment 1 for both chips */ 4988c2ecf20Sopenharmony_ci#define TDA985x_A2 0x09 /* Alignment 2 for both chips */ 4998c2ecf20Sopenharmony_ci#define TDA985x_A3 0x0a /* Alignment 3 for both chips */ 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* Masks for bits in TDA9855 subaddresses */ 5028c2ecf20Sopenharmony_ci/* 0x00 - VR in TDA9855 */ 5038c2ecf20Sopenharmony_ci/* 0x01 - VL in TDA9855 */ 5048c2ecf20Sopenharmony_ci/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) 5058c2ecf20Sopenharmony_ci * in 1dB steps - mute is 0x27 */ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/* 0x02 - BA in TDA9855 */ 5098c2ecf20Sopenharmony_ci/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) 5108c2ecf20Sopenharmony_ci * in .5dB steps - 0 is 0x0E */ 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/* 0x03 - TR in TDA9855 */ 5148c2ecf20Sopenharmony_ci/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) 5158c2ecf20Sopenharmony_ci * in 3dB steps - 0 is 0x7 */ 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/* Masks for bits in both chips' subaddresses */ 5188c2ecf20Sopenharmony_ci/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */ 5198c2ecf20Sopenharmony_ci/* Unique to TDA9855: */ 5208c2ecf20Sopenharmony_ci/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf) 5218c2ecf20Sopenharmony_ci * in 3dB steps - mute is 0x0 */ 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/* Unique to TDA9850: */ 5248c2ecf20Sopenharmony_ci/* lower 4 bits control stereo noise threshold, over which stereo turns off 5258c2ecf20Sopenharmony_ci * set to values of 0x00 through 0x0f for Ster1 through Ster16 */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/ 5298c2ecf20Sopenharmony_ci/* Unique to TDA9855: */ 5308c2ecf20Sopenharmony_ci#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ 5318c2ecf20Sopenharmony_ci#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ 5328c2ecf20Sopenharmony_ci#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ 5338c2ecf20Sopenharmony_ci#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ 5348c2ecf20Sopenharmony_ci /* Bits 0 to 3 select various combinations 5358c2ecf20Sopenharmony_ci * of line in and line out, only the 5368c2ecf20Sopenharmony_ci * interesting ones are defined */ 5378c2ecf20Sopenharmony_ci#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ 5388c2ecf20Sopenharmony_ci#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* Unique to TDA9850: */ 5418c2ecf20Sopenharmony_ci/* lower 4 bits control SAP noise threshold, over which SAP turns off 5428c2ecf20Sopenharmony_ci * set to values of 0x00 through 0x0f for SAP1 through SAP16 */ 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ 5468c2ecf20Sopenharmony_ci/* Common to TDA9855 and TDA9850: */ 5478c2ecf20Sopenharmony_ci#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ 5488c2ecf20Sopenharmony_ci#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */ 5498c2ecf20Sopenharmony_ci#define TDA985x_STEREO 1<<6 /* Selects Stereo output, mono if not received */ 5508c2ecf20Sopenharmony_ci#define TDA985x_MONO 0 /* Forces Mono output */ 5518c2ecf20Sopenharmony_ci#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/* Unique to TDA9855: */ 5548c2ecf20Sopenharmony_ci#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ 5558c2ecf20Sopenharmony_ci#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ 5568c2ecf20Sopenharmony_ci#define TDA9855_LINEAR 0 /* Linear Stereo */ 5578c2ecf20Sopenharmony_ci#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ 5588c2ecf20Sopenharmony_ci#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ 5598c2ecf20Sopenharmony_ci#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ 5608c2ecf20Sopenharmony_ci#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */ 5638c2ecf20Sopenharmony_ci/* Common to both TDA9855 and TDA9850: */ 5648c2ecf20Sopenharmony_ci/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) 5658c2ecf20Sopenharmony_ci * in .5dB steps - 0dB is 0x7 */ 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci/* 0x08, 0x09 - A1 and A2 (read/write) */ 5688c2ecf20Sopenharmony_ci/* Common to both TDA9855 and TDA9850: */ 5698c2ecf20Sopenharmony_ci/* lower 5 bites are wideband and spectral expander alignment 5708c2ecf20Sopenharmony_ci * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ 5718c2ecf20Sopenharmony_ci#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */ 5728c2ecf20Sopenharmony_ci#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ 5738c2ecf20Sopenharmony_ci#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci/* 0x0a - A3 */ 5768c2ecf20Sopenharmony_ci/* Common to both TDA9855 and TDA9850: */ 5778c2ecf20Sopenharmony_ci/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), 5788c2ecf20Sopenharmony_ci * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ 5798c2ecf20Sopenharmony_ci#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int tda9855_volume(int val) { return val/0x2e8+0x27; } 5828c2ecf20Sopenharmony_cistatic int tda9855_bass(int val) { return val/0xccc+0x06; } 5838c2ecf20Sopenharmony_cistatic int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int tda985x_getrxsubchans(struct CHIPSTATE *chip) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci int mode, val; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Add mono mode regardless of SAP and stereo */ 5908c2ecf20Sopenharmony_ci /* Allows forced mono */ 5918c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_MONO; 5928c2ecf20Sopenharmony_ci val = chip_read(chip); 5938c2ecf20Sopenharmony_ci if (val < 0) 5948c2ecf20Sopenharmony_ci return mode; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (val & TDA985x_STP) 5978c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_STEREO; 5988c2ecf20Sopenharmony_ci if (val & TDA985x_SAPP) 5998c2ecf20Sopenharmony_ci mode |= V4L2_TUNER_SUB_SAP; 6008c2ecf20Sopenharmony_ci return mode; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void tda985x_setaudmode(struct CHIPSTATE *chip, int mode) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int update = 1; 6068c2ecf20Sopenharmony_ci int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci switch (mode) { 6098c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 6108c2ecf20Sopenharmony_ci c6 |= TDA985x_MONO; 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 6138c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 6148c2ecf20Sopenharmony_ci c6 |= TDA985x_STEREO; 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_SAP: 6178c2ecf20Sopenharmony_ci c6 |= TDA985x_SAP; 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 6208c2ecf20Sopenharmony_ci c6 |= TDA985x_MONOSAP; 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci default: 6238c2ecf20Sopenharmony_ci update = 0; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci if (update) 6268c2ecf20Sopenharmony_ci chip_write(chip,TDA985x_C6,c6); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 6318c2ecf20Sopenharmony_ci/* audio chip descriptions - defines+functions for tda9873h */ 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci/* Subaddresses for TDA9873H */ 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci#define TDA9873_SW 0x00 /* Switching */ 6368c2ecf20Sopenharmony_ci#define TDA9873_AD 0x01 /* Adjust */ 6378c2ecf20Sopenharmony_ci#define TDA9873_PT 0x02 /* Port */ 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci/* Subaddress 0x00: Switching Data 6408c2ecf20Sopenharmony_ci * B7..B0: 6418c2ecf20Sopenharmony_ci * 6428c2ecf20Sopenharmony_ci * B1, B0: Input source selection 6438c2ecf20Sopenharmony_ci * 0, 0 internal 6448c2ecf20Sopenharmony_ci * 1, 0 external stereo 6458c2ecf20Sopenharmony_ci * 0, 1 external mono 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci#define TDA9873_INP_MASK 3 6488c2ecf20Sopenharmony_ci#define TDA9873_INTERNAL 0 6498c2ecf20Sopenharmony_ci#define TDA9873_EXT_STEREO 2 6508c2ecf20Sopenharmony_ci#define TDA9873_EXT_MONO 1 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/* B3, B2: output signal select 6538c2ecf20Sopenharmony_ci * B4 : transmission mode 6548c2ecf20Sopenharmony_ci * 0, 0, 1 Mono 6558c2ecf20Sopenharmony_ci * 1, 0, 0 Stereo 6568c2ecf20Sopenharmony_ci * 1, 1, 1 Stereo (reversed channel) 6578c2ecf20Sopenharmony_ci * 0, 0, 0 Dual AB 6588c2ecf20Sopenharmony_ci * 0, 0, 1 Dual AA 6598c2ecf20Sopenharmony_ci * 0, 1, 0 Dual BB 6608c2ecf20Sopenharmony_ci * 0, 1, 1 Dual BA 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci#define TDA9873_TR_MASK (7 << 2) 6648c2ecf20Sopenharmony_ci#define TDA9873_TR_MONO 4 6658c2ecf20Sopenharmony_ci#define TDA9873_TR_STEREO 1 << 4 6668c2ecf20Sopenharmony_ci#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2)) 6678c2ecf20Sopenharmony_ci#define TDA9873_TR_DUALA 1 << 2 6688c2ecf20Sopenharmony_ci#define TDA9873_TR_DUALB 1 << 3 6698c2ecf20Sopenharmony_ci#define TDA9873_TR_DUALAB 0 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci/* output level controls 6728c2ecf20Sopenharmony_ci * B5: output level switch (0 = reduced gain, 1 = normal gain) 6738c2ecf20Sopenharmony_ci * B6: mute (1 = muted) 6748c2ecf20Sopenharmony_ci * B7: auto-mute (1 = auto-mute enabled) 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci#define TDA9873_GAIN_NORMAL 1 << 5 6788c2ecf20Sopenharmony_ci#define TDA9873_MUTE 1 << 6 6798c2ecf20Sopenharmony_ci#define TDA9873_AUTOMUTE 1 << 7 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci/* Subaddress 0x01: Adjust/standard */ 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB) 6848c2ecf20Sopenharmony_ci * Recommended value is +0 dB 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */ 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* Bits C6..C4 control FM stantard 6908c2ecf20Sopenharmony_ci * C6, C5, C4 6918c2ecf20Sopenharmony_ci * 0, 0, 0 B/G (PAL FM) 6928c2ecf20Sopenharmony_ci * 0, 0, 1 M 6938c2ecf20Sopenharmony_ci * 0, 1, 0 D/K(1) 6948c2ecf20Sopenharmony_ci * 0, 1, 1 D/K(2) 6958c2ecf20Sopenharmony_ci * 1, 0, 0 D/K(3) 6968c2ecf20Sopenharmony_ci * 1, 0, 1 I 6978c2ecf20Sopenharmony_ci */ 6988c2ecf20Sopenharmony_ci#define TDA9873_BG 0 6998c2ecf20Sopenharmony_ci#define TDA9873_M 1 7008c2ecf20Sopenharmony_ci#define TDA9873_DK1 2 7018c2ecf20Sopenharmony_ci#define TDA9873_DK2 3 7028c2ecf20Sopenharmony_ci#define TDA9873_DK3 4 7038c2ecf20Sopenharmony_ci#define TDA9873_I 5 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci/* C7 controls identification response time (1=fast/0=normal) 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci#define TDA9873_IDR_NORM 0 7088c2ecf20Sopenharmony_ci#define TDA9873_IDR_FAST 1 << 7 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci/* Subaddress 0x02: Port data */ 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/* E1, E0 free programmable ports P1/P2 7148c2ecf20Sopenharmony_ci 0, 0 both ports low 7158c2ecf20Sopenharmony_ci 0, 1 P1 high 7168c2ecf20Sopenharmony_ci 1, 0 P2 high 7178c2ecf20Sopenharmony_ci 1, 1 both ports high 7188c2ecf20Sopenharmony_ci*/ 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci#define TDA9873_PORTS 3 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci/* E2: test port */ 7238c2ecf20Sopenharmony_ci#define TDA9873_TST_PORT 1 << 2 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/* E5..E3 control mono output channel (together with transmission mode bit B4) 7268c2ecf20Sopenharmony_ci * 7278c2ecf20Sopenharmony_ci * E5 E4 E3 B4 OUTM 7288c2ecf20Sopenharmony_ci * 0 0 0 0 mono 7298c2ecf20Sopenharmony_ci * 0 0 1 0 DUAL B 7308c2ecf20Sopenharmony_ci * 0 1 0 1 mono (from stereo decoder) 7318c2ecf20Sopenharmony_ci */ 7328c2ecf20Sopenharmony_ci#define TDA9873_MOUT_MONO 0 7338c2ecf20Sopenharmony_ci#define TDA9873_MOUT_FMONO 0 7348c2ecf20Sopenharmony_ci#define TDA9873_MOUT_DUALA 0 7358c2ecf20Sopenharmony_ci#define TDA9873_MOUT_DUALB 1 << 3 7368c2ecf20Sopenharmony_ci#define TDA9873_MOUT_ST 1 << 4 7378c2ecf20Sopenharmony_ci#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3)) 7388c2ecf20Sopenharmony_ci#define TDA9873_MOUT_EXTL 1 << 5 7398c2ecf20Sopenharmony_ci#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3)) 7408c2ecf20Sopenharmony_ci#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4)) 7418c2ecf20Sopenharmony_ci#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3)) 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/* Status bits: (chip read) */ 7448c2ecf20Sopenharmony_ci#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ 7458c2ecf20Sopenharmony_ci#define TDA9873_STEREO 2 /* Stereo sound is identified */ 7468c2ecf20Sopenharmony_ci#define TDA9873_DUAL 4 /* Dual sound is identified */ 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int tda9873_getrxsubchans(struct CHIPSTATE *chip) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 7518c2ecf20Sopenharmony_ci int val,mode; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_MONO; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci val = chip_read(chip); 7568c2ecf20Sopenharmony_ci if (val < 0) 7578c2ecf20Sopenharmony_ci return mode; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (val & TDA9873_STEREO) 7608c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_STEREO; 7618c2ecf20Sopenharmony_ci if (val & TDA9873_DUAL) 7628c2ecf20Sopenharmony_ci mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 7638c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 7648c2ecf20Sopenharmony_ci "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n", 7658c2ecf20Sopenharmony_ci val, mode); 7668c2ecf20Sopenharmony_ci return mode; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic void tda9873_setaudmode(struct CHIPSTATE *chip, int mode) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 7728c2ecf20Sopenharmony_ci int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; 7738c2ecf20Sopenharmony_ci /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { 7768c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 7778c2ecf20Sopenharmony_ci "tda9873_setaudmode(): external input\n"); 7788c2ecf20Sopenharmony_ci return; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 7828c2ecf20Sopenharmony_ci "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n", 7838c2ecf20Sopenharmony_ci TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); 7848c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n", 7858c2ecf20Sopenharmony_ci sw_data); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci switch (mode) { 7888c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 7898c2ecf20Sopenharmony_ci sw_data |= TDA9873_TR_MONO; 7908c2ecf20Sopenharmony_ci break; 7918c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 7928c2ecf20Sopenharmony_ci sw_data |= TDA9873_TR_STEREO; 7938c2ecf20Sopenharmony_ci break; 7948c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 7958c2ecf20Sopenharmony_ci sw_data |= TDA9873_TR_DUALA; 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 7988c2ecf20Sopenharmony_ci sw_data |= TDA9873_TR_DUALB; 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 8018c2ecf20Sopenharmony_ci sw_data |= TDA9873_TR_DUALAB; 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci default: 8048c2ecf20Sopenharmony_ci return; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci chip_write(chip, TDA9873_SW, sw_data); 8088c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 8098c2ecf20Sopenharmony_ci "tda9873_setaudmode(): req. mode %d; chip_write: %d\n", 8108c2ecf20Sopenharmony_ci mode, sw_data); 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int tda9873_checkit(struct CHIPSTATE *chip) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci int rc; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci rc = chip_read2(chip, 254); 8188c2ecf20Sopenharmony_ci if (rc < 0) 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci return (rc & ~0x1f) == 0x80; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 8258c2ecf20Sopenharmony_ci/* audio chip description - defines+functions for tda9874h and tda9874a */ 8268c2ecf20Sopenharmony_ci/* Dariusz Kowalewski <darekk@automex.pl> */ 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci/* Subaddresses for TDA9874H and TDA9874A (slave rx) */ 8298c2ecf20Sopenharmony_ci#define TDA9874A_AGCGR 0x00 /* AGC gain */ 8308c2ecf20Sopenharmony_ci#define TDA9874A_GCONR 0x01 /* general config */ 8318c2ecf20Sopenharmony_ci#define TDA9874A_MSR 0x02 /* monitor select */ 8328c2ecf20Sopenharmony_ci#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */ 8338c2ecf20Sopenharmony_ci#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */ 8348c2ecf20Sopenharmony_ci#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */ 8358c2ecf20Sopenharmony_ci#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */ 8368c2ecf20Sopenharmony_ci#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */ 8378c2ecf20Sopenharmony_ci#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */ 8388c2ecf20Sopenharmony_ci#define TDA9874A_DCR 0x09 /* demodulator config */ 8398c2ecf20Sopenharmony_ci#define TDA9874A_FMER 0x0a /* FM de-emphasis */ 8408c2ecf20Sopenharmony_ci#define TDA9874A_FMMR 0x0b /* FM dematrix */ 8418c2ecf20Sopenharmony_ci#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */ 8428c2ecf20Sopenharmony_ci#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */ 8438c2ecf20Sopenharmony_ci#define TDA9874A_NCONR 0x0e /* NICAM config */ 8448c2ecf20Sopenharmony_ci#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */ 8458c2ecf20Sopenharmony_ci#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */ 8468c2ecf20Sopenharmony_ci#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */ 8478c2ecf20Sopenharmony_ci#define TDA9874A_AMCONR 0x12 /* audio mute control */ 8488c2ecf20Sopenharmony_ci#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */ 8498c2ecf20Sopenharmony_ci#define TDA9874A_AOSR 0x14 /* analog output select */ 8508c2ecf20Sopenharmony_ci#define TDA9874A_DAICONR 0x15 /* digital audio interface config */ 8518c2ecf20Sopenharmony_ci#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */ 8528c2ecf20Sopenharmony_ci#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */ 8538c2ecf20Sopenharmony_ci#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */ 8548c2ecf20Sopenharmony_ci#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */ 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci/* Subaddresses for TDA9874H and TDA9874A (slave tx) */ 8578c2ecf20Sopenharmony_ci#define TDA9874A_DSR 0x00 /* device status */ 8588c2ecf20Sopenharmony_ci#define TDA9874A_NSR 0x01 /* NICAM status */ 8598c2ecf20Sopenharmony_ci#define TDA9874A_NECR 0x02 /* NICAM error count */ 8608c2ecf20Sopenharmony_ci#define TDA9874A_DR1 0x03 /* add. data LSB */ 8618c2ecf20Sopenharmony_ci#define TDA9874A_DR2 0x04 /* add. data MSB */ 8628c2ecf20Sopenharmony_ci#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */ 8638c2ecf20Sopenharmony_ci#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */ 8648c2ecf20Sopenharmony_ci#define TDA9874A_SIFLR 0x07 /* SIF level */ 8658c2ecf20Sopenharmony_ci#define TDA9874A_TR2 252 /* test reg. 2 */ 8668c2ecf20Sopenharmony_ci#define TDA9874A_TR1 253 /* test reg. 1 */ 8678c2ecf20Sopenharmony_ci#define TDA9874A_DIC 254 /* device id. code */ 8688c2ecf20Sopenharmony_ci#define TDA9874A_SIC 255 /* software id. code */ 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int tda9874a_mode = 1; /* 0: A2, 1: NICAM */ 8728c2ecf20Sopenharmony_cistatic int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */ 8738c2ecf20Sopenharmony_cistatic int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */ 8748c2ecf20Sopenharmony_cistatic int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */ 8758c2ecf20Sopenharmony_cistatic int tda9874a_dic = -1; /* device id. code */ 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci/* insmod options for tda9874a */ 8788c2ecf20Sopenharmony_cistatic unsigned int tda9874a_SIF = UNSET; 8798c2ecf20Sopenharmony_cistatic unsigned int tda9874a_AMSEL = UNSET; 8808c2ecf20Sopenharmony_cistatic unsigned int tda9874a_STD = UNSET; 8818c2ecf20Sopenharmony_cimodule_param(tda9874a_SIF, int, 0444); 8828c2ecf20Sopenharmony_cimodule_param(tda9874a_AMSEL, int, 0444); 8838c2ecf20Sopenharmony_cimodule_param(tda9874a_STD, int, 0444); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci/* 8868c2ecf20Sopenharmony_ci * initialization table for tda9874 decoder: 8878c2ecf20Sopenharmony_ci * - carrier 1 freq. registers (3 bytes) 8888c2ecf20Sopenharmony_ci * - carrier 2 freq. registers (3 bytes) 8898c2ecf20Sopenharmony_ci * - demudulator config register 8908c2ecf20Sopenharmony_ci * - FM de-emphasis register (slow identification mode) 8918c2ecf20Sopenharmony_ci * Note: frequency registers must be written in single i2c transfer. 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_cistatic struct tda9874a_MODES { 8948c2ecf20Sopenharmony_ci char *name; 8958c2ecf20Sopenharmony_ci audiocmd cmd; 8968c2ecf20Sopenharmony_ci} tda9874a_modelist[9] = { 8978c2ecf20Sopenharmony_ci { "A2, B/G", /* default */ 8988c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, 8998c2ecf20Sopenharmony_ci { "A2, M (Korea)", 9008c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, 9018c2ecf20Sopenharmony_ci { "A2, D/K (1)", 9028c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} }, 9038c2ecf20Sopenharmony_ci { "A2, D/K (2)", 9048c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} }, 9058c2ecf20Sopenharmony_ci { "A2, D/K (3)", 9068c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} }, 9078c2ecf20Sopenharmony_ci { "NICAM, I", 9088c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, 9098c2ecf20Sopenharmony_ci { "NICAM, B/G", 9108c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, 9118c2ecf20Sopenharmony_ci { "NICAM, D/K", 9128c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, 9138c2ecf20Sopenharmony_ci { "NICAM, L", 9148c2ecf20Sopenharmony_ci { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } 9158c2ecf20Sopenharmony_ci}; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic int tda9874a_setup(struct CHIPSTATE *chip) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ 9228c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); 9238c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); 9248c2ecf20Sopenharmony_ci if(tda9874a_dic == 0x11) { 9258c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_FMMR, 0x80); 9268c2ecf20Sopenharmony_ci } else { /* dic == 0x07 */ 9278c2ecf20Sopenharmony_ci chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd); 9288c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_FMMR, 0x00); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */ 9318c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */ 9328c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); 9338c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */ 9348c2ecf20Sopenharmony_ci /* Note: If signal quality is poor you may want to change NICAM */ 9358c2ecf20Sopenharmony_ci /* error limit registers (NLELR and NUELR) to some greater values. */ 9368c2ecf20Sopenharmony_ci /* Then the sound would remain stereo, but won't be so clear. */ 9378c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_NLELR, 0x14); /* default */ 9388c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_NUELR, 0x50); /* default */ 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if(tda9874a_dic == 0x11) { 9418c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_AMCONR, 0xf9); 9428c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); 9438c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_AOSR, 0x80); 9448c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); 9458c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_ESP, tda9874a_ESP); 9468c2ecf20Sopenharmony_ci } else { /* dic == 0x07 */ 9478c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_AMCONR, 0xfb); 9488c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); 9498c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", 9528c2ecf20Sopenharmony_ci tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); 9538c2ecf20Sopenharmony_ci return 1; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic int tda9874a_getrxsubchans(struct CHIPSTATE *chip) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 9598c2ecf20Sopenharmony_ci int dsr,nsr,mode; 9608c2ecf20Sopenharmony_ci int necr; /* just for debugging */ 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_MONO; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci dsr = chip_read2(chip, TDA9874A_DSR); 9658c2ecf20Sopenharmony_ci if (dsr < 0) 9668c2ecf20Sopenharmony_ci return mode; 9678c2ecf20Sopenharmony_ci nsr = chip_read2(chip, TDA9874A_NSR); 9688c2ecf20Sopenharmony_ci if (nsr < 0) 9698c2ecf20Sopenharmony_ci return mode; 9708c2ecf20Sopenharmony_ci necr = chip_read2(chip, TDA9874A_NECR); 9718c2ecf20Sopenharmony_ci if (necr < 0) 9728c2ecf20Sopenharmony_ci return mode; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* need to store dsr/nsr somewhere */ 9758c2ecf20Sopenharmony_ci chip->shadow.bytes[MAXREGS-2] = dsr; 9768c2ecf20Sopenharmony_ci chip->shadow.bytes[MAXREGS-1] = nsr; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if(tda9874a_mode) { 9798c2ecf20Sopenharmony_ci /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked. 9808c2ecf20Sopenharmony_ci * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates 9818c2ecf20Sopenharmony_ci * that sound has (temporarily) switched from NICAM to 9828c2ecf20Sopenharmony_ci * mono FM (or AM) on 1st sound carrier due to high NICAM bit 9838c2ecf20Sopenharmony_ci * error count. So in fact there is no stereo in this case :-( 9848c2ecf20Sopenharmony_ci * But changing the mode to V4L2_TUNER_MODE_MONO would switch 9858c2ecf20Sopenharmony_ci * external 4052 multiplexer in audio_hook(). 9868c2ecf20Sopenharmony_ci */ 9878c2ecf20Sopenharmony_ci if(nsr & 0x02) /* NSR.S/MB=1 */ 9888c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_STEREO; 9898c2ecf20Sopenharmony_ci if(nsr & 0x01) /* NSR.D/SB=1 */ 9908c2ecf20Sopenharmony_ci mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 9918c2ecf20Sopenharmony_ci } else { 9928c2ecf20Sopenharmony_ci if(dsr & 0x02) /* DSR.IDSTE=1 */ 9938c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_STEREO; 9948c2ecf20Sopenharmony_ci if(dsr & 0x04) /* DSR.IDDUA=1 */ 9958c2ecf20Sopenharmony_ci mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 9998c2ecf20Sopenharmony_ci "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", 10008c2ecf20Sopenharmony_ci dsr, nsr, necr, mode); 10018c2ecf20Sopenharmony_ci return mode; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ 10098c2ecf20Sopenharmony_ci /* If auto-muting is disabled, we can hear a signal of degrading quality. */ 10108c2ecf20Sopenharmony_ci if (tda9874a_mode) { 10118c2ecf20Sopenharmony_ci if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ 10128c2ecf20Sopenharmony_ci tda9874a_NCONR &= 0xfe; /* enable */ 10138c2ecf20Sopenharmony_ci else 10148c2ecf20Sopenharmony_ci tda9874a_NCONR |= 0x01; /* disable */ 10158c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Note: TDA9874A supports automatic FM dematrixing (FMMR register) 10198c2ecf20Sopenharmony_ci * and has auto-select function for audio output (AOSR register). 10208c2ecf20Sopenharmony_ci * Old TDA9874H doesn't support these features. 10218c2ecf20Sopenharmony_ci * TDA9874A also has additional mono output pin (OUTM), which 10228c2ecf20Sopenharmony_ci * on same (all?) tv-cards is not used, anyway (as well as MONOIN). 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ci if(tda9874a_dic == 0x11) { 10258c2ecf20Sopenharmony_ci int aosr = 0x80; 10268c2ecf20Sopenharmony_ci int mdacosr = (tda9874a_mode) ? 0x82:0x80; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci switch(mode) { 10298c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 10308c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 10338c2ecf20Sopenharmony_ci aosr = 0x80; /* auto-select, dual A/A */ 10348c2ecf20Sopenharmony_ci mdacosr = (tda9874a_mode) ? 0x82:0x80; 10358c2ecf20Sopenharmony_ci break; 10368c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 10378c2ecf20Sopenharmony_ci aosr = 0xa0; /* auto-select, dual B/B */ 10388c2ecf20Sopenharmony_ci mdacosr = (tda9874a_mode) ? 0x83:0x81; 10398c2ecf20Sopenharmony_ci break; 10408c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 10418c2ecf20Sopenharmony_ci aosr = 0x00; /* always route L to L and R to R */ 10428c2ecf20Sopenharmony_ci mdacosr = (tda9874a_mode) ? 0x82:0x80; 10438c2ecf20Sopenharmony_ci break; 10448c2ecf20Sopenharmony_ci default: 10458c2ecf20Sopenharmony_ci return; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_AOSR, aosr); 10488c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_MDACOSR, mdacosr); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 10518c2ecf20Sopenharmony_ci "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", 10528c2ecf20Sopenharmony_ci mode, aosr, mdacosr); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci } else { /* dic == 0x07 */ 10558c2ecf20Sopenharmony_ci int fmmr,aosr; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci switch(mode) { 10588c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 10598c2ecf20Sopenharmony_ci fmmr = 0x00; /* mono */ 10608c2ecf20Sopenharmony_ci aosr = 0x10; /* A/A */ 10618c2ecf20Sopenharmony_ci break; 10628c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 10638c2ecf20Sopenharmony_ci if(tda9874a_mode) { 10648c2ecf20Sopenharmony_ci fmmr = 0x00; 10658c2ecf20Sopenharmony_ci aosr = 0x00; /* handled by NICAM auto-mute */ 10668c2ecf20Sopenharmony_ci } else { 10678c2ecf20Sopenharmony_ci fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */ 10688c2ecf20Sopenharmony_ci aosr = 0x00; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci break; 10718c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 10728c2ecf20Sopenharmony_ci fmmr = 0x02; /* dual */ 10738c2ecf20Sopenharmony_ci aosr = 0x10; /* dual A/A */ 10748c2ecf20Sopenharmony_ci break; 10758c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 10768c2ecf20Sopenharmony_ci fmmr = 0x02; /* dual */ 10778c2ecf20Sopenharmony_ci aosr = 0x20; /* dual B/B */ 10788c2ecf20Sopenharmony_ci break; 10798c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 10808c2ecf20Sopenharmony_ci fmmr = 0x02; /* dual */ 10818c2ecf20Sopenharmony_ci aosr = 0x00; /* dual A/B */ 10828c2ecf20Sopenharmony_ci break; 10838c2ecf20Sopenharmony_ci default: 10848c2ecf20Sopenharmony_ci return; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_FMMR, fmmr); 10878c2ecf20Sopenharmony_ci chip_write(chip, TDA9874A_AOSR, aosr); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, 10908c2ecf20Sopenharmony_ci "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", 10918c2ecf20Sopenharmony_ci mode, fmmr, aosr); 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int tda9874a_checkit(struct CHIPSTATE *chip) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 10988c2ecf20Sopenharmony_ci int dic,sic; /* device id. and software id. codes */ 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci dic = chip_read2(chip, TDA9874A_DIC); 11018c2ecf20Sopenharmony_ci if (dic < 0) 11028c2ecf20Sopenharmony_ci return 0; 11038c2ecf20Sopenharmony_ci sic = chip_read2(chip, TDA9874A_SIC); 11048c2ecf20Sopenharmony_ci if (sic < 0) 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if((dic == 0x11)||(dic == 0x07)) { 11108c2ecf20Sopenharmony_ci v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); 11118c2ecf20Sopenharmony_ci tda9874a_dic = dic; /* remember device id. */ 11128c2ecf20Sopenharmony_ci return 1; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci return 0; /* not found */ 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int tda9874a_initialize(struct CHIPSTATE *chip) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci if (tda9874a_SIF > 2) 11208c2ecf20Sopenharmony_ci tda9874a_SIF = 1; 11218c2ecf20Sopenharmony_ci if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) 11228c2ecf20Sopenharmony_ci tda9874a_STD = 0; 11238c2ecf20Sopenharmony_ci if(tda9874a_AMSEL > 1) 11248c2ecf20Sopenharmony_ci tda9874a_AMSEL = 0; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if(tda9874a_SIF == 1) 11278c2ecf20Sopenharmony_ci tda9874a_GCONR = 0xc0; /* sound IF input 1 */ 11288c2ecf20Sopenharmony_ci else 11298c2ecf20Sopenharmony_ci tda9874a_GCONR = 0xc1; /* sound IF input 2 */ 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci tda9874a_ESP = tda9874a_STD; 11328c2ecf20Sopenharmony_ci tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if(tda9874a_AMSEL == 0) 11358c2ecf20Sopenharmony_ci tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ 11368c2ecf20Sopenharmony_ci else 11378c2ecf20Sopenharmony_ci tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci tda9874a_setup(chip); 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 11448c2ecf20Sopenharmony_ci/* audio chip description - defines+functions for tda9875 */ 11458c2ecf20Sopenharmony_ci/* The TDA9875 is made by Philips Semiconductor 11468c2ecf20Sopenharmony_ci * http://www.semiconductors.philips.com 11478c2ecf20Sopenharmony_ci * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator 11488c2ecf20Sopenharmony_ci * 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci/* subaddresses for TDA9875 */ 11528c2ecf20Sopenharmony_ci#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ 11538c2ecf20Sopenharmony_ci#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ 11548c2ecf20Sopenharmony_ci#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ 11558c2ecf20Sopenharmony_ci#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ 11588c2ecf20Sopenharmony_ci#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ 11598c2ecf20Sopenharmony_ci#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ 11608c2ecf20Sopenharmony_ci#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ 11638c2ecf20Sopenharmony_ci#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ 11648c2ecf20Sopenharmony_ci#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ 11658c2ecf20Sopenharmony_ci#define TDA9875_MVL 0x1a /* Main volume gauche */ 11668c2ecf20Sopenharmony_ci#define TDA9875_MVR 0x1b /* Main volume droite */ 11678c2ecf20Sopenharmony_ci#define TDA9875_MBA 0x1d /* Main Basse */ 11688c2ecf20Sopenharmony_ci#define TDA9875_MTR 0x1e /* Main treble */ 11698c2ecf20Sopenharmony_ci#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/ 11708c2ecf20Sopenharmony_ci#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */ 11718c2ecf20Sopenharmony_ci#define TDA9875_AVR 0x21 /* Auxiliary volume droite */ 11728c2ecf20Sopenharmony_ci#define TDA9875_ABA 0x22 /* Auxiliary Basse */ 11738c2ecf20Sopenharmony_ci#define TDA9875_ATR 0x23 /* Auxiliary treble */ 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci#define TDA9875_MSR 0x02 /* Monitor select register */ 11768c2ecf20Sopenharmony_ci#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ 11778c2ecf20Sopenharmony_ci#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ 11788c2ecf20Sopenharmony_ci#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ 11798c2ecf20Sopenharmony_ci#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ 11808c2ecf20Sopenharmony_ci#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ 11818c2ecf20Sopenharmony_ci#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ 11828c2ecf20Sopenharmony_ci#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ 11838c2ecf20Sopenharmony_ci#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ 11848c2ecf20Sopenharmony_ci#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci/* values */ 11878c2ecf20Sopenharmony_ci#define TDA9875_MUTE_ON 0xff /* general mute */ 11888c2ecf20Sopenharmony_ci#define TDA9875_MUTE_OFF 0xcc /* general no mute */ 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cistatic int tda9875_initialize(struct CHIPSTATE *chip) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ 11938c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ 11948c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ 11958c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ 11968c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ 11978c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ 11988c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ 11998c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ 12008c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ 12018c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ 12028c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ 12038c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ 12048c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ 12078c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ 12088c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ 12098c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ 12108c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ 12118c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ 12128c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ 12138c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ 12148c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ 12158c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ 12168c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ 12178c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ 12188c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ 12198c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ 12208c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ 12218c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } 12288c2ecf20Sopenharmony_cistatic int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } 12298c2ecf20Sopenharmony_cistatic int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci/* *********************** * 12358c2ecf20Sopenharmony_ci * i2c interface functions * 12368c2ecf20Sopenharmony_ci * *********************** */ 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic int tda9875_checkit(struct CHIPSTATE *chip) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 12418c2ecf20Sopenharmony_ci int dic, rev; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci dic = chip_read2(chip, 254); 12448c2ecf20Sopenharmony_ci if (dic < 0) 12458c2ecf20Sopenharmony_ci return 0; 12468c2ecf20Sopenharmony_ci rev = chip_read2(chip, 255); 12478c2ecf20Sopenharmony_ci if (rev < 0) 12488c2ecf20Sopenharmony_ci return 0; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ 12518c2ecf20Sopenharmony_ci v4l2_info(sd, "found tda9875%s rev. %d.\n", 12528c2ecf20Sopenharmony_ci dic == 0 ? "" : "A", rev); 12538c2ecf20Sopenharmony_ci return 1; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci return 0; 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 12598c2ecf20Sopenharmony_ci/* audio chip descriptions - defines+functions for tea6420 */ 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci#define TEA6300_VL 0x00 /* volume left */ 12628c2ecf20Sopenharmony_ci#define TEA6300_VR 0x01 /* volume right */ 12638c2ecf20Sopenharmony_ci#define TEA6300_BA 0x02 /* bass */ 12648c2ecf20Sopenharmony_ci#define TEA6300_TR 0x03 /* treble */ 12658c2ecf20Sopenharmony_ci#define TEA6300_FA 0x04 /* fader control */ 12668c2ecf20Sopenharmony_ci#define TEA6300_S 0x05 /* switch register */ 12678c2ecf20Sopenharmony_ci /* values for those registers: */ 12688c2ecf20Sopenharmony_ci#define TEA6300_S_SA 0x01 /* stereo A input */ 12698c2ecf20Sopenharmony_ci#define TEA6300_S_SB 0x02 /* stereo B */ 12708c2ecf20Sopenharmony_ci#define TEA6300_S_SC 0x04 /* stereo C */ 12718c2ecf20Sopenharmony_ci#define TEA6300_S_GMU 0x80 /* general mute */ 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */ 12748c2ecf20Sopenharmony_ci#define TEA6320_FFR 0x01 /* fader front right (0-5) */ 12758c2ecf20Sopenharmony_ci#define TEA6320_FFL 0x02 /* fader front left (0-5) */ 12768c2ecf20Sopenharmony_ci#define TEA6320_FRR 0x03 /* fader rear right (0-5) */ 12778c2ecf20Sopenharmony_ci#define TEA6320_FRL 0x04 /* fader rear left (0-5) */ 12788c2ecf20Sopenharmony_ci#define TEA6320_BA 0x05 /* bass (0-4) */ 12798c2ecf20Sopenharmony_ci#define TEA6320_TR 0x06 /* treble (0-4) */ 12808c2ecf20Sopenharmony_ci#define TEA6320_S 0x07 /* switch register */ 12818c2ecf20Sopenharmony_ci /* values for those registers: */ 12828c2ecf20Sopenharmony_ci#define TEA6320_S_SA 0x07 /* stereo A input */ 12838c2ecf20Sopenharmony_ci#define TEA6320_S_SB 0x06 /* stereo B */ 12848c2ecf20Sopenharmony_ci#define TEA6320_S_SC 0x05 /* stereo C */ 12858c2ecf20Sopenharmony_ci#define TEA6320_S_SD 0x04 /* stereo D */ 12868c2ecf20Sopenharmony_ci#define TEA6320_S_GMU 0x80 /* general mute */ 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci#define TEA6420_S_SA 0x00 /* stereo A input */ 12898c2ecf20Sopenharmony_ci#define TEA6420_S_SB 0x01 /* stereo B */ 12908c2ecf20Sopenharmony_ci#define TEA6420_S_SC 0x02 /* stereo C */ 12918c2ecf20Sopenharmony_ci#define TEA6420_S_SD 0x03 /* stereo D */ 12928c2ecf20Sopenharmony_ci#define TEA6420_S_SE 0x04 /* stereo E */ 12938c2ecf20Sopenharmony_ci#define TEA6420_S_GMU 0x05 /* general mute */ 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_cistatic int tea6300_shift10(int val) { return val >> 10; } 12968c2ecf20Sopenharmony_cistatic int tea6300_shift12(int val) { return val >> 12; } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */ 12998c2ecf20Sopenharmony_ci/* 0x0c mirror those immediately higher) */ 13008c2ecf20Sopenharmony_cistatic int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; } 13018c2ecf20Sopenharmony_cistatic int tea6320_shift11(int val) { return val >> 11; } 13028c2ecf20Sopenharmony_cistatic int tea6320_initialize(struct CHIPSTATE * chip) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci chip_write(chip, TEA6320_FFR, 0x3f); 13058c2ecf20Sopenharmony_ci chip_write(chip, TEA6320_FFL, 0x3f); 13068c2ecf20Sopenharmony_ci chip_write(chip, TEA6320_FRR, 0x3f); 13078c2ecf20Sopenharmony_ci chip_write(chip, TEA6320_FRL, 0x3f); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 13148c2ecf20Sopenharmony_ci/* audio chip descriptions - defines+functions for tda8425 */ 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci#define TDA8425_VL 0x00 /* volume left */ 13178c2ecf20Sopenharmony_ci#define TDA8425_VR 0x01 /* volume right */ 13188c2ecf20Sopenharmony_ci#define TDA8425_BA 0x02 /* bass */ 13198c2ecf20Sopenharmony_ci#define TDA8425_TR 0x03 /* treble */ 13208c2ecf20Sopenharmony_ci#define TDA8425_S1 0x08 /* switch functions */ 13218c2ecf20Sopenharmony_ci /* values for those registers: */ 13228c2ecf20Sopenharmony_ci#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ 13238c2ecf20Sopenharmony_ci#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */ 13248c2ecf20Sopenharmony_ci#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */ 13258c2ecf20Sopenharmony_ci#define TDA8425_S1_MU 0x20 /* mute bit */ 13268c2ecf20Sopenharmony_ci#define TDA8425_S1_STEREO 0x18 /* stereo bits */ 13278c2ecf20Sopenharmony_ci#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */ 13288c2ecf20Sopenharmony_ci#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */ 13298c2ecf20Sopenharmony_ci#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */ 13308c2ecf20Sopenharmony_ci#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */ 13318c2ecf20Sopenharmony_ci#define TDA8425_S1_ML 0x06 /* language selector */ 13328c2ecf20Sopenharmony_ci#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */ 13338c2ecf20Sopenharmony_ci#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */ 13348c2ecf20Sopenharmony_ci#define TDA8425_S1_ML_STEREO 0x06 /* stereo */ 13358c2ecf20Sopenharmony_ci#define TDA8425_S1_IS 0x01 /* channel selector */ 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_cistatic int tda8425_shift10(int val) { return (val >> 10) | 0xc0; } 13398c2ecf20Sopenharmony_cistatic int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cistatic void tda8425_setaudmode(struct CHIPSTATE *chip, int mode) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci switch (mode) { 13468c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 13478c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_ML_SOUND_A; 13488c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_STEREO_PSEUDO; 13498c2ecf20Sopenharmony_ci break; 13508c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 13518c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_ML_SOUND_B; 13528c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_STEREO_PSEUDO; 13538c2ecf20Sopenharmony_ci break; 13548c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 13558c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_ML_STEREO; 13568c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_STEREO_LINEAR; 13578c2ecf20Sopenharmony_ci break; 13588c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 13598c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_ML_STEREO; 13608c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_STEREO_MONO; 13618c2ecf20Sopenharmony_ci break; 13628c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 13638c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_ML_STEREO; 13648c2ecf20Sopenharmony_ci s1 |= TDA8425_S1_STEREO_SPATIAL; 13658c2ecf20Sopenharmony_ci break; 13668c2ecf20Sopenharmony_ci default: 13678c2ecf20Sopenharmony_ci return; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci chip_write(chip,TDA8425_S1,s1); 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 13748c2ecf20Sopenharmony_ci/* audio chip descriptions - defines+functions for pic16c54 (PV951) */ 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci/* the registers of 16C54, I2C sub address. */ 13778c2ecf20Sopenharmony_ci#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */ 13788c2ecf20Sopenharmony_ci#define PIC16C54_REG_MISC 0x02 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci/* bit definition of the RESET register, I2C data. */ 13818c2ecf20Sopenharmony_ci#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ 13828c2ecf20Sopenharmony_ci /* code of remote controller */ 13838c2ecf20Sopenharmony_ci#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */ 13848c2ecf20Sopenharmony_ci#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */ 13858c2ecf20Sopenharmony_ci#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */ 13868c2ecf20Sopenharmony_ci#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */ 13878c2ecf20Sopenharmony_ci#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */ 13888c2ecf20Sopenharmony_ci#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ 13898c2ecf20Sopenharmony_ci#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 13928c2ecf20Sopenharmony_ci/* audio chip descriptions - defines+functions for TA8874Z */ 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci/* write 1st byte */ 13958c2ecf20Sopenharmony_ci#define TA8874Z_LED_STE 0x80 13968c2ecf20Sopenharmony_ci#define TA8874Z_LED_BIL 0x40 13978c2ecf20Sopenharmony_ci#define TA8874Z_LED_EXT 0x20 13988c2ecf20Sopenharmony_ci#define TA8874Z_MONO_SET 0x10 13998c2ecf20Sopenharmony_ci#define TA8874Z_MUTE 0x08 14008c2ecf20Sopenharmony_ci#define TA8874Z_F_MONO 0x04 14018c2ecf20Sopenharmony_ci#define TA8874Z_MODE_SUB 0x02 14028c2ecf20Sopenharmony_ci#define TA8874Z_MODE_MAIN 0x01 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci/* write 2nd byte */ 14058c2ecf20Sopenharmony_ci/*#define TA8874Z_TI 0x80 */ /* test mode */ 14068c2ecf20Sopenharmony_ci#define TA8874Z_SEPARATION 0x3f 14078c2ecf20Sopenharmony_ci#define TA8874Z_SEPARATION_DEFAULT 0x10 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci/* read */ 14108c2ecf20Sopenharmony_ci#define TA8874Z_B1 0x80 14118c2ecf20Sopenharmony_ci#define TA8874Z_B0 0x40 14128c2ecf20Sopenharmony_ci#define TA8874Z_CHAG_FLAG 0x20 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci/* 14158c2ecf20Sopenharmony_ci * B1 B0 14168c2ecf20Sopenharmony_ci * mono L H 14178c2ecf20Sopenharmony_ci * stereo L L 14188c2ecf20Sopenharmony_ci * BIL H L 14198c2ecf20Sopenharmony_ci */ 14208c2ecf20Sopenharmony_cistatic int ta8874z_getrxsubchans(struct CHIPSTATE *chip) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci int val, mode; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_MONO; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci val = chip_read(chip); 14278c2ecf20Sopenharmony_ci if (val < 0) 14288c2ecf20Sopenharmony_ci return mode; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci if (val & TA8874Z_B1){ 14318c2ecf20Sopenharmony_ci mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 14328c2ecf20Sopenharmony_ci }else if (!(val & TA8874Z_B0)){ 14338c2ecf20Sopenharmony_ci mode = V4L2_TUNER_SUB_STEREO; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci /* v4l2_dbg(1, debug, &chip->sd, 14368c2ecf20Sopenharmony_ci "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n", 14378c2ecf20Sopenharmony_ci val, mode); */ 14388c2ecf20Sopenharmony_ci return mode; 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_cistatic audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; 14428c2ecf20Sopenharmony_cistatic audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; 14438c2ecf20Sopenharmony_cistatic audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; 14448c2ecf20Sopenharmony_cistatic audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; 14458c2ecf20Sopenharmony_cistatic audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &chip->sd; 14508c2ecf20Sopenharmony_ci int update = 1; 14518c2ecf20Sopenharmony_ci audiocmd *t = NULL; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode); 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci switch(mode){ 14568c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 14578c2ecf20Sopenharmony_ci t = &ta8874z_mono; 14588c2ecf20Sopenharmony_ci break; 14598c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 14608c2ecf20Sopenharmony_ci t = &ta8874z_stereo; 14618c2ecf20Sopenharmony_ci break; 14628c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 14638c2ecf20Sopenharmony_ci t = &ta8874z_main; 14648c2ecf20Sopenharmony_ci break; 14658c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 14668c2ecf20Sopenharmony_ci t = &ta8874z_sub; 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 14698c2ecf20Sopenharmony_ci t = &ta8874z_both; 14708c2ecf20Sopenharmony_ci break; 14718c2ecf20Sopenharmony_ci default: 14728c2ecf20Sopenharmony_ci update = 0; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci if(update) 14768c2ecf20Sopenharmony_ci chip_cmd(chip, "TA8874Z", t); 14778c2ecf20Sopenharmony_ci} 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_cistatic int ta8874z_checkit(struct CHIPSTATE *chip) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci int rc; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci rc = chip_read(chip); 14848c2ecf20Sopenharmony_ci if (rc < 0) 14858c2ecf20Sopenharmony_ci return rc; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci return ((rc & 0x1f) == 0x1f) ? 1 : 0; 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 14918c2ecf20Sopenharmony_ci/* audio chip descriptions - struct CHIPDESC */ 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci/* insmod options to enable/disable individual audio chips */ 14948c2ecf20Sopenharmony_cistatic int tda8425 = 1; 14958c2ecf20Sopenharmony_cistatic int tda9840 = 1; 14968c2ecf20Sopenharmony_cistatic int tda9850 = 1; 14978c2ecf20Sopenharmony_cistatic int tda9855 = 1; 14988c2ecf20Sopenharmony_cistatic int tda9873 = 1; 14998c2ecf20Sopenharmony_cistatic int tda9874a = 1; 15008c2ecf20Sopenharmony_cistatic int tda9875 = 1; 15018c2ecf20Sopenharmony_cistatic int tea6300; /* default 0 - address clash with msp34xx */ 15028c2ecf20Sopenharmony_cistatic int tea6320; /* default 0 - address clash with msp34xx */ 15038c2ecf20Sopenharmony_cistatic int tea6420 = 1; 15048c2ecf20Sopenharmony_cistatic int pic16c54 = 1; 15058c2ecf20Sopenharmony_cistatic int ta8874z; /* default 0 - address clash with tda9840 */ 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_cimodule_param(tda8425, int, 0444); 15088c2ecf20Sopenharmony_cimodule_param(tda9840, int, 0444); 15098c2ecf20Sopenharmony_cimodule_param(tda9850, int, 0444); 15108c2ecf20Sopenharmony_cimodule_param(tda9855, int, 0444); 15118c2ecf20Sopenharmony_cimodule_param(tda9873, int, 0444); 15128c2ecf20Sopenharmony_cimodule_param(tda9874a, int, 0444); 15138c2ecf20Sopenharmony_cimodule_param(tda9875, int, 0444); 15148c2ecf20Sopenharmony_cimodule_param(tea6300, int, 0444); 15158c2ecf20Sopenharmony_cimodule_param(tea6320, int, 0444); 15168c2ecf20Sopenharmony_cimodule_param(tea6420, int, 0444); 15178c2ecf20Sopenharmony_cimodule_param(pic16c54, int, 0444); 15188c2ecf20Sopenharmony_cimodule_param(ta8874z, int, 0444); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_cistatic struct CHIPDESC chiplist[] = { 15218c2ecf20Sopenharmony_ci { 15228c2ecf20Sopenharmony_ci .name = "tda9840", 15238c2ecf20Sopenharmony_ci .insmodopt = &tda9840, 15248c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA9840 >> 1, 15258c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA9840 >> 1, 15268c2ecf20Sopenharmony_ci .registers = 5, 15278c2ecf20Sopenharmony_ci .flags = CHIP_NEED_CHECKMODE, 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci /* callbacks */ 15308c2ecf20Sopenharmony_ci .checkit = tda9840_checkit, 15318c2ecf20Sopenharmony_ci .getrxsubchans = tda9840_getrxsubchans, 15328c2ecf20Sopenharmony_ci .setaudmode = tda9840_setaudmode, 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN 15358c2ecf20Sopenharmony_ci /* ,TDA9840_SW, TDA9840_MONO */} } 15368c2ecf20Sopenharmony_ci }, 15378c2ecf20Sopenharmony_ci { 15388c2ecf20Sopenharmony_ci .name = "tda9873h", 15398c2ecf20Sopenharmony_ci .insmodopt = &tda9873, 15408c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA985x_L >> 1, 15418c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA985x_H >> 1, 15428c2ecf20Sopenharmony_ci .registers = 3, 15438c2ecf20Sopenharmony_ci .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* callbacks */ 15468c2ecf20Sopenharmony_ci .checkit = tda9873_checkit, 15478c2ecf20Sopenharmony_ci .getrxsubchans = tda9873_getrxsubchans, 15488c2ecf20Sopenharmony_ci .setaudmode = tda9873_setaudmode, 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, 15518c2ecf20Sopenharmony_ci .inputreg = TDA9873_SW, 15528c2ecf20Sopenharmony_ci .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE, 15538c2ecf20Sopenharmony_ci .inputmap = {0xa0, 0xa2, 0xa0, 0xa0}, 15548c2ecf20Sopenharmony_ci .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE, 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci }, 15578c2ecf20Sopenharmony_ci { 15588c2ecf20Sopenharmony_ci .name = "tda9874h/a", 15598c2ecf20Sopenharmony_ci .insmodopt = &tda9874a, 15608c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA9874 >> 1, 15618c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA9874 >> 1, 15628c2ecf20Sopenharmony_ci .flags = CHIP_NEED_CHECKMODE, 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* callbacks */ 15658c2ecf20Sopenharmony_ci .initialize = tda9874a_initialize, 15668c2ecf20Sopenharmony_ci .checkit = tda9874a_checkit, 15678c2ecf20Sopenharmony_ci .getrxsubchans = tda9874a_getrxsubchans, 15688c2ecf20Sopenharmony_ci .setaudmode = tda9874a_setaudmode, 15698c2ecf20Sopenharmony_ci }, 15708c2ecf20Sopenharmony_ci { 15718c2ecf20Sopenharmony_ci .name = "tda9875", 15728c2ecf20Sopenharmony_ci .insmodopt = &tda9875, 15738c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA9875 >> 1, 15748c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA9875 >> 1, 15758c2ecf20Sopenharmony_ci .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci /* callbacks */ 15788c2ecf20Sopenharmony_ci .initialize = tda9875_initialize, 15798c2ecf20Sopenharmony_ci .checkit = tda9875_checkit, 15808c2ecf20Sopenharmony_ci .volfunc = tda9875_volume, 15818c2ecf20Sopenharmony_ci .bassfunc = tda9875_bass, 15828c2ecf20Sopenharmony_ci .treblefunc = tda9875_treble, 15838c2ecf20Sopenharmony_ci .leftreg = TDA9875_MVL, 15848c2ecf20Sopenharmony_ci .rightreg = TDA9875_MVR, 15858c2ecf20Sopenharmony_ci .bassreg = TDA9875_MBA, 15868c2ecf20Sopenharmony_ci .treblereg = TDA9875_MTR, 15878c2ecf20Sopenharmony_ci .volinit = 58880, 15888c2ecf20Sopenharmony_ci }, 15898c2ecf20Sopenharmony_ci { 15908c2ecf20Sopenharmony_ci .name = "tda9850", 15918c2ecf20Sopenharmony_ci .insmodopt = &tda9850, 15928c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA985x_L >> 1, 15938c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA985x_H >> 1, 15948c2ecf20Sopenharmony_ci .registers = 11, 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci .getrxsubchans = tda985x_getrxsubchans, 15978c2ecf20Sopenharmony_ci .setaudmode = tda985x_setaudmode, 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } 16008c2ecf20Sopenharmony_ci }, 16018c2ecf20Sopenharmony_ci { 16028c2ecf20Sopenharmony_ci .name = "tda9855", 16038c2ecf20Sopenharmony_ci .insmodopt = &tda9855, 16048c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA985x_L >> 1, 16058c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA985x_H >> 1, 16068c2ecf20Sopenharmony_ci .registers = 11, 16078c2ecf20Sopenharmony_ci .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci .leftreg = TDA9855_VL, 16108c2ecf20Sopenharmony_ci .rightreg = TDA9855_VR, 16118c2ecf20Sopenharmony_ci .bassreg = TDA9855_BA, 16128c2ecf20Sopenharmony_ci .treblereg = TDA9855_TR, 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci /* callbacks */ 16158c2ecf20Sopenharmony_ci .volfunc = tda9855_volume, 16168c2ecf20Sopenharmony_ci .bassfunc = tda9855_bass, 16178c2ecf20Sopenharmony_ci .treblefunc = tda9855_treble, 16188c2ecf20Sopenharmony_ci .getrxsubchans = tda985x_getrxsubchans, 16198c2ecf20Sopenharmony_ci .setaudmode = tda985x_setaudmode, 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, 16228c2ecf20Sopenharmony_ci TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, 16238c2ecf20Sopenharmony_ci TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, 16248c2ecf20Sopenharmony_ci 0x07, 0x10, 0x10, 0x03 }} 16258c2ecf20Sopenharmony_ci }, 16268c2ecf20Sopenharmony_ci { 16278c2ecf20Sopenharmony_ci .name = "tea6300", 16288c2ecf20Sopenharmony_ci .insmodopt = &tea6300, 16298c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TEA6300 >> 1, 16308c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TEA6300 >> 1, 16318c2ecf20Sopenharmony_ci .registers = 6, 16328c2ecf20Sopenharmony_ci .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci .leftreg = TEA6300_VR, 16358c2ecf20Sopenharmony_ci .rightreg = TEA6300_VL, 16368c2ecf20Sopenharmony_ci .bassreg = TEA6300_BA, 16378c2ecf20Sopenharmony_ci .treblereg = TEA6300_TR, 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci /* callbacks */ 16408c2ecf20Sopenharmony_ci .volfunc = tea6300_shift10, 16418c2ecf20Sopenharmony_ci .bassfunc = tea6300_shift12, 16428c2ecf20Sopenharmony_ci .treblefunc = tea6300_shift12, 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci .inputreg = TEA6300_S, 16458c2ecf20Sopenharmony_ci .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, 16468c2ecf20Sopenharmony_ci .inputmute = TEA6300_S_GMU, 16478c2ecf20Sopenharmony_ci }, 16488c2ecf20Sopenharmony_ci { 16498c2ecf20Sopenharmony_ci .name = "tea6320", 16508c2ecf20Sopenharmony_ci .insmodopt = &tea6320, 16518c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TEA6300 >> 1, 16528c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TEA6300 >> 1, 16538c2ecf20Sopenharmony_ci .registers = 8, 16548c2ecf20Sopenharmony_ci .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci .leftreg = TEA6320_V, 16578c2ecf20Sopenharmony_ci .rightreg = TEA6320_V, 16588c2ecf20Sopenharmony_ci .bassreg = TEA6320_BA, 16598c2ecf20Sopenharmony_ci .treblereg = TEA6320_TR, 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci /* callbacks */ 16628c2ecf20Sopenharmony_ci .initialize = tea6320_initialize, 16638c2ecf20Sopenharmony_ci .volfunc = tea6320_volume, 16648c2ecf20Sopenharmony_ci .bassfunc = tea6320_shift11, 16658c2ecf20Sopenharmony_ci .treblefunc = tea6320_shift11, 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci .inputreg = TEA6320_S, 16688c2ecf20Sopenharmony_ci .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD }, 16698c2ecf20Sopenharmony_ci .inputmute = TEA6300_S_GMU, 16708c2ecf20Sopenharmony_ci }, 16718c2ecf20Sopenharmony_ci { 16728c2ecf20Sopenharmony_ci .name = "tea6420", 16738c2ecf20Sopenharmony_ci .insmodopt = &tea6420, 16748c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TEA6420 >> 1, 16758c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TEA6420 >> 1, 16768c2ecf20Sopenharmony_ci .registers = 1, 16778c2ecf20Sopenharmony_ci .flags = CHIP_HAS_INPUTSEL, 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci .inputreg = -1, 16808c2ecf20Sopenharmony_ci .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, 16818c2ecf20Sopenharmony_ci .inputmute = TEA6420_S_GMU, 16828c2ecf20Sopenharmony_ci .inputmask = 0x07, 16838c2ecf20Sopenharmony_ci }, 16848c2ecf20Sopenharmony_ci { 16858c2ecf20Sopenharmony_ci .name = "tda8425", 16868c2ecf20Sopenharmony_ci .insmodopt = &tda8425, 16878c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA8425 >> 1, 16888c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA8425 >> 1, 16898c2ecf20Sopenharmony_ci .registers = 9, 16908c2ecf20Sopenharmony_ci .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci .leftreg = TDA8425_VL, 16938c2ecf20Sopenharmony_ci .rightreg = TDA8425_VR, 16948c2ecf20Sopenharmony_ci .bassreg = TDA8425_BA, 16958c2ecf20Sopenharmony_ci .treblereg = TDA8425_TR, 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci /* callbacks */ 16988c2ecf20Sopenharmony_ci .volfunc = tda8425_shift10, 16998c2ecf20Sopenharmony_ci .bassfunc = tda8425_shift12, 17008c2ecf20Sopenharmony_ci .treblefunc = tda8425_shift12, 17018c2ecf20Sopenharmony_ci .setaudmode = tda8425_setaudmode, 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci .inputreg = TDA8425_S1, 17048c2ecf20Sopenharmony_ci .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, 17058c2ecf20Sopenharmony_ci .inputmute = TDA8425_S1_OFF, 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci }, 17088c2ecf20Sopenharmony_ci { 17098c2ecf20Sopenharmony_ci .name = "pic16c54 (PV951)", 17108c2ecf20Sopenharmony_ci .insmodopt = &pic16c54, 17118c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_PIC16C54 >> 1, 17128c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_PIC16C54>> 1, 17138c2ecf20Sopenharmony_ci .registers = 2, 17148c2ecf20Sopenharmony_ci .flags = CHIP_HAS_INPUTSEL, 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci .inputreg = PIC16C54_REG_MISC, 17178c2ecf20Sopenharmony_ci .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, 17188c2ecf20Sopenharmony_ci PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, 17198c2ecf20Sopenharmony_ci PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, 17208c2ecf20Sopenharmony_ci PIC16C54_MISC_SND_MUTE}, 17218c2ecf20Sopenharmony_ci .inputmute = PIC16C54_MISC_SND_MUTE, 17228c2ecf20Sopenharmony_ci }, 17238c2ecf20Sopenharmony_ci { 17248c2ecf20Sopenharmony_ci .name = "ta8874z", 17258c2ecf20Sopenharmony_ci .checkit = ta8874z_checkit, 17268c2ecf20Sopenharmony_ci .insmodopt = &ta8874z, 17278c2ecf20Sopenharmony_ci .addr_lo = I2C_ADDR_TDA9840 >> 1, 17288c2ecf20Sopenharmony_ci .addr_hi = I2C_ADDR_TDA9840 >> 1, 17298c2ecf20Sopenharmony_ci .registers = 2, 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci /* callbacks */ 17328c2ecf20Sopenharmony_ci .getrxsubchans = ta8874z_getrxsubchans, 17338c2ecf20Sopenharmony_ci .setaudmode = ta8874z_setaudmode, 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, 17368c2ecf20Sopenharmony_ci }, 17378c2ecf20Sopenharmony_ci { .name = NULL } /* EOF */ 17388c2ecf20Sopenharmony_ci}; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic int tvaudio_s_ctrl(struct v4l2_ctrl *ctrl) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = to_sd(ctrl); 17468c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 17478c2ecf20Sopenharmony_ci struct CHIPDESC *desc = chip->desc; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci switch (ctrl->id) { 17508c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 17518c2ecf20Sopenharmony_ci chip->muted = ctrl->val; 17528c2ecf20Sopenharmony_ci if (chip->muted) 17538c2ecf20Sopenharmony_ci chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); 17548c2ecf20Sopenharmony_ci else 17558c2ecf20Sopenharmony_ci chip_write_masked(chip,desc->inputreg, 17568c2ecf20Sopenharmony_ci desc->inputmap[chip->input],desc->inputmask); 17578c2ecf20Sopenharmony_ci return 0; 17588c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: { 17598c2ecf20Sopenharmony_ci u32 volume, balance; 17608c2ecf20Sopenharmony_ci u32 left, right; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci volume = chip->volume->val; 17638c2ecf20Sopenharmony_ci balance = chip->balance->val; 17648c2ecf20Sopenharmony_ci left = (min(65536U - balance, 32768U) * volume) / 32768U; 17658c2ecf20Sopenharmony_ci right = (min(balance, 32768U) * volume) / 32768U; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci chip_write(chip, desc->leftreg, desc->volfunc(left)); 17688c2ecf20Sopenharmony_ci chip_write(chip, desc->rightreg, desc->volfunc(right)); 17698c2ecf20Sopenharmony_ci return 0; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_BASS: 17728c2ecf20Sopenharmony_ci chip_write(chip, desc->bassreg, desc->bassfunc(ctrl->val)); 17738c2ecf20Sopenharmony_ci return 0; 17748c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_TREBLE: 17758c2ecf20Sopenharmony_ci chip_write(chip, desc->treblereg, desc->treblefunc(ctrl->val)); 17768c2ecf20Sopenharmony_ci return 0; 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci return -EINVAL; 17798c2ecf20Sopenharmony_ci} 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 17838c2ecf20Sopenharmony_ci/* video4linux interface */ 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_cistatic int tvaudio_s_radio(struct v4l2_subdev *sd) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci chip->radio = 1; 17908c2ecf20Sopenharmony_ci /* del_timer(&chip->wt); */ 17918c2ecf20Sopenharmony_ci return 0; 17928c2ecf20Sopenharmony_ci} 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_cistatic int tvaudio_s_routing(struct v4l2_subdev *sd, 17958c2ecf20Sopenharmony_ci u32 input, u32 output, u32 config) 17968c2ecf20Sopenharmony_ci{ 17978c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 17988c2ecf20Sopenharmony_ci struct CHIPDESC *desc = chip->desc; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (!(desc->flags & CHIP_HAS_INPUTSEL)) 18018c2ecf20Sopenharmony_ci return 0; 18028c2ecf20Sopenharmony_ci if (input >= 4) 18038c2ecf20Sopenharmony_ci return -EINVAL; 18048c2ecf20Sopenharmony_ci /* There are four inputs: tuner, radio, extern and intern. */ 18058c2ecf20Sopenharmony_ci chip->input = input; 18068c2ecf20Sopenharmony_ci if (chip->muted) 18078c2ecf20Sopenharmony_ci return 0; 18088c2ecf20Sopenharmony_ci chip_write_masked(chip, desc->inputreg, 18098c2ecf20Sopenharmony_ci desc->inputmap[chip->input], desc->inputmask); 18108c2ecf20Sopenharmony_ci return 0; 18118c2ecf20Sopenharmony_ci} 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_cistatic int tvaudio_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 18168c2ecf20Sopenharmony_ci struct CHIPDESC *desc = chip->desc; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (!desc->setaudmode) 18198c2ecf20Sopenharmony_ci return 0; 18208c2ecf20Sopenharmony_ci if (chip->radio) 18218c2ecf20Sopenharmony_ci return 0; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci switch (vt->audmode) { 18248c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 18258c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 18268c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 18278c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 18288c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 18298c2ecf20Sopenharmony_ci break; 18308c2ecf20Sopenharmony_ci default: 18318c2ecf20Sopenharmony_ci return -EINVAL; 18328c2ecf20Sopenharmony_ci } 18338c2ecf20Sopenharmony_ci chip->audmode = vt->audmode; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci if (chip->thread) 18368c2ecf20Sopenharmony_ci wake_up_process(chip->thread); 18378c2ecf20Sopenharmony_ci else 18388c2ecf20Sopenharmony_ci desc->setaudmode(chip, vt->audmode); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci return 0; 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_cistatic int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) 18448c2ecf20Sopenharmony_ci{ 18458c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 18468c2ecf20Sopenharmony_ci struct CHIPDESC *desc = chip->desc; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci if (!desc->getrxsubchans) 18498c2ecf20Sopenharmony_ci return 0; 18508c2ecf20Sopenharmony_ci if (chip->radio) 18518c2ecf20Sopenharmony_ci return 0; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci vt->audmode = chip->audmode; 18548c2ecf20Sopenharmony_ci vt->rxsubchans = desc->getrxsubchans(chip); 18558c2ecf20Sopenharmony_ci vt->capability |= V4L2_TUNER_CAP_STEREO | 18568c2ecf20Sopenharmony_ci V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci return 0; 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_cistatic int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) 18628c2ecf20Sopenharmony_ci{ 18638c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci chip->radio = 0; 18668c2ecf20Sopenharmony_ci return 0; 18678c2ecf20Sopenharmony_ci} 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_cistatic int tvaudio_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 18728c2ecf20Sopenharmony_ci struct CHIPDESC *desc = chip->desc; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* For chips that provide getrxsubchans and setaudmode, and doesn't 18758c2ecf20Sopenharmony_ci automatically follows the stereo carrier, a kthread is 18768c2ecf20Sopenharmony_ci created to set the audio standard. In this case, when then 18778c2ecf20Sopenharmony_ci the video channel is changed, tvaudio starts on MONO mode. 18788c2ecf20Sopenharmony_ci After waiting for 2 seconds, the kernel thread is called, 18798c2ecf20Sopenharmony_ci to follow whatever audio standard is pointed by the 18808c2ecf20Sopenharmony_ci audio carrier. 18818c2ecf20Sopenharmony_ci */ 18828c2ecf20Sopenharmony_ci if (chip->thread) { 18838c2ecf20Sopenharmony_ci desc->setaudmode(chip, V4L2_TUNER_MODE_MONO); 18848c2ecf20Sopenharmony_ci chip->prevmode = -1; /* reset previous mode */ 18858c2ecf20Sopenharmony_ci mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci return 0; 18888c2ecf20Sopenharmony_ci} 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_cistatic int tvaudio_log_status(struct v4l2_subdev *sd) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 18938c2ecf20Sopenharmony_ci struct CHIPDESC *desc = chip->desc; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci v4l2_info(sd, "Chip: %s\n", desc->name); 18968c2ecf20Sopenharmony_ci v4l2_ctrl_handler_log_status(&chip->hdl, sd->name); 18978c2ecf20Sopenharmony_ci return 0; 18988c2ecf20Sopenharmony_ci} 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops tvaudio_ctrl_ops = { 19038c2ecf20Sopenharmony_ci .s_ctrl = tvaudio_s_ctrl, 19048c2ecf20Sopenharmony_ci}; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops tvaudio_core_ops = { 19078c2ecf20Sopenharmony_ci .log_status = tvaudio_log_status, 19088c2ecf20Sopenharmony_ci}; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { 19118c2ecf20Sopenharmony_ci .s_radio = tvaudio_s_radio, 19128c2ecf20Sopenharmony_ci .s_frequency = tvaudio_s_frequency, 19138c2ecf20Sopenharmony_ci .s_tuner = tvaudio_s_tuner, 19148c2ecf20Sopenharmony_ci .g_tuner = tvaudio_g_tuner, 19158c2ecf20Sopenharmony_ci}; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { 19188c2ecf20Sopenharmony_ci .s_routing = tvaudio_s_routing, 19198c2ecf20Sopenharmony_ci}; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops tvaudio_video_ops = { 19228c2ecf20Sopenharmony_ci .s_std = tvaudio_s_std, 19238c2ecf20Sopenharmony_ci}; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops tvaudio_ops = { 19268c2ecf20Sopenharmony_ci .core = &tvaudio_core_ops, 19278c2ecf20Sopenharmony_ci .tuner = &tvaudio_tuner_ops, 19288c2ecf20Sopenharmony_ci .audio = &tvaudio_audio_ops, 19298c2ecf20Sopenharmony_ci .video = &tvaudio_video_ops, 19308c2ecf20Sopenharmony_ci}; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci/* i2c registration */ 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_cistatic int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) 19388c2ecf20Sopenharmony_ci{ 19398c2ecf20Sopenharmony_ci struct CHIPSTATE *chip; 19408c2ecf20Sopenharmony_ci struct CHIPDESC *desc; 19418c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci if (debug) { 19448c2ecf20Sopenharmony_ci printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); 19458c2ecf20Sopenharmony_ci printk(KERN_INFO "tvaudio: known chips: "); 19468c2ecf20Sopenharmony_ci for (desc = chiplist; desc->name != NULL; desc++) 19478c2ecf20Sopenharmony_ci printk(KERN_CONT "%s%s", 19488c2ecf20Sopenharmony_ci (desc == chiplist) ? "" : ", ", desc->name); 19498c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 19538c2ecf20Sopenharmony_ci if (!chip) 19548c2ecf20Sopenharmony_ci return -ENOMEM; 19558c2ecf20Sopenharmony_ci sd = &chip->sd; 19568c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci /* find description for the chip */ 19598c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); 19608c2ecf20Sopenharmony_ci for (desc = chiplist; desc->name != NULL; desc++) { 19618c2ecf20Sopenharmony_ci if (0 == *(desc->insmodopt)) 19628c2ecf20Sopenharmony_ci continue; 19638c2ecf20Sopenharmony_ci if (client->addr < desc->addr_lo || 19648c2ecf20Sopenharmony_ci client->addr > desc->addr_hi) 19658c2ecf20Sopenharmony_ci continue; 19668c2ecf20Sopenharmony_ci if (desc->checkit && !desc->checkit(chip)) 19678c2ecf20Sopenharmony_ci continue; 19688c2ecf20Sopenharmony_ci break; 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci if (desc->name == NULL) { 19718c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "no matching chip description found\n"); 19728c2ecf20Sopenharmony_ci return -EIO; 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); 19758c2ecf20Sopenharmony_ci if (desc->flags) { 19768c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", 19778c2ecf20Sopenharmony_ci (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", 19788c2ecf20Sopenharmony_ci (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", 19798c2ecf20Sopenharmony_ci (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); 19808c2ecf20Sopenharmony_ci } 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* fill required data structures */ 19838c2ecf20Sopenharmony_ci if (!id) 19848c2ecf20Sopenharmony_ci strscpy(client->name, desc->name, I2C_NAME_SIZE); 19858c2ecf20Sopenharmony_ci chip->desc = desc; 19868c2ecf20Sopenharmony_ci chip->shadow.count = desc->registers+1; 19878c2ecf20Sopenharmony_ci chip->prevmode = -1; 19888c2ecf20Sopenharmony_ci chip->audmode = V4L2_TUNER_MODE_LANG1; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci /* initialization */ 19918c2ecf20Sopenharmony_ci if (desc->initialize != NULL) 19928c2ecf20Sopenharmony_ci desc->initialize(chip); 19938c2ecf20Sopenharmony_ci else 19948c2ecf20Sopenharmony_ci chip_cmd(chip, "init", &desc->init); 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&chip->hdl, 5); 19978c2ecf20Sopenharmony_ci if (desc->flags & CHIP_HAS_INPUTSEL) 19988c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&chip->hdl, &tvaudio_ctrl_ops, 19998c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); 20008c2ecf20Sopenharmony_ci if (desc->flags & CHIP_HAS_VOLUME) { 20018c2ecf20Sopenharmony_ci if (!desc->volfunc) { 20028c2ecf20Sopenharmony_ci /* This shouldn't be happen. Warn user, but keep working 20038c2ecf20Sopenharmony_ci without volume controls 20048c2ecf20Sopenharmony_ci */ 20058c2ecf20Sopenharmony_ci v4l2_info(sd, "volume callback undefined!\n"); 20068c2ecf20Sopenharmony_ci desc->flags &= ~CHIP_HAS_VOLUME; 20078c2ecf20Sopenharmony_ci } else { 20088c2ecf20Sopenharmony_ci chip->volume = v4l2_ctrl_new_std(&chip->hdl, 20098c2ecf20Sopenharmony_ci &tvaudio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 20108c2ecf20Sopenharmony_ci 0, 65535, 65535 / 100, 20118c2ecf20Sopenharmony_ci desc->volinit ? desc->volinit : 65535); 20128c2ecf20Sopenharmony_ci chip->balance = v4l2_ctrl_new_std(&chip->hdl, 20138c2ecf20Sopenharmony_ci &tvaudio_ctrl_ops, V4L2_CID_AUDIO_BALANCE, 20148c2ecf20Sopenharmony_ci 0, 65535, 65535 / 100, 32768); 20158c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &chip->volume); 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci } 20188c2ecf20Sopenharmony_ci if (desc->flags & CHIP_HAS_BASSTREBLE) { 20198c2ecf20Sopenharmony_ci if (!desc->bassfunc || !desc->treblefunc) { 20208c2ecf20Sopenharmony_ci /* This shouldn't be happen. Warn user, but keep working 20218c2ecf20Sopenharmony_ci without bass/treble controls 20228c2ecf20Sopenharmony_ci */ 20238c2ecf20Sopenharmony_ci v4l2_info(sd, "bass/treble callbacks undefined!\n"); 20248c2ecf20Sopenharmony_ci desc->flags &= ~CHIP_HAS_BASSTREBLE; 20258c2ecf20Sopenharmony_ci } else { 20268c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&chip->hdl, 20278c2ecf20Sopenharmony_ci &tvaudio_ctrl_ops, V4L2_CID_AUDIO_BASS, 20288c2ecf20Sopenharmony_ci 0, 65535, 65535 / 100, 20298c2ecf20Sopenharmony_ci desc->bassinit ? desc->bassinit : 32768); 20308c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&chip->hdl, 20318c2ecf20Sopenharmony_ci &tvaudio_ctrl_ops, V4L2_CID_AUDIO_TREBLE, 20328c2ecf20Sopenharmony_ci 0, 65535, 65535 / 100, 20338c2ecf20Sopenharmony_ci desc->trebleinit ? desc->trebleinit : 32768); 20348c2ecf20Sopenharmony_ci } 20358c2ecf20Sopenharmony_ci } 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci sd->ctrl_handler = &chip->hdl; 20388c2ecf20Sopenharmony_ci if (chip->hdl.error) { 20398c2ecf20Sopenharmony_ci int err = chip->hdl.error; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&chip->hdl); 20428c2ecf20Sopenharmony_ci return err; 20438c2ecf20Sopenharmony_ci } 20448c2ecf20Sopenharmony_ci /* set controls to the default values */ 20458c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&chip->hdl); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci chip->thread = NULL; 20488c2ecf20Sopenharmony_ci timer_setup(&chip->wt, chip_thread_wake, 0); 20498c2ecf20Sopenharmony_ci if (desc->flags & CHIP_NEED_CHECKMODE) { 20508c2ecf20Sopenharmony_ci if (!desc->getrxsubchans || !desc->setaudmode) { 20518c2ecf20Sopenharmony_ci /* This shouldn't be happen. Warn user, but keep working 20528c2ecf20Sopenharmony_ci without kthread 20538c2ecf20Sopenharmony_ci */ 20548c2ecf20Sopenharmony_ci v4l2_info(sd, "set/get mode callbacks undefined!\n"); 20558c2ecf20Sopenharmony_ci return 0; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci /* start async thread */ 20588c2ecf20Sopenharmony_ci chip->thread = kthread_run(chip_thread, chip, "%s", 20598c2ecf20Sopenharmony_ci client->name); 20608c2ecf20Sopenharmony_ci if (IS_ERR(chip->thread)) { 20618c2ecf20Sopenharmony_ci v4l2_warn(sd, "failed to create kthread\n"); 20628c2ecf20Sopenharmony_ci chip->thread = NULL; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci } 20658c2ecf20Sopenharmony_ci return 0; 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_cistatic int tvaudio_remove(struct i2c_client *client) 20698c2ecf20Sopenharmony_ci{ 20708c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 20718c2ecf20Sopenharmony_ci struct CHIPSTATE *chip = to_state(sd); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci del_timer_sync(&chip->wt); 20748c2ecf20Sopenharmony_ci if (chip->thread) { 20758c2ecf20Sopenharmony_ci /* shutdown async thread */ 20768c2ecf20Sopenharmony_ci kthread_stop(chip->thread); 20778c2ecf20Sopenharmony_ci chip->thread = NULL; 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 20818c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&chip->hdl); 20828c2ecf20Sopenharmony_ci return 0; 20838c2ecf20Sopenharmony_ci} 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci/* This driver supports many devices and the idea is to let the driver 20868c2ecf20Sopenharmony_ci detect which device is present. So rather than listing all supported 20878c2ecf20Sopenharmony_ci devices here, we pretend to support a single, fake device type. */ 20888c2ecf20Sopenharmony_cistatic const struct i2c_device_id tvaudio_id[] = { 20898c2ecf20Sopenharmony_ci { "tvaudio", 0 }, 20908c2ecf20Sopenharmony_ci { } 20918c2ecf20Sopenharmony_ci}; 20928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tvaudio_id); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_cistatic struct i2c_driver tvaudio_driver = { 20958c2ecf20Sopenharmony_ci .driver = { 20968c2ecf20Sopenharmony_ci .name = "tvaudio", 20978c2ecf20Sopenharmony_ci }, 20988c2ecf20Sopenharmony_ci .probe = tvaudio_probe, 20998c2ecf20Sopenharmony_ci .remove = tvaudio_remove, 21008c2ecf20Sopenharmony_ci .id_table = tvaudio_id, 21018c2ecf20Sopenharmony_ci}; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_cimodule_i2c_driver(tvaudio_driver); 2104