18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Programming the mspx4xx sound processor family 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/freezer.h> 138c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 148c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 158c2ecf20Sopenharmony_ci#include <media/drv-intf/msp3400.h> 168c2ecf20Sopenharmony_ci#include <linux/kthread.h> 178c2ecf20Sopenharmony_ci#include <linux/suspend.h> 188c2ecf20Sopenharmony_ci#include "msp3400-driver.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* this one uses the automatic sound standard detection of newer msp34xx 218c2ecf20Sopenharmony_ci chip versions */ 228c2ecf20Sopenharmony_cistatic struct { 238c2ecf20Sopenharmony_ci int retval; 248c2ecf20Sopenharmony_ci int main, second; 258c2ecf20Sopenharmony_ci char *name; 268c2ecf20Sopenharmony_ci v4l2_std_id std; 278c2ecf20Sopenharmony_ci} msp_stdlist[] = { 288c2ecf20Sopenharmony_ci { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL }, 298c2ecf20Sopenharmony_ci { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL }, 308c2ecf20Sopenharmony_ci { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), 318c2ecf20Sopenharmony_ci "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN }, 328c2ecf20Sopenharmony_ci { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), 338c2ecf20Sopenharmony_ci "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG }, 348c2ecf20Sopenharmony_ci { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), 358c2ecf20Sopenharmony_ci "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK }, 368c2ecf20Sopenharmony_ci { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), 378c2ecf20Sopenharmony_ci "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK }, 388c2ecf20Sopenharmony_ci { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), 398c2ecf20Sopenharmony_ci "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK }, 408c2ecf20Sopenharmony_ci { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), 418c2ecf20Sopenharmony_ci "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK }, 428c2ecf20Sopenharmony_ci { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), 438c2ecf20Sopenharmony_ci "5.5/5.85 B/G NICAM FM", V4L2_STD_BG }, 448c2ecf20Sopenharmony_ci { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), 458c2ecf20Sopenharmony_ci "6.5/5.85 L NICAM AM", V4L2_STD_L }, 468c2ecf20Sopenharmony_ci { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), 478c2ecf20Sopenharmony_ci "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I }, 488c2ecf20Sopenharmony_ci { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), 498c2ecf20Sopenharmony_ci "6.5/5.85 D/K NICAM FM", V4L2_STD_DK }, 508c2ecf20Sopenharmony_ci { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), 518c2ecf20Sopenharmony_ci "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK }, 528c2ecf20Sopenharmony_ci { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), 538c2ecf20Sopenharmony_ci "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK }, 548c2ecf20Sopenharmony_ci { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), 558c2ecf20Sopenharmony_ci "4.5 M BTSC-Stereo", V4L2_STD_MTS }, 568c2ecf20Sopenharmony_ci { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), 578c2ecf20Sopenharmony_ci "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS }, 588c2ecf20Sopenharmony_ci { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), 598c2ecf20Sopenharmony_ci "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP }, 608c2ecf20Sopenharmony_ci { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), 618c2ecf20Sopenharmony_ci "10.7 FM-Stereo Radio", V4L2_STD_ALL }, 628c2ecf20Sopenharmony_ci { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), 638c2ecf20Sopenharmony_ci "6.5 SAT-Mono", V4L2_STD_ALL }, 648c2ecf20Sopenharmony_ci { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), 658c2ecf20Sopenharmony_ci "7.02/7.20 SAT-Stereo", V4L2_STD_ALL }, 668c2ecf20Sopenharmony_ci { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), 678c2ecf20Sopenharmony_ci "7.2 SAT ADR", V4L2_STD_ALL }, 688c2ecf20Sopenharmony_ci { -1, 0, 0, NULL, 0 }, /* EOF */ 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic struct msp3400c_init_data_dem { 728c2ecf20Sopenharmony_ci int fir1[6]; 738c2ecf20Sopenharmony_ci int fir2[6]; 748c2ecf20Sopenharmony_ci int cdo1; 758c2ecf20Sopenharmony_ci int cdo2; 768c2ecf20Sopenharmony_ci int ad_cv; 778c2ecf20Sopenharmony_ci int mode_reg; 788c2ecf20Sopenharmony_ci int dsp_src; 798c2ecf20Sopenharmony_ci int dsp_matrix; 808c2ecf20Sopenharmony_ci} msp3400c_init_data[] = { 818c2ecf20Sopenharmony_ci { /* AM (for carrier detect / msp3400) */ 828c2ecf20Sopenharmony_ci {75, 19, 36, 35, 39, 40}, 838c2ecf20Sopenharmony_ci {75, 19, 36, 35, 39, 40}, 848c2ecf20Sopenharmony_ci MSP_CARRIER(5.5), MSP_CARRIER(5.5), 858c2ecf20Sopenharmony_ci 0x00d0, 0x0500, 0x0020, 0x3000 868c2ecf20Sopenharmony_ci }, { /* AM (for carrier detect / msp3410) */ 878c2ecf20Sopenharmony_ci {-1, -1, -8, 2, 59, 126}, 888c2ecf20Sopenharmony_ci {-1, -1, -8, 2, 59, 126}, 898c2ecf20Sopenharmony_ci MSP_CARRIER(5.5), MSP_CARRIER(5.5), 908c2ecf20Sopenharmony_ci 0x00d0, 0x0100, 0x0020, 0x3000 918c2ecf20Sopenharmony_ci }, { /* FM Radio */ 928c2ecf20Sopenharmony_ci {-8, -8, 4, 6, 78, 107}, 938c2ecf20Sopenharmony_ci {-8, -8, 4, 6, 78, 107}, 948c2ecf20Sopenharmony_ci MSP_CARRIER(10.7), MSP_CARRIER(10.7), 958c2ecf20Sopenharmony_ci 0x00d0, 0x0480, 0x0020, 0x3000 968c2ecf20Sopenharmony_ci }, { /* Terrestrial FM-mono + FM-stereo */ 978c2ecf20Sopenharmony_ci {3, 18, 27, 48, 66, 72}, 988c2ecf20Sopenharmony_ci {3, 18, 27, 48, 66, 72}, 998c2ecf20Sopenharmony_ci MSP_CARRIER(5.5), MSP_CARRIER(5.5), 1008c2ecf20Sopenharmony_ci 0x00d0, 0x0480, 0x0030, 0x3000 1018c2ecf20Sopenharmony_ci }, { /* Sat FM-mono */ 1028c2ecf20Sopenharmony_ci { 1, 9, 14, 24, 33, 37}, 1038c2ecf20Sopenharmony_ci { 3, 18, 27, 48, 66, 72}, 1048c2ecf20Sopenharmony_ci MSP_CARRIER(6.5), MSP_CARRIER(6.5), 1058c2ecf20Sopenharmony_ci 0x00c6, 0x0480, 0x0000, 0x3000 1068c2ecf20Sopenharmony_ci }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ 1078c2ecf20Sopenharmony_ci {-2, -8, -10, 10, 50, 86}, 1088c2ecf20Sopenharmony_ci {3, 18, 27, 48, 66, 72}, 1098c2ecf20Sopenharmony_ci MSP_CARRIER(5.5), MSP_CARRIER(5.5), 1108c2ecf20Sopenharmony_ci 0x00d0, 0x0040, 0x0120, 0x3000 1118c2ecf20Sopenharmony_ci }, { /* NICAM/FM -- I (6.0/6.552) */ 1128c2ecf20Sopenharmony_ci {2, 4, -6, -4, 40, 94}, 1138c2ecf20Sopenharmony_ci {3, 18, 27, 48, 66, 72}, 1148c2ecf20Sopenharmony_ci MSP_CARRIER(6.0), MSP_CARRIER(6.0), 1158c2ecf20Sopenharmony_ci 0x00d0, 0x0040, 0x0120, 0x3000 1168c2ecf20Sopenharmony_ci }, { /* NICAM/AM -- L (6.5/5.85) */ 1178c2ecf20Sopenharmony_ci {-2, -8, -10, 10, 50, 86}, 1188c2ecf20Sopenharmony_ci {-4, -12, -9, 23, 79, 126}, 1198c2ecf20Sopenharmony_ci MSP_CARRIER(6.5), MSP_CARRIER(6.5), 1208c2ecf20Sopenharmony_ci 0x00c6, 0x0140, 0x0120, 0x7c00 1218c2ecf20Sopenharmony_ci }, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct msp3400c_carrier_detect { 1258c2ecf20Sopenharmony_ci int cdo; 1268c2ecf20Sopenharmony_ci char *name; 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { 1308c2ecf20Sopenharmony_ci /* main carrier */ 1318c2ecf20Sopenharmony_ci { MSP_CARRIER(4.5), "4.5 NTSC" }, 1328c2ecf20Sopenharmony_ci { MSP_CARRIER(5.5), "5.5 PAL B/G" }, 1338c2ecf20Sopenharmony_ci { MSP_CARRIER(6.0), "6.0 PAL I" }, 1348c2ecf20Sopenharmony_ci { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { 1388c2ecf20Sopenharmony_ci /* PAL B/G */ 1398c2ecf20Sopenharmony_ci { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, 1408c2ecf20Sopenharmony_ci { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { 1448c2ecf20Sopenharmony_ci /* PAL SAT / SECAM */ 1458c2ecf20Sopenharmony_ci { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, 1468c2ecf20Sopenharmony_ci { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, 1478c2ecf20Sopenharmony_ci { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, 1488c2ecf20Sopenharmony_ci { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, 1498c2ecf20Sopenharmony_ci { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, 1508c2ecf20Sopenharmony_ci { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciconst char *msp_standard_std_name(int std) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int i; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci for (i = 0; msp_stdlist[i].name != NULL; i++) 1608c2ecf20Sopenharmony_ci if (msp_stdlist[i].retval == std) 1618c2ecf20Sopenharmony_ci return msp_stdlist[i].name; 1628c2ecf20Sopenharmony_ci return "unknown"; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic v4l2_std_id msp_standard_std(int std) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int i; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci for (i = 0; msp_stdlist[i].name != NULL; i++) 1708c2ecf20Sopenharmony_ci if (msp_stdlist[i].retval == std) 1718c2ecf20Sopenharmony_ci return msp_stdlist[i].std; 1728c2ecf20Sopenharmony_ci return V4L2_STD_ALL; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void msp_set_source(struct i2c_client *client, u16 src) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (msp_dolby) { 1808c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ 1818c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ 1828c2ecf20Sopenharmony_ci } else { 1838c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0008, src); 1848c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0009, src); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x000a, src); 1878c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x000b, src); 1888c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x000c, src); 1898c2ecf20Sopenharmony_ci if (state->has_scart2_out) 1908c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0041, src); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_civoid msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0093, cdo1 & 0xfff); 1968c2ecf20Sopenharmony_ci msp_write_dem(client, 0x009b, cdo1 >> 12); 1978c2ecf20Sopenharmony_ci msp_write_dem(client, 0x00a3, cdo2 & 0xfff); 1988c2ecf20Sopenharmony_ci msp_write_dem(client, 0x00ab, cdo2 >> 12); 1998c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0056, 0); /* LOAD_REG_1/2 */ 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_civoid msp3400c_set_mode(struct i2c_client *client, int mode) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 2058c2ecf20Sopenharmony_ci struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; 2068c2ecf20Sopenharmony_ci int tuner = (state->route_in >> 3) & 1; 2078c2ecf20Sopenharmony_ci int i; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "set_mode: %d\n", mode); 2108c2ecf20Sopenharmony_ci state->mode = mode; 2118c2ecf20Sopenharmony_ci state->rxsubchans = V4L2_TUNER_SUB_MONO; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0)); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 5; i >= 0; i--) /* fir 1 */ 2168c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0001, data->fir1[i]); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ 2198c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0005, 0x0040); 2208c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0005, 0x0000); 2218c2ecf20Sopenharmony_ci for (i = 5; i >= 0; i--) 2228c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0005, data->fir2[i]); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci msp_write_dem(client, 0x0083, data->mode_reg); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, data->cdo1, data->cdo2); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci msp_set_source(client, data->dsp_src); 2298c2ecf20Sopenharmony_ci /* set prescales */ 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* volume prescale for SCART (AM mono input) */ 2328c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x000d, 0x1900); 2338c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x000e, data->dsp_matrix); 2348c2ecf20Sopenharmony_ci if (state->has_nicam) /* nicam prescale */ 2358c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0010, 0x5a00); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP, 2398c2ecf20Sopenharmony_ci nor do they support stereo BTSC. */ 2408c2ecf20Sopenharmony_cistatic void msp3400c_set_audmode(struct i2c_client *client) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci static char *strmode[] = { 2438c2ecf20Sopenharmony_ci "mono", "stereo", "lang2", "lang1", "lang1+lang2" 2448c2ecf20Sopenharmony_ci }; 2458c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 2468c2ecf20Sopenharmony_ci char *modestr = (state->audmode >= 0 && state->audmode < 5) ? 2478c2ecf20Sopenharmony_ci strmode[state->audmode] : "unknown"; 2488c2ecf20Sopenharmony_ci int src = 0; /* channel source: FM/AM, nicam or SCART */ 2498c2ecf20Sopenharmony_ci int audmode = state->audmode; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (state->opmode == OPMODE_AUTOSELECT) { 2528c2ecf20Sopenharmony_ci /* this method would break everything, let's make sure 2538c2ecf20Sopenharmony_ci * it's never called 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 2568c2ecf20Sopenharmony_ci "set_audmode called with mode=%d instead of set_source (ignored)\n", 2578c2ecf20Sopenharmony_ci state->audmode); 2588c2ecf20Sopenharmony_ci return; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Note: for the C and D revs no NTSC stereo + SAP is possible as 2628c2ecf20Sopenharmony_ci the hardware does not support SAP. So the rxsubchans combination 2638c2ecf20Sopenharmony_ci of STEREO | LANG2 does not occur. */ 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (state->mode != MSP_MODE_EXTERN) { 2668c2ecf20Sopenharmony_ci /* switch to mono if only mono is available */ 2678c2ecf20Sopenharmony_ci if (state->rxsubchans == V4L2_TUNER_SUB_MONO) 2688c2ecf20Sopenharmony_ci audmode = V4L2_TUNER_MODE_MONO; 2698c2ecf20Sopenharmony_ci /* if bilingual */ 2708c2ecf20Sopenharmony_ci else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { 2718c2ecf20Sopenharmony_ci /* and mono or stereo, then fallback to lang1 */ 2728c2ecf20Sopenharmony_ci if (audmode == V4L2_TUNER_MODE_MONO || 2738c2ecf20Sopenharmony_ci audmode == V4L2_TUNER_MODE_STEREO) 2748c2ecf20Sopenharmony_ci audmode = V4L2_TUNER_MODE_LANG1; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci /* if stereo, and audmode is not mono, then switch to stereo */ 2778c2ecf20Sopenharmony_ci else if (audmode != V4L2_TUNER_MODE_MONO) 2788c2ecf20Sopenharmony_ci audmode = V4L2_TUNER_MODE_STEREO; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* switch demodulator */ 2828c2ecf20Sopenharmony_ci switch (state->mode) { 2838c2ecf20Sopenharmony_ci case MSP_MODE_FM_TERRA: 2848c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "FM set_audmode: %s\n", modestr); 2858c2ecf20Sopenharmony_ci switch (audmode) { 2868c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 2878c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x000e, 0x3001); 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 2908c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 2918c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 2928c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 2938c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x000e, 0x3000); 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case MSP_MODE_FM_SAT: 2988c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "SAT set_audmode: %s\n", modestr); 2998c2ecf20Sopenharmony_ci switch (audmode) { 3008c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 3018c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 3048c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 3058c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 3088c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 3118c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case MSP_MODE_FM_NICAM1: 3168c2ecf20Sopenharmony_ci case MSP_MODE_FM_NICAM2: 3178c2ecf20Sopenharmony_ci case MSP_MODE_AM_NICAM: 3188c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3198c2ecf20Sopenharmony_ci "NICAM set_audmode: %s\n", modestr); 3208c2ecf20Sopenharmony_ci if (state->nicam_on) 3218c2ecf20Sopenharmony_ci src = 0x0100; /* NICAM */ 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case MSP_MODE_BTSC: 3248c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3258c2ecf20Sopenharmony_ci "BTSC set_audmode: %s\n", modestr); 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci case MSP_MODE_EXTERN: 3288c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3298c2ecf20Sopenharmony_ci "extern set_audmode: %s\n", modestr); 3308c2ecf20Sopenharmony_ci src = 0x0200; /* SCART */ 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci case MSP_MODE_FM_RADIO: 3338c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3348c2ecf20Sopenharmony_ci "FM-Radio set_audmode: %s\n", modestr); 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci default: 3378c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "mono set_audmode\n"); 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* switch audio */ 3428c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "set audmode %d\n", audmode); 3438c2ecf20Sopenharmony_ci switch (audmode) { 3448c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 3458c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 3468c2ecf20Sopenharmony_ci src |= 0x0020; 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 3498c2ecf20Sopenharmony_ci if (state->mode == MSP_MODE_AM_NICAM) { 3508c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "switching to AM mono\n"); 3518c2ecf20Sopenharmony_ci /* AM mono decoding is handled by tuner, not MSP chip */ 3528c2ecf20Sopenharmony_ci /* SCART switching control register */ 3538c2ecf20Sopenharmony_ci msp_set_scart(client, SCART_MONO, 0); 3548c2ecf20Sopenharmony_ci src = 0x0200; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) 3588c2ecf20Sopenharmony_ci src = 0x0030; 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 3638c2ecf20Sopenharmony_ci src |= 0x0010; 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3678c2ecf20Sopenharmony_ci "set_audmode final source/matrix = 0x%x\n", src); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci msp_set_source(client, src); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void msp3400c_print_mode(struct i2c_client *client) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (state->main == state->second) 3778c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3788c2ecf20Sopenharmony_ci "mono sound carrier: %d.%03d MHz\n", 3798c2ecf20Sopenharmony_ci state->main / 910000, (state->main / 910) % 1000); 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3828c2ecf20Sopenharmony_ci "main sound carrier: %d.%03d MHz\n", 3838c2ecf20Sopenharmony_ci state->main / 910000, (state->main / 910) % 1000); 3848c2ecf20Sopenharmony_ci if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) 3858c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3868c2ecf20Sopenharmony_ci "NICAM/FM carrier : %d.%03d MHz\n", 3878c2ecf20Sopenharmony_ci state->second / 910000, (state->second/910) % 1000); 3888c2ecf20Sopenharmony_ci if (state->mode == MSP_MODE_AM_NICAM) 3898c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3908c2ecf20Sopenharmony_ci "NICAM/AM carrier : %d.%03d MHz\n", 3918c2ecf20Sopenharmony_ci state->second / 910000, (state->second / 910) % 1000); 3928c2ecf20Sopenharmony_ci if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { 3938c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 3948c2ecf20Sopenharmony_ci "FM-stereo carrier : %d.%03d MHz\n", 3958c2ecf20Sopenharmony_ci state->second / 910000, (state->second / 910) % 1000); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int msp3400c_detect_stereo(struct i2c_client *client) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 4048c2ecf20Sopenharmony_ci int val; 4058c2ecf20Sopenharmony_ci int rxsubchans = state->rxsubchans; 4068c2ecf20Sopenharmony_ci int newnicam = state->nicam_on; 4078c2ecf20Sopenharmony_ci int update = 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci switch (state->mode) { 4108c2ecf20Sopenharmony_ci case MSP_MODE_FM_TERRA: 4118c2ecf20Sopenharmony_ci val = msp_read_dsp(client, 0x18); 4128c2ecf20Sopenharmony_ci if (val > 32767) 4138c2ecf20Sopenharmony_ci val -= 65536; 4148c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, 4158c2ecf20Sopenharmony_ci "stereo detect register: %d\n", val); 4168c2ecf20Sopenharmony_ci if (val > 8192) { 4178c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_STEREO; 4188c2ecf20Sopenharmony_ci } else if (val < -4096) { 4198c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 4208c2ecf20Sopenharmony_ci } else { 4218c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_MONO; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci newnicam = 0; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case MSP_MODE_FM_NICAM1: 4268c2ecf20Sopenharmony_ci case MSP_MODE_FM_NICAM2: 4278c2ecf20Sopenharmony_ci case MSP_MODE_AM_NICAM: 4288c2ecf20Sopenharmony_ci val = msp_read_dem(client, 0x23); 4298c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "nicam sync=%d, mode=%d\n", 4308c2ecf20Sopenharmony_ci val & 1, (val & 0x1e) >> 1); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (val & 1) { 4338c2ecf20Sopenharmony_ci /* nicam synced */ 4348c2ecf20Sopenharmony_ci switch ((val & 0x1e) >> 1) { 4358c2ecf20Sopenharmony_ci case 0: 4368c2ecf20Sopenharmony_ci case 8: 4378c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_STEREO; 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci case 1: 4408c2ecf20Sopenharmony_ci case 9: 4418c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_MONO; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci case 2: 4448c2ecf20Sopenharmony_ci case 10: 4458c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci default: 4488c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_MONO; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci newnicam = 1; 4528c2ecf20Sopenharmony_ci } else { 4538c2ecf20Sopenharmony_ci newnicam = 0; 4548c2ecf20Sopenharmony_ci rxsubchans = V4L2_TUNER_SUB_MONO; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci if (rxsubchans != state->rxsubchans) { 4598c2ecf20Sopenharmony_ci update = 1; 4608c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 4618c2ecf20Sopenharmony_ci "watch: rxsubchans %02x => %02x\n", 4628c2ecf20Sopenharmony_ci state->rxsubchans, rxsubchans); 4638c2ecf20Sopenharmony_ci state->rxsubchans = rxsubchans; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci if (newnicam != state->nicam_on) { 4668c2ecf20Sopenharmony_ci update = 1; 4678c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "watch: nicam %d => %d\n", 4688c2ecf20Sopenharmony_ci state->nicam_on, newnicam); 4698c2ecf20Sopenharmony_ci state->nicam_on = newnicam; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci return update; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/* 4758c2ecf20Sopenharmony_ci * A kernel thread for msp3400 control -- we don't want to block the 4768c2ecf20Sopenharmony_ci * in the ioctl while doing the sound carrier & stereo detect 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci/* stereo/multilang monitoring */ 4798c2ecf20Sopenharmony_cistatic void watch_stereo(struct i2c_client *client) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (msp_detect_stereo(client)) 4848c2ecf20Sopenharmony_ci msp_set_audmode(client); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (msp_once) 4878c2ecf20Sopenharmony_ci state->watch_stereo = 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ciint msp3400c_thread(void *data) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct i2c_client *client = data; 4938c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 4948c2ecf20Sopenharmony_ci struct msp3400c_carrier_detect *cd; 4958c2ecf20Sopenharmony_ci int count, max1, max2, val1, val2, val, i; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "msp3400 daemon started\n"); 4988c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_ALL; 4998c2ecf20Sopenharmony_ci set_freezable(); 5008c2ecf20Sopenharmony_ci for (;;) { 5018c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "msp3400 thread: sleep\n"); 5028c2ecf20Sopenharmony_ci msp_sleep(state, -1); 5038c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "msp3400 thread: wakeup\n"); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cirestart: 5068c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "thread: restart scan\n"); 5078c2ecf20Sopenharmony_ci state->restart = 0; 5088c2ecf20Sopenharmony_ci if (kthread_should_stop()) 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (state->radio || MSP_MODE_EXTERN == state->mode) { 5128c2ecf20Sopenharmony_ci /* no carrier scan, just unmute */ 5138c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 5148c2ecf20Sopenharmony_ci "thread: no carrier scan\n"); 5158c2ecf20Sopenharmony_ci state->scan_in_progress = 0; 5168c2ecf20Sopenharmony_ci msp_update_volume(state); 5178c2ecf20Sopenharmony_ci continue; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* mute audio */ 5218c2ecf20Sopenharmony_ci state->scan_in_progress = 1; 5228c2ecf20Sopenharmony_ci msp_update_volume(state); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_AM_DETECT); 5258c2ecf20Sopenharmony_ci val1 = val2 = 0; 5268c2ecf20Sopenharmony_ci max1 = max2 = -1; 5278c2ecf20Sopenharmony_ci state->watch_stereo = 0; 5288c2ecf20Sopenharmony_ci state->nicam_on = 0; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* wait for tuner to settle down after a channel change */ 5318c2ecf20Sopenharmony_ci if (msp_sleep(state, 200)) 5328c2ecf20Sopenharmony_ci goto restart; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* carrier detect pass #1 -- main carrier */ 5358c2ecf20Sopenharmony_ci cd = msp3400c_carrier_detect_main; 5368c2ecf20Sopenharmony_ci count = ARRAY_SIZE(msp3400c_carrier_detect_main); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { 5398c2ecf20Sopenharmony_ci /* autodetect doesn't work well with AM ... */ 5408c2ecf20Sopenharmony_ci max1 = 3; 5418c2ecf20Sopenharmony_ci count = 0; 5428c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "AM sound override\n"); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 5468c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); 5478c2ecf20Sopenharmony_ci if (msp_sleep(state, 100)) 5488c2ecf20Sopenharmony_ci goto restart; 5498c2ecf20Sopenharmony_ci val = msp_read_dsp(client, 0x1b); 5508c2ecf20Sopenharmony_ci if (val > 32767) 5518c2ecf20Sopenharmony_ci val -= 65536; 5528c2ecf20Sopenharmony_ci if (val1 < val) 5538c2ecf20Sopenharmony_ci val1 = val, max1 = i; 5548c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 5558c2ecf20Sopenharmony_ci "carrier1 val: %5d / %s\n", val, cd[i].name); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* carrier detect pass #2 -- second (stereo) carrier */ 5598c2ecf20Sopenharmony_ci switch (max1) { 5608c2ecf20Sopenharmony_ci case 1: /* 5.5 */ 5618c2ecf20Sopenharmony_ci cd = msp3400c_carrier_detect_55; 5628c2ecf20Sopenharmony_ci count = ARRAY_SIZE(msp3400c_carrier_detect_55); 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci case 3: /* 6.5 */ 5658c2ecf20Sopenharmony_ci cd = msp3400c_carrier_detect_65; 5668c2ecf20Sopenharmony_ci count = ARRAY_SIZE(msp3400c_carrier_detect_65); 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci case 0: /* 4.5 */ 5698c2ecf20Sopenharmony_ci case 2: /* 6.0 */ 5708c2ecf20Sopenharmony_ci default: 5718c2ecf20Sopenharmony_ci cd = NULL; 5728c2ecf20Sopenharmony_ci count = 0; 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { 5778c2ecf20Sopenharmony_ci /* autodetect doesn't work well with AM ... */ 5788c2ecf20Sopenharmony_ci cd = NULL; 5798c2ecf20Sopenharmony_ci count = 0; 5808c2ecf20Sopenharmony_ci max2 = 0; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 5838c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); 5848c2ecf20Sopenharmony_ci if (msp_sleep(state, 100)) 5858c2ecf20Sopenharmony_ci goto restart; 5868c2ecf20Sopenharmony_ci val = msp_read_dsp(client, 0x1b); 5878c2ecf20Sopenharmony_ci if (val > 32767) 5888c2ecf20Sopenharmony_ci val -= 65536; 5898c2ecf20Sopenharmony_ci if (val2 < val) 5908c2ecf20Sopenharmony_ci val2 = val, max2 = i; 5918c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 5928c2ecf20Sopenharmony_ci "carrier2 val: %5d / %s\n", val, cd[i].name); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* program the msp3400 according to the results */ 5968c2ecf20Sopenharmony_ci state->main = msp3400c_carrier_detect_main[max1].cdo; 5978c2ecf20Sopenharmony_ci switch (max1) { 5988c2ecf20Sopenharmony_ci case 1: /* 5.5 */ 5998c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H; 6008c2ecf20Sopenharmony_ci if (max2 == 0) { 6018c2ecf20Sopenharmony_ci /* B/G FM-stereo */ 6028c2ecf20Sopenharmony_ci state->second = msp3400c_carrier_detect_55[max2].cdo; 6038c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_FM_TERRA); 6048c2ecf20Sopenharmony_ci state->watch_stereo = 1; 6058c2ecf20Sopenharmony_ci } else if (max2 == 1 && state->has_nicam) { 6068c2ecf20Sopenharmony_ci /* B/G NICAM */ 6078c2ecf20Sopenharmony_ci state->second = msp3400c_carrier_detect_55[max2].cdo; 6088c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); 6098c2ecf20Sopenharmony_ci state->nicam_on = 1; 6108c2ecf20Sopenharmony_ci state->watch_stereo = 1; 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci goto no_second; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci case 2: /* 6.0 */ 6168c2ecf20Sopenharmony_ci /* PAL I NICAM */ 6178c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_PAL_I; 6188c2ecf20Sopenharmony_ci state->second = MSP_CARRIER(6.552); 6198c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); 6208c2ecf20Sopenharmony_ci state->nicam_on = 1; 6218c2ecf20Sopenharmony_ci state->watch_stereo = 1; 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci case 3: /* 6.5 */ 6248c2ecf20Sopenharmony_ci if (max2 == 1 || max2 == 2) { 6258c2ecf20Sopenharmony_ci /* D/K FM-stereo */ 6268c2ecf20Sopenharmony_ci state->second = msp3400c_carrier_detect_65[max2].cdo; 6278c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_FM_TERRA); 6288c2ecf20Sopenharmony_ci state->watch_stereo = 1; 6298c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_DK; 6308c2ecf20Sopenharmony_ci } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { 6318c2ecf20Sopenharmony_ci /* L NICAM or AM-mono */ 6328c2ecf20Sopenharmony_ci state->second = msp3400c_carrier_detect_65[max2].cdo; 6338c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_AM_NICAM); 6348c2ecf20Sopenharmony_ci state->watch_stereo = 1; 6358c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_L; 6368c2ecf20Sopenharmony_ci } else if (max2 == 0 && state->has_nicam) { 6378c2ecf20Sopenharmony_ci /* D/K NICAM */ 6388c2ecf20Sopenharmony_ci state->second = msp3400c_carrier_detect_65[max2].cdo; 6398c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); 6408c2ecf20Sopenharmony_ci state->nicam_on = 1; 6418c2ecf20Sopenharmony_ci state->watch_stereo = 1; 6428c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_DK; 6438c2ecf20Sopenharmony_ci } else { 6448c2ecf20Sopenharmony_ci goto no_second; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci case 0: /* 4.5 */ 6488c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_MN; 6498c2ecf20Sopenharmony_ci fallthrough; 6508c2ecf20Sopenharmony_ci default: 6518c2ecf20Sopenharmony_cino_second: 6528c2ecf20Sopenharmony_ci state->second = msp3400c_carrier_detect_main[max1].cdo; 6538c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_FM_TERRA); 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, state->second, state->main); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* unmute */ 6598c2ecf20Sopenharmony_ci state->scan_in_progress = 0; 6608c2ecf20Sopenharmony_ci msp3400c_set_audmode(client); 6618c2ecf20Sopenharmony_ci msp_update_volume(state); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (msp_debug) 6648c2ecf20Sopenharmony_ci msp3400c_print_mode(client); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* monitor tv audio mode, the first time don't wait 6678c2ecf20Sopenharmony_ci so long to get a quick stereo/bilingual result */ 6688c2ecf20Sopenharmony_ci count = 3; 6698c2ecf20Sopenharmony_ci while (state->watch_stereo) { 6708c2ecf20Sopenharmony_ci if (msp_sleep(state, count ? 1000 : 5000)) 6718c2ecf20Sopenharmony_ci goto restart; 6728c2ecf20Sopenharmony_ci if (count) 6738c2ecf20Sopenharmony_ci count--; 6748c2ecf20Sopenharmony_ci watch_stereo(client); 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "thread: exit\n"); 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciint msp3410d_thread(void *data) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct i2c_client *client = data; 6858c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 6868c2ecf20Sopenharmony_ci int val, i, std, count; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "msp3410 daemon started\n"); 6898c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_ALL; 6908c2ecf20Sopenharmony_ci set_freezable(); 6918c2ecf20Sopenharmony_ci for (;;) { 6928c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "msp3410 thread: sleep\n"); 6938c2ecf20Sopenharmony_ci msp_sleep(state, -1); 6948c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "msp3410 thread: wakeup\n"); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cirestart: 6978c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "thread: restart scan\n"); 6988c2ecf20Sopenharmony_ci state->restart = 0; 6998c2ecf20Sopenharmony_ci if (kthread_should_stop()) 7008c2ecf20Sopenharmony_ci break; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (state->mode == MSP_MODE_EXTERN) { 7038c2ecf20Sopenharmony_ci /* no carrier scan needed, just unmute */ 7048c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 7058c2ecf20Sopenharmony_ci "thread: no carrier scan\n"); 7068c2ecf20Sopenharmony_ci state->scan_in_progress = 0; 7078c2ecf20Sopenharmony_ci msp_update_volume(state); 7088c2ecf20Sopenharmony_ci continue; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* mute audio */ 7128c2ecf20Sopenharmony_ci state->scan_in_progress = 1; 7138c2ecf20Sopenharmony_ci msp_update_volume(state); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* start autodetect. Note: autodetect is not supported for 7168c2ecf20Sopenharmony_ci NTSC-M and radio, hence we force the standard in those 7178c2ecf20Sopenharmony_ci cases. */ 7188c2ecf20Sopenharmony_ci if (state->radio) 7198c2ecf20Sopenharmony_ci std = 0x40; 7208c2ecf20Sopenharmony_ci else 7218c2ecf20Sopenharmony_ci std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; 7228c2ecf20Sopenharmony_ci state->watch_stereo = 0; 7238c2ecf20Sopenharmony_ci state->nicam_on = 0; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* wait for tuner to settle down after a channel change */ 7268c2ecf20Sopenharmony_ci if (msp_sleep(state, 200)) 7278c2ecf20Sopenharmony_ci goto restart; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (msp_debug) 7308c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, 7318c2ecf20Sopenharmony_ci "setting standard: %s (0x%04x)\n", 7328c2ecf20Sopenharmony_ci msp_standard_std_name(std), std); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (std != 1) { 7358c2ecf20Sopenharmony_ci /* programmed some specific mode */ 7368c2ecf20Sopenharmony_ci val = std; 7378c2ecf20Sopenharmony_ci } else { 7388c2ecf20Sopenharmony_ci /* triggered autodetect */ 7398c2ecf20Sopenharmony_ci msp_write_dem(client, 0x20, std); 7408c2ecf20Sopenharmony_ci for (;;) { 7418c2ecf20Sopenharmony_ci if (msp_sleep(state, 100)) 7428c2ecf20Sopenharmony_ci goto restart; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* check results */ 7458c2ecf20Sopenharmony_ci val = msp_read_dem(client, 0x7e); 7468c2ecf20Sopenharmony_ci if (val < 0x07ff) 7478c2ecf20Sopenharmony_ci break; 7488c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, 7498c2ecf20Sopenharmony_ci "detection still in progress\n"); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci for (i = 0; msp_stdlist[i].name != NULL; i++) 7538c2ecf20Sopenharmony_ci if (msp_stdlist[i].retval == val) 7548c2ecf20Sopenharmony_ci break; 7558c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "current standard: %s (0x%04x)\n", 7568c2ecf20Sopenharmony_ci msp_standard_std_name(val), val); 7578c2ecf20Sopenharmony_ci state->main = msp_stdlist[i].main; 7588c2ecf20Sopenharmony_ci state->second = msp_stdlist[i].second; 7598c2ecf20Sopenharmony_ci state->std = val; 7608c2ecf20Sopenharmony_ci state->rxsubchans = V4L2_TUNER_SUB_MONO; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (msp_amsound && !state->radio && 7638c2ecf20Sopenharmony_ci (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { 7648c2ecf20Sopenharmony_ci /* autodetection has failed, let backup */ 7658c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "autodetection failed, switching to backup standard: %s (0x%04x)\n", 7668c2ecf20Sopenharmony_ci msp_stdlist[8].name ? 7678c2ecf20Sopenharmony_ci msp_stdlist[8].name : "unknown", val); 7688c2ecf20Sopenharmony_ci state->std = val = 0x0009; 7698c2ecf20Sopenharmony_ci msp_write_dem(client, 0x20, val); 7708c2ecf20Sopenharmony_ci } else { 7718c2ecf20Sopenharmony_ci state->detected_std = msp_standard_std(state->std); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* set stereo */ 7758c2ecf20Sopenharmony_ci switch (val) { 7768c2ecf20Sopenharmony_ci case 0x0008: /* B/G NICAM */ 7778c2ecf20Sopenharmony_ci case 0x000a: /* I NICAM */ 7788c2ecf20Sopenharmony_ci case 0x000b: /* D/K NICAM */ 7798c2ecf20Sopenharmony_ci if (val == 0x000a) 7808c2ecf20Sopenharmony_ci state->mode = MSP_MODE_FM_NICAM2; 7818c2ecf20Sopenharmony_ci else 7828c2ecf20Sopenharmony_ci state->mode = MSP_MODE_FM_NICAM1; 7838c2ecf20Sopenharmony_ci /* just turn on stereo */ 7848c2ecf20Sopenharmony_ci state->nicam_on = 1; 7858c2ecf20Sopenharmony_ci state->watch_stereo = 1; 7868c2ecf20Sopenharmony_ci break; 7878c2ecf20Sopenharmony_ci case 0x0009: 7888c2ecf20Sopenharmony_ci state->mode = MSP_MODE_AM_NICAM; 7898c2ecf20Sopenharmony_ci state->nicam_on = 1; 7908c2ecf20Sopenharmony_ci state->watch_stereo = 1; 7918c2ecf20Sopenharmony_ci break; 7928c2ecf20Sopenharmony_ci case 0x0020: /* BTSC */ 7938c2ecf20Sopenharmony_ci /* The pre-'G' models only have BTSC-mono */ 7948c2ecf20Sopenharmony_ci state->mode = MSP_MODE_BTSC; 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci case 0x0040: /* FM radio */ 7978c2ecf20Sopenharmony_ci state->mode = MSP_MODE_FM_RADIO; 7988c2ecf20Sopenharmony_ci state->rxsubchans = V4L2_TUNER_SUB_STEREO; 7998c2ecf20Sopenharmony_ci /* not needed in theory if we have radio, but 8008c2ecf20Sopenharmony_ci short programming enables carrier mute */ 8018c2ecf20Sopenharmony_ci msp3400c_set_mode(client, MSP_MODE_FM_RADIO); 8028c2ecf20Sopenharmony_ci msp3400c_set_carrier(client, MSP_CARRIER(10.7), 8038c2ecf20Sopenharmony_ci MSP_CARRIER(10.7)); 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci case 0x0002: 8068c2ecf20Sopenharmony_ci case 0x0003: 8078c2ecf20Sopenharmony_ci case 0x0004: 8088c2ecf20Sopenharmony_ci case 0x0005: 8098c2ecf20Sopenharmony_ci state->mode = MSP_MODE_FM_TERRA; 8108c2ecf20Sopenharmony_ci state->watch_stereo = 1; 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* set various prescales */ 8158c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0d, 0x1900); /* scart */ 8168c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0e, 0x3000); /* FM */ 8178c2ecf20Sopenharmony_ci if (state->has_nicam) 8188c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (state->has_i2s_conf) 8218c2ecf20Sopenharmony_ci msp_write_dem(client, 0x40, state->i2s_mode); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* unmute */ 8248c2ecf20Sopenharmony_ci msp3400c_set_audmode(client); 8258c2ecf20Sopenharmony_ci state->scan_in_progress = 0; 8268c2ecf20Sopenharmony_ci msp_update_volume(state); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* monitor tv audio mode, the first time don't wait 8298c2ecf20Sopenharmony_ci so long to get a quick stereo/bilingual result */ 8308c2ecf20Sopenharmony_ci count = 3; 8318c2ecf20Sopenharmony_ci while (state->watch_stereo) { 8328c2ecf20Sopenharmony_ci if (msp_sleep(state, count ? 1000 : 5000)) 8338c2ecf20Sopenharmony_ci goto restart; 8348c2ecf20Sopenharmony_ci if (count) 8358c2ecf20Sopenharmony_ci count--; 8368c2ecf20Sopenharmony_ci watch_stereo(client); 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "thread: exit\n"); 8408c2ecf20Sopenharmony_ci return 0; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/* msp34xxG + (autoselect no-thread) 8468c2ecf20Sopenharmony_ci * this one uses both automatic standard detection and automatic sound 8478c2ecf20Sopenharmony_ci * select which are available in the newer G versions 8488c2ecf20Sopenharmony_ci * struct msp: only norm, acb and source are really used in this mode 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic int msp34xxg_modus(struct i2c_client *client) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (state->radio) { 8568c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "selected radio modus\n"); 8578c2ecf20Sopenharmony_ci return 0x0001; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci if (state->v4l2_std == V4L2_STD_NTSC_M_JP) { 8608c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "selected M (EIA-J) modus\n"); 8618c2ecf20Sopenharmony_ci return 0x4001; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci if (state->v4l2_std == V4L2_STD_NTSC_M_KR) { 8648c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "selected M (A2) modus\n"); 8658c2ecf20Sopenharmony_ci return 0x0001; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci if (state->v4l2_std == V4L2_STD_SECAM_L) { 8688c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "selected SECAM-L modus\n"); 8698c2ecf20Sopenharmony_ci return 0x6001; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci if (state->v4l2_std & V4L2_STD_MN) { 8728c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "selected M (BTSC) modus\n"); 8738c2ecf20Sopenharmony_ci return 0x2001; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci return 0x7001; 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 8818c2ecf20Sopenharmony_ci int source, matrix; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci switch (state->audmode) { 8848c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 8858c2ecf20Sopenharmony_ci source = 0; /* mono only */ 8868c2ecf20Sopenharmony_ci matrix = 0x30; 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG2: 8898c2ecf20Sopenharmony_ci source = 4; /* stereo or B */ 8908c2ecf20Sopenharmony_ci matrix = 0x10; 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1_LANG2: 8938c2ecf20Sopenharmony_ci source = 1; /* stereo or A|B */ 8948c2ecf20Sopenharmony_ci matrix = 0x20; 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_LANG1: 8978c2ecf20Sopenharmony_ci source = 3; /* stereo or A */ 8988c2ecf20Sopenharmony_ci matrix = 0x00; 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_STEREO: 9018c2ecf20Sopenharmony_ci default: 9028c2ecf20Sopenharmony_ci source = 3; /* stereo or A */ 9038c2ecf20Sopenharmony_ci matrix = 0x20; 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (in == MSP_DSP_IN_TUNER) 9088c2ecf20Sopenharmony_ci source = (source << 8) | 0x20; 9098c2ecf20Sopenharmony_ci /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 9108c2ecf20Sopenharmony_ci instead of 11, 12, 13. So we add one for that msp version. */ 9118c2ecf20Sopenharmony_ci else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) 9128c2ecf20Sopenharmony_ci source = ((in + 1) << 8) | matrix; 9138c2ecf20Sopenharmony_ci else 9148c2ecf20Sopenharmony_ci source = (in << 8) | matrix; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 9178c2ecf20Sopenharmony_ci "set source to %d (0x%x) for output %02x\n", in, source, reg); 9188c2ecf20Sopenharmony_ci msp_write_dsp(client, reg, source); 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic void msp34xxg_set_sources(struct i2c_client *client) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 9248c2ecf20Sopenharmony_ci u32 in = state->route_in; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); 9278c2ecf20Sopenharmony_ci /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ 9288c2ecf20Sopenharmony_ci msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); 9298c2ecf20Sopenharmony_ci msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); 9308c2ecf20Sopenharmony_ci msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); 9318c2ecf20Sopenharmony_ci if (state->has_scart2_out) 9328c2ecf20Sopenharmony_ci msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); 9338c2ecf20Sopenharmony_ci msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci/* (re-)initialize the msp34xxg */ 9378c2ecf20Sopenharmony_cistatic void msp34xxg_reset(struct i2c_client *client) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 9408c2ecf20Sopenharmony_ci int tuner = (state->route_in >> 3) & 1; 9418c2ecf20Sopenharmony_ci int modus; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* initialize std to 1 (autodetect) to signal that no standard is 9448c2ecf20Sopenharmony_ci selected yet. */ 9458c2ecf20Sopenharmony_ci state->std = 1; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci msp_reset(client); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (state->has_i2s_conf) 9508c2ecf20Sopenharmony_ci msp_write_dem(client, 0x40, state->i2s_mode); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* step-by-step initialisation, as described in the manual */ 9538c2ecf20Sopenharmony_ci modus = msp34xxg_modus(client); 9548c2ecf20Sopenharmony_ci modus |= tuner ? 0x100 : 0; 9558c2ecf20Sopenharmony_ci msp_write_dem(client, 0x30, modus); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* write the dsps that may have an influence on 9588c2ecf20Sopenharmony_ci standard/audio autodetection right now */ 9598c2ecf20Sopenharmony_ci msp34xxg_set_sources(client); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0d, 0x1900); /* scart */ 9628c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0e, 0x3000); /* FM */ 9638c2ecf20Sopenharmony_ci if (state->has_nicam) 9648c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* set identification threshold. Personally, I 9678c2ecf20Sopenharmony_ci * I set it to a higher value than the default 9688c2ecf20Sopenharmony_ci * of 0x190 to ignore noisy stereo signals. 9698c2ecf20Sopenharmony_ci * this needs tuning. (recommended range 0x00a0-0x03c0) 9708c2ecf20Sopenharmony_ci * 0x7f0 = forced mono mode 9718c2ecf20Sopenharmony_ci * 9728c2ecf20Sopenharmony_ci * a2 threshold for stereo/bilingual. 9738c2ecf20Sopenharmony_ci * Note: this register is part of the Manual/Compatibility mode. 9748c2ecf20Sopenharmony_ci * It is supported by all 'G'-family chips. 9758c2ecf20Sopenharmony_ci */ 9768c2ecf20Sopenharmony_ci msp_write_dem(client, 0x22, msp_stereo_thresh); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ciint msp34xxg_thread(void *data) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci struct i2c_client *client = data; 9828c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 9838c2ecf20Sopenharmony_ci int val, i; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "msp34xxg daemon started\n"); 9868c2ecf20Sopenharmony_ci state->detected_std = V4L2_STD_ALL; 9878c2ecf20Sopenharmony_ci set_freezable(); 9888c2ecf20Sopenharmony_ci for (;;) { 9898c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "msp34xxg thread: sleep\n"); 9908c2ecf20Sopenharmony_ci msp_sleep(state, -1); 9918c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, "msp34xxg thread: wakeup\n"); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cirestart: 9948c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "thread: restart scan\n"); 9958c2ecf20Sopenharmony_ci state->restart = 0; 9968c2ecf20Sopenharmony_ci if (kthread_should_stop()) 9978c2ecf20Sopenharmony_ci break; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (state->mode == MSP_MODE_EXTERN) { 10008c2ecf20Sopenharmony_ci /* no carrier scan needed, just unmute */ 10018c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 10028c2ecf20Sopenharmony_ci "thread: no carrier scan\n"); 10038c2ecf20Sopenharmony_ci state->scan_in_progress = 0; 10048c2ecf20Sopenharmony_ci msp_update_volume(state); 10058c2ecf20Sopenharmony_ci continue; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* setup the chip*/ 10098c2ecf20Sopenharmony_ci msp34xxg_reset(client); 10108c2ecf20Sopenharmony_ci state->std = state->radio ? 0x40 : 10118c2ecf20Sopenharmony_ci (state->force_btsc && msp_standard == 1) ? 32 : msp_standard; 10128c2ecf20Sopenharmony_ci msp_write_dem(client, 0x20, state->std); 10138c2ecf20Sopenharmony_ci /* start autodetect */ 10148c2ecf20Sopenharmony_ci if (state->std != 1) 10158c2ecf20Sopenharmony_ci goto unmute; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* watch autodetect */ 10188c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 10198c2ecf20Sopenharmony_ci "started autodetect, waiting for result\n"); 10208c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 10218c2ecf20Sopenharmony_ci if (msp_sleep(state, 100)) 10228c2ecf20Sopenharmony_ci goto restart; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* check results */ 10258c2ecf20Sopenharmony_ci val = msp_read_dem(client, 0x7e); 10268c2ecf20Sopenharmony_ci if (val < 0x07ff) { 10278c2ecf20Sopenharmony_ci state->std = val; 10288c2ecf20Sopenharmony_ci break; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 2, msp_debug, 10318c2ecf20Sopenharmony_ci "detection still in progress\n"); 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci if (state->std == 1) { 10348c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 10358c2ecf20Sopenharmony_ci "detection still in progress after 10 tries. giving up.\n"); 10368c2ecf20Sopenharmony_ci continue; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ciunmute: 10408c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 10418c2ecf20Sopenharmony_ci "detected standard: %s (0x%04x)\n", 10428c2ecf20Sopenharmony_ci msp_standard_std_name(state->std), state->std); 10438c2ecf20Sopenharmony_ci state->detected_std = msp_standard_std(state->std); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (state->std == 9) { 10468c2ecf20Sopenharmony_ci /* AM NICAM mode */ 10478c2ecf20Sopenharmony_ci msp_write_dsp(client, 0x0e, 0x7c00); 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* unmute: dispatch sound to scart output, set scart volume */ 10518c2ecf20Sopenharmony_ci msp_update_volume(state); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* restore ACB */ 10548c2ecf20Sopenharmony_ci if (msp_write_dsp(client, 0x13, state->acb)) 10558c2ecf20Sopenharmony_ci return -1; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* the periodic stereo/SAP check is only relevant for 10588c2ecf20Sopenharmony_ci the 0x20 standard (BTSC) */ 10598c2ecf20Sopenharmony_ci if (state->std != 0x20) 10608c2ecf20Sopenharmony_ci continue; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci state->watch_stereo = 1; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* monitor tv audio mode, the first time don't wait 10658c2ecf20Sopenharmony_ci in order to get a quick stereo/SAP update */ 10668c2ecf20Sopenharmony_ci watch_stereo(client); 10678c2ecf20Sopenharmony_ci while (state->watch_stereo) { 10688c2ecf20Sopenharmony_ci watch_stereo(client); 10698c2ecf20Sopenharmony_ci if (msp_sleep(state, 5000)) 10708c2ecf20Sopenharmony_ci goto restart; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, "thread: exit\n"); 10748c2ecf20Sopenharmony_ci return 0; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic int msp34xxg_detect_stereo(struct i2c_client *client) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 10808c2ecf20Sopenharmony_ci int status = msp_read_dem(client, 0x0200); 10818c2ecf20Sopenharmony_ci int is_bilingual = status & 0x100; 10828c2ecf20Sopenharmony_ci int is_stereo = status & 0x40; 10838c2ecf20Sopenharmony_ci int oldrx = state->rxsubchans; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (state->mode == MSP_MODE_EXTERN) 10868c2ecf20Sopenharmony_ci return 0; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci state->rxsubchans = 0; 10898c2ecf20Sopenharmony_ci if (is_stereo) 10908c2ecf20Sopenharmony_ci state->rxsubchans = V4L2_TUNER_SUB_STEREO; 10918c2ecf20Sopenharmony_ci else 10928c2ecf20Sopenharmony_ci state->rxsubchans = V4L2_TUNER_SUB_MONO; 10938c2ecf20Sopenharmony_ci if (is_bilingual) { 10948c2ecf20Sopenharmony_ci if (state->std == 0x20) 10958c2ecf20Sopenharmony_ci state->rxsubchans |= V4L2_TUNER_SUB_SAP; 10968c2ecf20Sopenharmony_ci else 10978c2ecf20Sopenharmony_ci state->rxsubchans = 10988c2ecf20Sopenharmony_ci V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci dev_dbg_lvl(&client->dev, 1, msp_debug, 11018c2ecf20Sopenharmony_ci "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", 11028c2ecf20Sopenharmony_ci status, is_stereo, is_bilingual, state->rxsubchans); 11038c2ecf20Sopenharmony_ci return (oldrx != state->rxsubchans); 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void msp34xxg_set_audmode(struct i2c_client *client) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if (state->std == 0x20) { 11118c2ecf20Sopenharmony_ci if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && 11128c2ecf20Sopenharmony_ci (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 || 11138c2ecf20Sopenharmony_ci state->audmode == V4L2_TUNER_MODE_LANG2)) { 11148c2ecf20Sopenharmony_ci msp_write_dem(client, 0x20, 0x21); 11158c2ecf20Sopenharmony_ci } else { 11168c2ecf20Sopenharmony_ci msp_write_dem(client, 0x20, 0x20); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci msp34xxg_set_sources(client); 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_civoid msp_set_audmode(struct i2c_client *client) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci switch (state->opmode) { 11288c2ecf20Sopenharmony_ci case OPMODE_MANUAL: 11298c2ecf20Sopenharmony_ci case OPMODE_AUTODETECT: 11308c2ecf20Sopenharmony_ci msp3400c_set_audmode(client); 11318c2ecf20Sopenharmony_ci break; 11328c2ecf20Sopenharmony_ci case OPMODE_AUTOSELECT: 11338c2ecf20Sopenharmony_ci msp34xxg_set_audmode(client); 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ciint msp_detect_stereo(struct i2c_client *client) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci struct msp_state *state = to_state(i2c_get_clientdata(client)); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci switch (state->opmode) { 11438c2ecf20Sopenharmony_ci case OPMODE_MANUAL: 11448c2ecf20Sopenharmony_ci case OPMODE_AUTODETECT: 11458c2ecf20Sopenharmony_ci return msp3400c_detect_stereo(client); 11468c2ecf20Sopenharmony_ci case OPMODE_AUTOSELECT: 11478c2ecf20Sopenharmony_ci return msp34xxg_detect_stereo(client); 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci return 0; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 1152