1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver 4 * 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 to 12 * understand what is going on here, the documentation for the audio 13 * part of the cx2388x chip is *very* bad. 14 * 15 * Some of this comes from party done linux driver sources I got from 16 * [undocumented]. 17 * 18 * Some comes from the dscaler sources, one of the dscaler driver guy works 19 * 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 41 static unsigned int audio_debug; 42 module_param(audio_debug, int, 0644); 43 MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]"); 44 45 static unsigned int always_analog; 46 module_param(always_analog, int, 0644); 47 MODULE_PARM_DESC(always_analog, "force analog audio out"); 48 49 static unsigned int radio_deemphasis; 50 module_param(radio_deemphasis, int, 0644); 51 MODULE_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 61 static 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 87 struct rlist { 88 u32 reg; 89 u32 val; 90 }; 91 92 static 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 113 static 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 124 static 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 i2s 138 * output to the mpeg encoder 139 */ 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 162 static 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 272 static 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 373 static 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 638 static 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 652 static 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 75us 692 * values by default. Analyzing the spectrum of the decoded audio 693 * reveals that "no deemphasis" is the same as 75 us, while the 50 us 694 * 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 724 static 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 747 void 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 - otherwise 763 * AUD_NICAM_STATUS2 contains wrong values 764 */ 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/Autobaud 785 * NB on Nova-S bit1 NPhilipsSony appears to be inverted: 786 * 0= Sony, 1=Philips 787 */ 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 } 799 EXPORT_SYMBOL(cx88_set_tvaudio); 800 801 void cx88_newstation(struct cx88_core *core) 802 { 803 core->audiomode_manual = UNSET; 804 core->last_change = jiffies; 805 } 806 EXPORT_SYMBOL(cx88_newstation); 807 808 void 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 return 872 * stereo as an available subchannel 873 */ 874 if (t->audmode == V4L2_TUNER_MODE_STEREO) 875 t->rxsubchans |= V4L2_TUNER_SUB_STEREO; 876 } 877 } 878 EXPORT_SYMBOL(cx88_get_stereo); 879 880 881 void 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 } 985 EXPORT_SYMBOL(cx88_set_stereo); 986 987 int 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 else 1022 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: 1034 hw_autodetect: 1035 /* 1036 * stereo autodetection is supported by hardware so 1037 * 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 } 1046 EXPORT_SYMBOL(cx88_audio_thread); 1047