1047 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver4 *5 * (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version]6 * (c) 2002 Yurij Sysoev <yurij@naturesoft.net>7 * (c) 2003 Gerd Knorr <kraxel@bytesex.org>8 *9 * -----------------------------------------------------------------------10 *11 * Lot of voodoo here. Even the data sheet doesn't help to12 * understand what is going on here, the documentation for the audio13 * part of the cx2388x chip is *very* bad.14 *15 * Some of this comes from party done linux driver sources I got from16 * [undocumented].17 *18 * Some comes from the dscaler sources, one of the dscaler driver guy works19 * for Conexant ...20 *21 * -----------------------------------------------------------------------22 */23 24#include "cx88.h"25 26#include <linux/module.h>27#include <linux/errno.h>28#include <linux/freezer.h>29#include <linux/kernel.h>30#include <linux/mm.h>31#include <linux/poll.h>32#include <linux/signal.h>33#include <linux/ioport.h>34#include <linux/types.h>35#include <linux/interrupt.h>36#include <linux/vmalloc.h>37#include <linux/init.h>38#include <linux/delay.h>39#include <linux/kthread.h>40 41static unsigned int audio_debug;42module_param(audio_debug, int, 0644);43MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]");44 45static unsigned int always_analog;46module_param(always_analog, int, 0644);47MODULE_PARM_DESC(always_analog, "force analog audio out");48 49static unsigned int radio_deemphasis;50module_param(radio_deemphasis, int, 0644);51MODULE_PARM_DESC(radio_deemphasis,52 "Radio deemphasis time constant, 0=None, 1=50us (elsewhere), 2=75us (USA)");53 54#define dprintk(fmt, arg...) do { \55 if (audio_debug) \56 printk(KERN_DEBUG pr_fmt("%s: tvaudio:" fmt), \57 __func__, ##arg); \58} while (0)59/* ----------------------------------------------------------- */60 61static const char * const aud_ctl_names[64] = {62 [EN_BTSC_FORCE_MONO] = "BTSC_FORCE_MONO",63 [EN_BTSC_FORCE_STEREO] = "BTSC_FORCE_STEREO",64 [EN_BTSC_FORCE_SAP] = "BTSC_FORCE_SAP",65 [EN_BTSC_AUTO_STEREO] = "BTSC_AUTO_STEREO",66 [EN_BTSC_AUTO_SAP] = "BTSC_AUTO_SAP",67 [EN_A2_FORCE_MONO1] = "A2_FORCE_MONO1",68 [EN_A2_FORCE_MONO2] = "A2_FORCE_MONO2",69 [EN_A2_FORCE_STEREO] = "A2_FORCE_STEREO",70 [EN_A2_AUTO_MONO2] = "A2_AUTO_MONO2",71 [EN_A2_AUTO_STEREO] = "A2_AUTO_STEREO",72 [EN_EIAJ_FORCE_MONO1] = "EIAJ_FORCE_MONO1",73 [EN_EIAJ_FORCE_MONO2] = "EIAJ_FORCE_MONO2",74 [EN_EIAJ_FORCE_STEREO] = "EIAJ_FORCE_STEREO",75 [EN_EIAJ_AUTO_MONO2] = "EIAJ_AUTO_MONO2",76 [EN_EIAJ_AUTO_STEREO] = "EIAJ_AUTO_STEREO",77 [EN_NICAM_FORCE_MONO1] = "NICAM_FORCE_MONO1",78 [EN_NICAM_FORCE_MONO2] = "NICAM_FORCE_MONO2",79 [EN_NICAM_FORCE_STEREO] = "NICAM_FORCE_STEREO",80 [EN_NICAM_AUTO_MONO2] = "NICAM_AUTO_MONO2",81 [EN_NICAM_AUTO_STEREO] = "NICAM_AUTO_STEREO",82 [EN_FMRADIO_FORCE_MONO] = "FMRADIO_FORCE_MONO",83 [EN_FMRADIO_FORCE_STEREO] = "FMRADIO_FORCE_STEREO",84 [EN_FMRADIO_AUTO_STEREO] = "FMRADIO_AUTO_STEREO",85};86 87struct rlist {88 u32 reg;89 u32 val;90};91 92static void set_audio_registers(struct cx88_core *core, const struct rlist *l)93{94 int i;95 96 for (i = 0; l[i].reg; i++) {97 switch (l[i].reg) {98 case AUD_PDF_DDS_CNST_BYTE2:99 case AUD_PDF_DDS_CNST_BYTE1:100 case AUD_PDF_DDS_CNST_BYTE0:101 case AUD_QAM_MODE:102 case AUD_PHACC_FREQ_8MSB:103 case AUD_PHACC_FREQ_8LSB:104 cx_writeb(l[i].reg, l[i].val);105 break;106 default:107 cx_write(l[i].reg, l[i].val);108 break;109 }110 }111}112 113static void set_audio_start(struct cx88_core *core, u32 mode)114{115 /* mute */116 cx_write(AUD_VOL_CTL, (1 << 6));117 118 /* start programming */119 cx_write(AUD_INIT, mode);120 cx_write(AUD_INIT_LD, 0x0001);121 cx_write(AUD_SOFT_RESET, 0x0001);122}123 124static void set_audio_finish(struct cx88_core *core, u32 ctl)125{126 u32 volume;127 128 /* restart dma; This avoids buzz in NICAM and is good in others */129 cx88_stop_audio_dma(core);130 cx_write(AUD_RATE_THRES_DMD, 0x000000C0);131 cx88_start_audio_dma(core);132 133 if (core->board.mpeg & CX88_MPEG_BLACKBIRD) {134 cx_write(AUD_I2SINPUTCNTL, 4);135 cx_write(AUD_BAUDRATE, 1);136 /*137 * 'pass-thru mode': this enables the i2s138 * output to the mpeg encoder139 */140 cx_set(AUD_CTL, EN_I2SOUT_ENABLE);141 cx_write(AUD_I2SOUTPUTCNTL, 1);142 cx_write(AUD_I2SCNTL, 0);143 /* cx_write(AUD_APB_IN_RATE_ADJ, 0); */144 }145 if ((always_analog) || (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))) {146 ctl |= EN_DAC_ENABLE;147 cx_write(AUD_CTL, ctl);148 }149 150 /* finish programming */151 cx_write(AUD_SOFT_RESET, 0x0000);152 153 /* unmute */154 volume = cx_sread(SHADOW_AUD_VOL_CTL);155 cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume);156 157 core->last_change = jiffies;158}159 160/* ----------------------------------------------------------- */161 162static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap,163 u32 mode)164{165 static const struct rlist btsc[] = {166 {AUD_AFE_12DB_EN, 0x00000001},167 {AUD_OUT1_SEL, 0x00000013},168 {AUD_OUT1_SHIFT, 0x00000000},169 {AUD_POLY0_DDS_CONSTANT, 0x0012010c},170 {AUD_DMD_RA_DDS, 0x00c3e7aa},171 {AUD_DBX_IN_GAIN, 0x00004734},172 {AUD_DBX_WBE_GAIN, 0x00004640},173 {AUD_DBX_SE_GAIN, 0x00008d31},174 {AUD_DCOC_0_SRC, 0x0000001a},175 {AUD_IIR1_4_SEL, 0x00000021},176 {AUD_DCOC_PASS_IN, 0x00000003},177 {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},178 {AUD_DCOC_0_SHIFT_IN1, 0x00000008},179 {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},180 {AUD_DCOC_1_SHIFT_IN1, 0x00000008},181 {AUD_DN0_FREQ, 0x0000283b},182 {AUD_DN2_SRC_SEL, 0x00000008},183 {AUD_DN2_FREQ, 0x00003000},184 {AUD_DN2_AFC, 0x00000002},185 {AUD_DN2_SHFT, 0x00000000},186 {AUD_IIR2_2_SEL, 0x00000020},187 {AUD_IIR2_2_SHIFT, 0x00000000},188 {AUD_IIR2_3_SEL, 0x0000001f},189 {AUD_IIR2_3_SHIFT, 0x00000000},190 {AUD_CRDC1_SRC_SEL, 0x000003ce},191 {AUD_CRDC1_SHIFT, 0x00000000},192 {AUD_CORDIC_SHIFT_1, 0x00000007},193 {AUD_DCOC_1_SRC, 0x0000001b},194 {AUD_DCOC1_SHIFT, 0x00000000},195 {AUD_RDSI_SEL, 0x00000008},196 {AUD_RDSQ_SEL, 0x00000008},197 {AUD_RDSI_SHIFT, 0x00000000},198 {AUD_RDSQ_SHIFT, 0x00000000},199 {AUD_POLYPH80SCALEFAC, 0x00000003},200 { /* end of list */ },201 };202 static const struct rlist btsc_sap[] = {203 {AUD_AFE_12DB_EN, 0x00000001},204 {AUD_DBX_IN_GAIN, 0x00007200},205 {AUD_DBX_WBE_GAIN, 0x00006200},206 {AUD_DBX_SE_GAIN, 0x00006200},207 {AUD_IIR1_1_SEL, 0x00000000},208 {AUD_IIR1_3_SEL, 0x00000001},209 {AUD_DN1_SRC_SEL, 0x00000007},210 {AUD_IIR1_4_SHIFT, 0x00000006},211 {AUD_IIR2_1_SHIFT, 0x00000000},212 {AUD_IIR2_2_SHIFT, 0x00000000},213 {AUD_IIR3_0_SHIFT, 0x00000000},214 {AUD_IIR3_1_SHIFT, 0x00000000},215 {AUD_IIR3_0_SEL, 0x0000000d},216 {AUD_IIR3_1_SEL, 0x0000000e},217 {AUD_DEEMPH1_SRC_SEL, 0x00000014},218 {AUD_DEEMPH1_SHIFT, 0x00000000},219 {AUD_DEEMPH1_G0, 0x00004000},220 {AUD_DEEMPH1_A0, 0x00000000},221 {AUD_DEEMPH1_B0, 0x00000000},222 {AUD_DEEMPH1_A1, 0x00000000},223 {AUD_DEEMPH1_B1, 0x00000000},224 {AUD_OUT0_SEL, 0x0000003f},225 {AUD_OUT1_SEL, 0x0000003f},226 {AUD_DN1_AFC, 0x00000002},227 {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},228 {AUD_DCOC_0_SHIFT_IN1, 0x00000008},229 {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},230 {AUD_DCOC_1_SHIFT_IN1, 0x00000008},231 {AUD_IIR1_0_SEL, 0x0000001d},232 {AUD_IIR1_2_SEL, 0x0000001e},233 {AUD_IIR2_1_SEL, 0x00000002},234 {AUD_IIR2_2_SEL, 0x00000004},235 {AUD_IIR3_2_SEL, 0x0000000f},236 {AUD_DCOC2_SHIFT, 0x00000001},237 {AUD_IIR3_2_SHIFT, 0x00000001},238 {AUD_DEEMPH0_SRC_SEL, 0x00000014},239 {AUD_CORDIC_SHIFT_1, 0x00000006},240 {AUD_POLY0_DDS_CONSTANT, 0x000e4db2},241 {AUD_DMD_RA_DDS, 0x00f696e6},242 {AUD_IIR2_3_SEL, 0x00000025},243 {AUD_IIR1_4_SEL, 0x00000021},244 {AUD_DN1_FREQ, 0x0000c965},245 {AUD_DCOC_PASS_IN, 0x00000003},246 {AUD_DCOC_0_SRC, 0x0000001a},247 {AUD_DCOC_1_SRC, 0x0000001b},248 {AUD_DCOC1_SHIFT, 0x00000000},249 {AUD_RDSI_SEL, 0x00000009},250 {AUD_RDSQ_SEL, 0x00000009},251 {AUD_RDSI_SHIFT, 0x00000000},252 {AUD_RDSQ_SHIFT, 0x00000000},253 {AUD_POLYPH80SCALEFAC, 0x00000003},254 { /* end of list */ },255 };256 257 mode |= EN_FMRADIO_EN_RDS;258 259 if (sap) {260 dprintk("%s SAP (status: unknown)\n", __func__);261 set_audio_start(core, SEL_SAP);262 set_audio_registers(core, btsc_sap);263 set_audio_finish(core, mode);264 } else {265 dprintk("%s (status: known-good)\n", __func__);266 set_audio_start(core, SEL_BTSC);267 set_audio_registers(core, btsc);268 set_audio_finish(core, mode);269 }270}271 272static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode)273{274 static const struct rlist nicam_l[] = {275 {AUD_AFE_12DB_EN, 0x00000001},276 {AUD_RATE_ADJ1, 0x00000060},277 {AUD_RATE_ADJ2, 0x000000F9},278 {AUD_RATE_ADJ3, 0x000001CC},279 {AUD_RATE_ADJ4, 0x000002B3},280 {AUD_RATE_ADJ5, 0x00000726},281 {AUD_DEEMPHDENOM1_R, 0x0000F3D0},282 {AUD_DEEMPHDENOM2_R, 0x00000000},283 {AUD_ERRLOGPERIOD_R, 0x00000064},284 {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},285 {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},286 {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},287 {AUD_POLYPH80SCALEFAC, 0x00000003},288 {AUD_DMD_RA_DDS, 0x00C00000},289 {AUD_PLL_INT, 0x0000001E},290 {AUD_PLL_DDS, 0x00000000},291 {AUD_PLL_FRAC, 0x0000E542},292 {AUD_START_TIMER, 0x00000000},293 {AUD_DEEMPHNUMER1_R, 0x000353DE},294 {AUD_DEEMPHNUMER2_R, 0x000001B1},295 {AUD_PDF_DDS_CNST_BYTE2, 0x06},296 {AUD_PDF_DDS_CNST_BYTE1, 0x82},297 {AUD_PDF_DDS_CNST_BYTE0, 0x12},298 {AUD_QAM_MODE, 0x05},299 {AUD_PHACC_FREQ_8MSB, 0x34},300 {AUD_PHACC_FREQ_8LSB, 0x4C},301 {AUD_DEEMPHGAIN_R, 0x00006680},302 {AUD_RATE_THRES_DMD, 0x000000C0},303 { /* end of list */ },304 };305 306 static const struct rlist nicam_bgdki_common[] = {307 {AUD_AFE_12DB_EN, 0x00000001},308 {AUD_RATE_ADJ1, 0x00000010},309 {AUD_RATE_ADJ2, 0x00000040},310 {AUD_RATE_ADJ3, 0x00000100},311 {AUD_RATE_ADJ4, 0x00000400},312 {AUD_RATE_ADJ5, 0x00001000},313 {AUD_ERRLOGPERIOD_R, 0x00000fff},314 {AUD_ERRINTRPTTHSHLD1_R, 0x000003ff},315 {AUD_ERRINTRPTTHSHLD2_R, 0x000000ff},316 {AUD_ERRINTRPTTHSHLD3_R, 0x0000003f},317 {AUD_POLYPH80SCALEFAC, 0x00000003},318 {AUD_DEEMPHGAIN_R, 0x000023c2},319 {AUD_DEEMPHNUMER1_R, 0x0002a7bc},320 {AUD_DEEMPHNUMER2_R, 0x0003023e},321 {AUD_DEEMPHDENOM1_R, 0x0000f3d0},322 {AUD_DEEMPHDENOM2_R, 0x00000000},323 {AUD_PDF_DDS_CNST_BYTE2, 0x06},324 {AUD_PDF_DDS_CNST_BYTE1, 0x82},325 {AUD_QAM_MODE, 0x05},326 { /* end of list */ },327 };328 329 static const struct rlist nicam_i[] = {330 {AUD_PDF_DDS_CNST_BYTE0, 0x12},331 {AUD_PHACC_FREQ_8MSB, 0x3a},332 {AUD_PHACC_FREQ_8LSB, 0x93},333 { /* end of list */ },334 };335 336 static const struct rlist nicam_default[] = {337 {AUD_PDF_DDS_CNST_BYTE0, 0x16},338 {AUD_PHACC_FREQ_8MSB, 0x34},339 {AUD_PHACC_FREQ_8LSB, 0x4c},340 { /* end of list */ },341 };342 343 set_audio_start(core, SEL_NICAM);344 switch (core->tvaudio) {345 case WW_L:346 dprintk("%s SECAM-L NICAM (status: devel)\n", __func__);347 set_audio_registers(core, nicam_l);348 break;349 case WW_I:350 dprintk("%s PAL-I NICAM (status: known-good)\n", __func__);351 set_audio_registers(core, nicam_bgdki_common);352 set_audio_registers(core, nicam_i);353 break;354 case WW_NONE:355 case WW_BTSC:356 case WW_BG:357 case WW_DK:358 case WW_EIAJ:359 case WW_I2SPT:360 case WW_FM:361 case WW_I2SADC:362 case WW_M:363 dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__);364 set_audio_registers(core, nicam_bgdki_common);365 set_audio_registers(core, nicam_default);366 break;367 }368 369 mode |= EN_DMTRX_LR | EN_DMTRX_BYPASS;370 set_audio_finish(core, mode);371}372 373static void set_audio_standard_A2(struct cx88_core *core, u32 mode)374{375 static const struct rlist a2_bgdk_common[] = {376 {AUD_ERRLOGPERIOD_R, 0x00000064},377 {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},378 {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},379 {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},380 {AUD_PDF_DDS_CNST_BYTE2, 0x06},381 {AUD_PDF_DDS_CNST_BYTE1, 0x82},382 {AUD_PDF_DDS_CNST_BYTE0, 0x12},383 {AUD_QAM_MODE, 0x05},384 {AUD_PHACC_FREQ_8MSB, 0x34},385 {AUD_PHACC_FREQ_8LSB, 0x4c},386 {AUD_RATE_ADJ1, 0x00000100},387 {AUD_RATE_ADJ2, 0x00000200},388 {AUD_RATE_ADJ3, 0x00000300},389 {AUD_RATE_ADJ4, 0x00000400},390 {AUD_RATE_ADJ5, 0x00000500},391 {AUD_THR_FR, 0x00000000},392 {AAGC_HYST, 0x0000001a},393 {AUD_PILOT_BQD_1_K0, 0x0000755b},394 {AUD_PILOT_BQD_1_K1, 0x00551340},395 {AUD_PILOT_BQD_1_K2, 0x006d30be},396 {AUD_PILOT_BQD_1_K3, 0xffd394af},397 {AUD_PILOT_BQD_1_K4, 0x00400000},398 {AUD_PILOT_BQD_2_K0, 0x00040000},399 {AUD_PILOT_BQD_2_K1, 0x002a4841},400 {AUD_PILOT_BQD_2_K2, 0x00400000},401 {AUD_PILOT_BQD_2_K3, 0x00000000},402 {AUD_PILOT_BQD_2_K4, 0x00000000},403 {AUD_MODE_CHG_TIMER, 0x00000040},404 {AUD_AFE_12DB_EN, 0x00000001},405 {AUD_CORDIC_SHIFT_0, 0x00000007},406 {AUD_CORDIC_SHIFT_1, 0x00000007},407 {AUD_DEEMPH0_G0, 0x00000380},408 {AUD_DEEMPH1_G0, 0x00000380},409 {AUD_DCOC_0_SRC, 0x0000001a},410 {AUD_DCOC0_SHIFT, 0x00000000},411 {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},412 {AUD_DCOC_0_SHIFT_IN1, 0x00000008},413 {AUD_DCOC_PASS_IN, 0x00000003},414 {AUD_IIR3_0_SEL, 0x00000021},415 {AUD_DN2_AFC, 0x00000002},416 {AUD_DCOC_1_SRC, 0x0000001b},417 {AUD_DCOC1_SHIFT, 0x00000000},418 {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},419 {AUD_DCOC_1_SHIFT_IN1, 0x00000008},420 {AUD_IIR3_1_SEL, 0x00000023},421 {AUD_RDSI_SEL, 0x00000017},422 {AUD_RDSI_SHIFT, 0x00000000},423 {AUD_RDSQ_SEL, 0x00000017},424 {AUD_RDSQ_SHIFT, 0x00000000},425 {AUD_PLL_INT, 0x0000001e},426 {AUD_PLL_DDS, 0x00000000},427 {AUD_PLL_FRAC, 0x0000e542},428 {AUD_POLYPH80SCALEFAC, 0x00000001},429 {AUD_START_TIMER, 0x00000000},430 { /* end of list */ },431 };432 433 static const struct rlist a2_bg[] = {434 {AUD_DMD_RA_DDS, 0x002a4f2f},435 {AUD_C1_UP_THR, 0x00007000},436 {AUD_C1_LO_THR, 0x00005400},437 {AUD_C2_UP_THR, 0x00005400},438 {AUD_C2_LO_THR, 0x00003000},439 { /* end of list */ },440 };441 442 static const struct rlist a2_dk[] = {443 {AUD_DMD_RA_DDS, 0x002a4f2f},444 {AUD_C1_UP_THR, 0x00007000},445 {AUD_C1_LO_THR, 0x00005400},446 {AUD_C2_UP_THR, 0x00005400},447 {AUD_C2_LO_THR, 0x00003000},448 {AUD_DN0_FREQ, 0x00003a1c},449 {AUD_DN2_FREQ, 0x0000d2e0},450 { /* end of list */ },451 };452 453 static const struct rlist a1_i[] = {454 {AUD_ERRLOGPERIOD_R, 0x00000064},455 {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},456 {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},457 {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},458 {AUD_PDF_DDS_CNST_BYTE2, 0x06},459 {AUD_PDF_DDS_CNST_BYTE1, 0x82},460 {AUD_PDF_DDS_CNST_BYTE0, 0x12},461 {AUD_QAM_MODE, 0x05},462 {AUD_PHACC_FREQ_8MSB, 0x3a},463 {AUD_PHACC_FREQ_8LSB, 0x93},464 {AUD_DMD_RA_DDS, 0x002a4f2f},465 {AUD_PLL_INT, 0x0000001e},466 {AUD_PLL_DDS, 0x00000004},467 {AUD_PLL_FRAC, 0x0000e542},468 {AUD_RATE_ADJ1, 0x00000100},469 {AUD_RATE_ADJ2, 0x00000200},470 {AUD_RATE_ADJ3, 0x00000300},471 {AUD_RATE_ADJ4, 0x00000400},472 {AUD_RATE_ADJ5, 0x00000500},473 {AUD_THR_FR, 0x00000000},474 {AUD_PILOT_BQD_1_K0, 0x0000755b},475 {AUD_PILOT_BQD_1_K1, 0x00551340},476 {AUD_PILOT_BQD_1_K2, 0x006d30be},477 {AUD_PILOT_BQD_1_K3, 0xffd394af},478 {AUD_PILOT_BQD_1_K4, 0x00400000},479 {AUD_PILOT_BQD_2_K0, 0x00040000},480 {AUD_PILOT_BQD_2_K1, 0x002a4841},481 {AUD_PILOT_BQD_2_K2, 0x00400000},482 {AUD_PILOT_BQD_2_K3, 0x00000000},483 {AUD_PILOT_BQD_2_K4, 0x00000000},484 {AUD_MODE_CHG_TIMER, 0x00000060},485 {AUD_AFE_12DB_EN, 0x00000001},486 {AAGC_HYST, 0x0000000a},487 {AUD_CORDIC_SHIFT_0, 0x00000007},488 {AUD_CORDIC_SHIFT_1, 0x00000007},489 {AUD_C1_UP_THR, 0x00007000},490 {AUD_C1_LO_THR, 0x00005400},491 {AUD_C2_UP_THR, 0x00005400},492 {AUD_C2_LO_THR, 0x00003000},493 {AUD_DCOC_0_SRC, 0x0000001a},494 {AUD_DCOC0_SHIFT, 0x00000000},495 {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},496 {AUD_DCOC_0_SHIFT_IN1, 0x00000008},497 {AUD_DCOC_PASS_IN, 0x00000003},498 {AUD_IIR3_0_SEL, 0x00000021},499 {AUD_DN2_AFC, 0x00000002},500 {AUD_DCOC_1_SRC, 0x0000001b},501 {AUD_DCOC1_SHIFT, 0x00000000},502 {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},503 {AUD_DCOC_1_SHIFT_IN1, 0x00000008},504 {AUD_IIR3_1_SEL, 0x00000023},505 {AUD_DN0_FREQ, 0x000035a3},506 {AUD_DN2_FREQ, 0x000029c7},507 {AUD_CRDC0_SRC_SEL, 0x00000511},508 {AUD_IIR1_0_SEL, 0x00000001},509 {AUD_IIR1_1_SEL, 0x00000000},510 {AUD_IIR3_2_SEL, 0x00000003},511 {AUD_IIR3_2_SHIFT, 0x00000000},512 {AUD_IIR3_0_SEL, 0x00000002},513 {AUD_IIR2_0_SEL, 0x00000021},514 {AUD_IIR2_0_SHIFT, 0x00000002},515 {AUD_DEEMPH0_SRC_SEL, 0x0000000b},516 {AUD_DEEMPH1_SRC_SEL, 0x0000000b},517 {AUD_POLYPH80SCALEFAC, 0x00000001},518 {AUD_START_TIMER, 0x00000000},519 { /* end of list */ },520 };521 522 static const struct rlist am_l[] = {523 {AUD_ERRLOGPERIOD_R, 0x00000064},524 {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},525 {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},526 {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},527 {AUD_PDF_DDS_CNST_BYTE2, 0x48},528 {AUD_PDF_DDS_CNST_BYTE1, 0x3D},529 {AUD_QAM_MODE, 0x00},530 {AUD_PDF_DDS_CNST_BYTE0, 0xf5},531 {AUD_PHACC_FREQ_8MSB, 0x3a},532 {AUD_PHACC_FREQ_8LSB, 0x4a},533 {AUD_DEEMPHGAIN_R, 0x00006680},534 {AUD_DEEMPHNUMER1_R, 0x000353DE},535 {AUD_DEEMPHNUMER2_R, 0x000001B1},536 {AUD_DEEMPHDENOM1_R, 0x0000F3D0},537 {AUD_DEEMPHDENOM2_R, 0x00000000},538 {AUD_FM_MODE_ENABLE, 0x00000007},539 {AUD_POLYPH80SCALEFAC, 0x00000003},540 {AUD_AFE_12DB_EN, 0x00000001},541 {AAGC_GAIN, 0x00000000},542 {AAGC_HYST, 0x00000018},543 {AAGC_DEF, 0x00000020},544 {AUD_DN0_FREQ, 0x00000000},545 {AUD_POLY0_DDS_CONSTANT, 0x000E4DB2},546 {AUD_DCOC_0_SRC, 0x00000021},547 {AUD_IIR1_0_SEL, 0x00000000},548 {AUD_IIR1_0_SHIFT, 0x00000007},549 {AUD_IIR1_1_SEL, 0x00000002},550 {AUD_IIR1_1_SHIFT, 0x00000000},551 {AUD_DCOC_1_SRC, 0x00000003},552 {AUD_DCOC1_SHIFT, 0x00000000},553 {AUD_DCOC_PASS_IN, 0x00000000},554 {AUD_IIR1_2_SEL, 0x00000023},555 {AUD_IIR1_2_SHIFT, 0x00000000},556 {AUD_IIR1_3_SEL, 0x00000004},557 {AUD_IIR1_3_SHIFT, 0x00000007},558 {AUD_IIR1_4_SEL, 0x00000005},559 {AUD_IIR1_4_SHIFT, 0x00000007},560 {AUD_IIR3_0_SEL, 0x00000007},561 {AUD_IIR3_0_SHIFT, 0x00000000},562 {AUD_DEEMPH0_SRC_SEL, 0x00000011},563 {AUD_DEEMPH0_SHIFT, 0x00000000},564 {AUD_DEEMPH0_G0, 0x00007000},565 {AUD_DEEMPH0_A0, 0x00000000},566 {AUD_DEEMPH0_B0, 0x00000000},567 {AUD_DEEMPH0_A1, 0x00000000},568 {AUD_DEEMPH0_B1, 0x00000000},569 {AUD_DEEMPH1_SRC_SEL, 0x00000011},570 {AUD_DEEMPH1_SHIFT, 0x00000000},571 {AUD_DEEMPH1_G0, 0x00007000},572 {AUD_DEEMPH1_A0, 0x00000000},573 {AUD_DEEMPH1_B0, 0x00000000},574 {AUD_DEEMPH1_A1, 0x00000000},575 {AUD_DEEMPH1_B1, 0x00000000},576 {AUD_OUT0_SEL, 0x0000003F},577 {AUD_OUT1_SEL, 0x0000003F},578 {AUD_DMD_RA_DDS, 0x00F5C285},579 {AUD_PLL_INT, 0x0000001E},580 {AUD_PLL_DDS, 0x00000000},581 {AUD_PLL_FRAC, 0x0000E542},582 {AUD_RATE_ADJ1, 0x00000100},583 {AUD_RATE_ADJ2, 0x00000200},584 {AUD_RATE_ADJ3, 0x00000300},585 {AUD_RATE_ADJ4, 0x00000400},586 {AUD_RATE_ADJ5, 0x00000500},587 {AUD_RATE_THRES_DMD, 0x000000C0},588 { /* end of list */ },589 };590 591 static const struct rlist a2_deemph50[] = {592 {AUD_DEEMPH0_G0, 0x00000380},593 {AUD_DEEMPH1_G0, 0x00000380},594 {AUD_DEEMPHGAIN_R, 0x000011e1},595 {AUD_DEEMPHNUMER1_R, 0x0002a7bc},596 {AUD_DEEMPHNUMER2_R, 0x0003023c},597 { /* end of list */ },598 };599 600 set_audio_start(core, SEL_A2);601 switch (core->tvaudio) {602 case WW_BG:603 dprintk("%s PAL-BG A1/2 (status: known-good)\n", __func__);604 set_audio_registers(core, a2_bgdk_common);605 set_audio_registers(core, a2_bg);606 set_audio_registers(core, a2_deemph50);607 break;608 case WW_DK:609 dprintk("%s PAL-DK A1/2 (status: known-good)\n", __func__);610 set_audio_registers(core, a2_bgdk_common);611 set_audio_registers(core, a2_dk);612 set_audio_registers(core, a2_deemph50);613 break;614 case WW_I:615 dprintk("%s PAL-I A1 (status: known-good)\n", __func__);616 set_audio_registers(core, a1_i);617 set_audio_registers(core, a2_deemph50);618 break;619 case WW_L:620 dprintk("%s AM-L (status: devel)\n", __func__);621 set_audio_registers(core, am_l);622 break;623 case WW_NONE:624 case WW_BTSC:625 case WW_EIAJ:626 case WW_I2SPT:627 case WW_FM:628 case WW_I2SADC:629 case WW_M:630 dprintk("%s Warning: wrong value\n", __func__);631 return;632 }633 634 mode |= EN_FMRADIO_EN_RDS | EN_DMTRX_SUMDIFF;635 set_audio_finish(core, mode);636}637 638static void set_audio_standard_EIAJ(struct cx88_core *core)639{640 static const struct rlist eiaj[] = {641 /* TODO: eiaj register settings are not there yet ... */642 643 { /* end of list */ },644 };645 dprintk("%s (status: unknown)\n", __func__);646 647 set_audio_start(core, SEL_EIAJ);648 set_audio_registers(core, eiaj);649 set_audio_finish(core, EN_EIAJ_AUTO_STEREO);650}651 652static void set_audio_standard_FM(struct cx88_core *core,653 enum cx88_deemph_type deemph)654{655 static const struct rlist fm_deemph_50[] = {656 {AUD_DEEMPH0_G0, 0x0C45},657 {AUD_DEEMPH0_A0, 0x6262},658 {AUD_DEEMPH0_B0, 0x1C29},659 {AUD_DEEMPH0_A1, 0x3FC66},660 {AUD_DEEMPH0_B1, 0x399A},661 662 {AUD_DEEMPH1_G0, 0x0D80},663 {AUD_DEEMPH1_A0, 0x6262},664 {AUD_DEEMPH1_B0, 0x1C29},665 {AUD_DEEMPH1_A1, 0x3FC66},666 {AUD_DEEMPH1_B1, 0x399A},667 668 {AUD_POLYPH80SCALEFAC, 0x0003},669 { /* end of list */ },670 };671 static const struct rlist fm_deemph_75[] = {672 {AUD_DEEMPH0_G0, 0x091B},673 {AUD_DEEMPH0_A0, 0x6B68},674 {AUD_DEEMPH0_B0, 0x11EC},675 {AUD_DEEMPH0_A1, 0x3FC66},676 {AUD_DEEMPH0_B1, 0x399A},677 678 {AUD_DEEMPH1_G0, 0x0AA0},679 {AUD_DEEMPH1_A0, 0x6B68},680 {AUD_DEEMPH1_B0, 0x11EC},681 {AUD_DEEMPH1_A1, 0x3FC66},682 {AUD_DEEMPH1_B1, 0x399A},683 684 {AUD_POLYPH80SCALEFAC, 0x0003},685 { /* end of list */ },686 };687 688 /*689 * It is enough to leave default values?690 *691 * No, it's not! The deemphasis registers are reset to the 75us692 * values by default. Analyzing the spectrum of the decoded audio693 * reveals that "no deemphasis" is the same as 75 us, while the 50 us694 * setting results in less deemphasis.695 */696 static const struct rlist fm_no_deemph[] = {697 {AUD_POLYPH80SCALEFAC, 0x0003},698 { /* end of list */ },699 };700 701 dprintk("%s (status: unknown)\n", __func__);702 set_audio_start(core, SEL_FMRADIO);703 704 switch (deemph) {705 default:706 case FM_NO_DEEMPH:707 set_audio_registers(core, fm_no_deemph);708 break;709 710 case FM_DEEMPH_50:711 set_audio_registers(core, fm_deemph_50);712 break;713 714 case FM_DEEMPH_75:715 set_audio_registers(core, fm_deemph_75);716 break;717 }718 719 set_audio_finish(core, EN_FMRADIO_AUTO_STEREO);720}721 722/* ----------------------------------------------------------- */723 724static int cx88_detect_nicam(struct cx88_core *core)725{726 int i, j = 0;727 728 dprintk("start nicam autodetect.\n");729 730 for (i = 0; i < 6; i++) {731 /* if bit1=1 then nicam is detected */732 j += ((cx_read(AUD_NICAM_STATUS2) & 0x02) >> 1);733 734 if (j == 1) {735 dprintk("nicam is detected.\n");736 return 1;737 }738 739 /* wait a little bit for next reading status */740 usleep_range(10000, 20000);741 }742 743 dprintk("nicam is not detected.\n");744 return 0;745}746 747void cx88_set_tvaudio(struct cx88_core *core)748{749 switch (core->tvaudio) {750 case WW_BTSC:751 set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);752 break;753 case WW_BG:754 case WW_DK:755 case WW_M:756 case WW_I:757 case WW_L:758 /* prepare all dsp registers */759 set_audio_standard_A2(core, EN_A2_FORCE_MONO1);760 761 /*762 * set nicam mode - otherwise763 * AUD_NICAM_STATUS2 contains wrong values764 */765 set_audio_standard_NICAM(core, EN_NICAM_AUTO_STEREO);766 if (cx88_detect_nicam(core) == 0) {767 /* fall back to fm / am mono */768 set_audio_standard_A2(core, EN_A2_FORCE_MONO1);769 core->audiomode_current = V4L2_TUNER_MODE_MONO;770 core->use_nicam = 0;771 } else {772 core->use_nicam = 1;773 }774 break;775 case WW_EIAJ:776 set_audio_standard_EIAJ(core);777 break;778 case WW_FM:779 set_audio_standard_FM(core, radio_deemphasis);780 break;781 case WW_I2SADC:782 set_audio_start(core, 0x01);783 /*784 * Slave/Philips/Autobaud785 * NB on Nova-S bit1 NPhilipsSony appears to be inverted:786 * 0= Sony, 1=Philips787 */788 cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl);789 /* Switch to "I2S ADC mode" */790 cx_write(AUD_I2SCNTL, 0x1);791 set_audio_finish(core, EN_I2SIN_ENABLE);792 break;793 case WW_NONE:794 case WW_I2SPT:795 pr_info("unknown tv audio mode [%d]\n", core->tvaudio);796 break;797 }798}799EXPORT_SYMBOL(cx88_set_tvaudio);800 801void cx88_newstation(struct cx88_core *core)802{803 core->audiomode_manual = UNSET;804 core->last_change = jiffies;805}806EXPORT_SYMBOL(cx88_newstation);807 808void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)809{810 static const char * const m[] = { "stereo", "dual mono",811 "mono", "sap" };812 static const char * const p[] = { "no pilot", "pilot c1",813 "pilot c2", "?" };814 u32 reg, mode, pilot;815 816 reg = cx_read(AUD_STATUS);817 mode = reg & 0x03;818 pilot = (reg >> 2) & 0x03;819 820 if (core->astat != reg)821 dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",822 reg, m[mode], p[pilot],823 aud_ctl_names[cx_read(AUD_CTL) & 63]);824 core->astat = reg;825 826 t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |827 V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;828 t->rxsubchans = UNSET;829 t->audmode = V4L2_TUNER_MODE_MONO;830 831 switch (mode) {832 case 0:833 t->audmode = V4L2_TUNER_MODE_STEREO;834 break;835 case 1:836 t->audmode = V4L2_TUNER_MODE_LANG2;837 break;838 case 2:839 t->audmode = V4L2_TUNER_MODE_MONO;840 break;841 case 3:842 t->audmode = V4L2_TUNER_MODE_SAP;843 break;844 }845 846 switch (core->tvaudio) {847 case WW_BTSC:848 case WW_BG:849 case WW_DK:850 case WW_M:851 case WW_EIAJ:852 if (!core->use_nicam) {853 t->rxsubchans = cx88_dsp_detect_stereo_sap(core);854 break;855 }856 break;857 case WW_NONE:858 case WW_I:859 case WW_L:860 case WW_I2SPT:861 case WW_FM:862 case WW_I2SADC:863 /* nothing */864 break;865 }866 867 /* If software stereo detection is not supported... */868 if (t->rxsubchans == UNSET) {869 t->rxsubchans = V4L2_TUNER_SUB_MONO;870 /*871 * If the hardware itself detected stereo, also return872 * stereo as an available subchannel873 */874 if (t->audmode == V4L2_TUNER_MODE_STEREO)875 t->rxsubchans |= V4L2_TUNER_SUB_STEREO;876 }877}878EXPORT_SYMBOL(cx88_get_stereo);879 880 881void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual)882{883 u32 ctl = UNSET;884 u32 mask = UNSET;885 886 if (manual) {887 core->audiomode_manual = mode;888 } else {889 if (core->audiomode_manual != UNSET)890 return;891 }892 core->audiomode_current = mode;893 894 switch (core->tvaudio) {895 case WW_BTSC:896 switch (mode) {897 case V4L2_TUNER_MODE_MONO:898 set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_MONO);899 break;900 case V4L2_TUNER_MODE_LANG1:901 set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);902 break;903 case V4L2_TUNER_MODE_LANG2:904 set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP);905 break;906 case V4L2_TUNER_MODE_STEREO:907 case V4L2_TUNER_MODE_LANG1_LANG2:908 set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO);909 break;910 }911 break;912 case WW_BG:913 case WW_DK:914 case WW_M:915 case WW_I:916 case WW_L:917 if (core->use_nicam == 1) {918 switch (mode) {919 case V4L2_TUNER_MODE_MONO:920 case V4L2_TUNER_MODE_LANG1:921 set_audio_standard_NICAM(core,922 EN_NICAM_FORCE_MONO1);923 break;924 case V4L2_TUNER_MODE_LANG2:925 set_audio_standard_NICAM(core,926 EN_NICAM_FORCE_MONO2);927 break;928 case V4L2_TUNER_MODE_STEREO:929 case V4L2_TUNER_MODE_LANG1_LANG2:930 set_audio_standard_NICAM(core,931 EN_NICAM_FORCE_STEREO);932 break;933 }934 } else {935 if ((core->tvaudio == WW_I) ||936 (core->tvaudio == WW_L)) {937 /* fall back to fm / am mono */938 set_audio_standard_A2(core, EN_A2_FORCE_MONO1);939 } else {940 /* TODO: Add A2 autodection */941 mask = 0x3f;942 switch (mode) {943 case V4L2_TUNER_MODE_MONO:944 case V4L2_TUNER_MODE_LANG1:945 ctl = EN_A2_FORCE_MONO1;946 break;947 case V4L2_TUNER_MODE_LANG2:948 ctl = EN_A2_FORCE_MONO2;949 break;950 case V4L2_TUNER_MODE_STEREO:951 case V4L2_TUNER_MODE_LANG1_LANG2:952 ctl = EN_A2_FORCE_STEREO;953 break;954 }955 }956 }957 break;958 case WW_FM:959 switch (mode) {960 case V4L2_TUNER_MODE_MONO:961 ctl = EN_FMRADIO_FORCE_MONO;962 mask = 0x3f;963 break;964 case V4L2_TUNER_MODE_STEREO:965 ctl = EN_FMRADIO_AUTO_STEREO;966 mask = 0x3f;967 break;968 }969 break;970 case WW_I2SADC:971 case WW_NONE:972 case WW_EIAJ:973 case WW_I2SPT:974 /* DO NOTHING */975 break;976 }977 978 if (ctl != UNSET) {979 dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x [status=0x%x,ctl=0x%x,vol=0x%x]\n",980 mask, ctl, cx_read(AUD_STATUS),981 cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));982 cx_andor(AUD_CTL, mask, ctl);983 }984}985EXPORT_SYMBOL(cx88_set_stereo);986 987int cx88_audio_thread(void *data)988{989 struct cx88_core *core = data;990 struct v4l2_tuner t;991 u32 mode = 0;992 993 dprintk("cx88: tvaudio thread started\n");994 set_freezable();995 for (;;) {996 msleep_interruptible(1000);997 if (kthread_should_stop())998 break;999 try_to_freeze();1000 1001 switch (core->tvaudio) {1002 case WW_BG:1003 case WW_DK:1004 case WW_M:1005 case WW_I:1006 case WW_L:1007 if (core->use_nicam)1008 goto hw_autodetect;1009 1010 /* just monitor the audio status for now ... */1011 memset(&t, 0, sizeof(t));1012 cx88_get_stereo(core, &t);1013 1014 if (core->audiomode_manual != UNSET)1015 /* manually set, don't do anything. */1016 continue;1017 1018 /* monitor signal and set stereo if available */1019 if (t.rxsubchans & V4L2_TUNER_SUB_STEREO)1020 mode = V4L2_TUNER_MODE_STEREO;1021 else1022 mode = V4L2_TUNER_MODE_MONO;1023 if (mode == core->audiomode_current)1024 continue;1025 /* automatically switch to best available mode */1026 cx88_set_stereo(core, mode, 0);1027 break;1028 case WW_NONE:1029 case WW_BTSC:1030 case WW_EIAJ:1031 case WW_I2SPT:1032 case WW_FM:1033 case WW_I2SADC:1034hw_autodetect:1035 /*1036 * stereo autodetection is supported by hardware so1037 * we don't need to do it manually. Do nothing.1038 */1039 break;1040 }1041 }1042 1043 dprintk("cx88: tvaudio thread exiting\n");1044 return 0;1045}1046EXPORT_SYMBOL(cx88_audio_thread);1047