Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver
0004  *
0005  *  (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version]
0006  *  (c) 2002 Yurij Sysoev <yurij@naturesoft.net>
0007  *  (c) 2003 Gerd Knorr <kraxel@bytesex.org>
0008  *
0009  * -----------------------------------------------------------------------
0010  *
0011  * Lot of voodoo here.  Even the data sheet doesn't help to
0012  * understand what is going on here, the documentation for the audio
0013  * part of the cx2388x chip is *very* bad.
0014  *
0015  * Some of this comes from party done linux driver sources I got from
0016  * [undocumented].
0017  *
0018  * Some comes from the dscaler sources, one of the dscaler driver guy works
0019  * for Conexant ...
0020  *
0021  * -----------------------------------------------------------------------
0022  */
0023 
0024 #include "cx88.h"
0025 
0026 #include <linux/module.h>
0027 #include <linux/errno.h>
0028 #include <linux/freezer.h>
0029 #include <linux/kernel.h>
0030 #include <linux/mm.h>
0031 #include <linux/poll.h>
0032 #include <linux/signal.h>
0033 #include <linux/ioport.h>
0034 #include <linux/types.h>
0035 #include <linux/interrupt.h>
0036 #include <linux/vmalloc.h>
0037 #include <linux/init.h>
0038 #include <linux/delay.h>
0039 #include <linux/kthread.h>
0040 
0041 static unsigned int audio_debug;
0042 module_param(audio_debug, int, 0644);
0043 MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]");
0044 
0045 static unsigned int always_analog;
0046 module_param(always_analog, int, 0644);
0047 MODULE_PARM_DESC(always_analog, "force analog audio out");
0048 
0049 static unsigned int radio_deemphasis;
0050 module_param(radio_deemphasis, int, 0644);
0051 MODULE_PARM_DESC(radio_deemphasis,
0052          "Radio deemphasis time constant, 0=None, 1=50us (elsewhere), 2=75us (USA)");
0053 
0054 #define dprintk(fmt, arg...) do {               \
0055     if (audio_debug)                        \
0056         printk(KERN_DEBUG pr_fmt("%s: tvaudio:" fmt),       \
0057             __func__, ##arg);               \
0058 } while (0)
0059 /* ----------------------------------------------------------- */
0060 
0061 static const char * const aud_ctl_names[64] = {
0062     [EN_BTSC_FORCE_MONO] = "BTSC_FORCE_MONO",
0063     [EN_BTSC_FORCE_STEREO] = "BTSC_FORCE_STEREO",
0064     [EN_BTSC_FORCE_SAP] = "BTSC_FORCE_SAP",
0065     [EN_BTSC_AUTO_STEREO] = "BTSC_AUTO_STEREO",
0066     [EN_BTSC_AUTO_SAP] = "BTSC_AUTO_SAP",
0067     [EN_A2_FORCE_MONO1] = "A2_FORCE_MONO1",
0068     [EN_A2_FORCE_MONO2] = "A2_FORCE_MONO2",
0069     [EN_A2_FORCE_STEREO] = "A2_FORCE_STEREO",
0070     [EN_A2_AUTO_MONO2] = "A2_AUTO_MONO2",
0071     [EN_A2_AUTO_STEREO] = "A2_AUTO_STEREO",
0072     [EN_EIAJ_FORCE_MONO1] = "EIAJ_FORCE_MONO1",
0073     [EN_EIAJ_FORCE_MONO2] = "EIAJ_FORCE_MONO2",
0074     [EN_EIAJ_FORCE_STEREO] = "EIAJ_FORCE_STEREO",
0075     [EN_EIAJ_AUTO_MONO2] = "EIAJ_AUTO_MONO2",
0076     [EN_EIAJ_AUTO_STEREO] = "EIAJ_AUTO_STEREO",
0077     [EN_NICAM_FORCE_MONO1] = "NICAM_FORCE_MONO1",
0078     [EN_NICAM_FORCE_MONO2] = "NICAM_FORCE_MONO2",
0079     [EN_NICAM_FORCE_STEREO] = "NICAM_FORCE_STEREO",
0080     [EN_NICAM_AUTO_MONO2] = "NICAM_AUTO_MONO2",
0081     [EN_NICAM_AUTO_STEREO] = "NICAM_AUTO_STEREO",
0082     [EN_FMRADIO_FORCE_MONO] = "FMRADIO_FORCE_MONO",
0083     [EN_FMRADIO_FORCE_STEREO] = "FMRADIO_FORCE_STEREO",
0084     [EN_FMRADIO_AUTO_STEREO] = "FMRADIO_AUTO_STEREO",
0085 };
0086 
0087 struct rlist {
0088     u32 reg;
0089     u32 val;
0090 };
0091 
0092 static void set_audio_registers(struct cx88_core *core, const struct rlist *l)
0093 {
0094     int i;
0095 
0096     for (i = 0; l[i].reg; i++) {
0097         switch (l[i].reg) {
0098         case AUD_PDF_DDS_CNST_BYTE2:
0099         case AUD_PDF_DDS_CNST_BYTE1:
0100         case AUD_PDF_DDS_CNST_BYTE0:
0101         case AUD_QAM_MODE:
0102         case AUD_PHACC_FREQ_8MSB:
0103         case AUD_PHACC_FREQ_8LSB:
0104             cx_writeb(l[i].reg, l[i].val);
0105             break;
0106         default:
0107             cx_write(l[i].reg, l[i].val);
0108             break;
0109         }
0110     }
0111 }
0112 
0113 static void set_audio_start(struct cx88_core *core, u32 mode)
0114 {
0115     /* mute */
0116     cx_write(AUD_VOL_CTL, (1 << 6));
0117 
0118     /* start programming */
0119     cx_write(AUD_INIT, mode);
0120     cx_write(AUD_INIT_LD, 0x0001);
0121     cx_write(AUD_SOFT_RESET, 0x0001);
0122 }
0123 
0124 static void set_audio_finish(struct cx88_core *core, u32 ctl)
0125 {
0126     u32 volume;
0127 
0128     /* restart dma; This avoids buzz in NICAM and is good in others  */
0129     cx88_stop_audio_dma(core);
0130     cx_write(AUD_RATE_THRES_DMD, 0x000000C0);
0131     cx88_start_audio_dma(core);
0132 
0133     if (core->board.mpeg & CX88_MPEG_BLACKBIRD) {
0134         cx_write(AUD_I2SINPUTCNTL, 4);
0135         cx_write(AUD_BAUDRATE, 1);
0136         /*
0137          * 'pass-thru mode': this enables the i2s
0138          * output to the mpeg encoder
0139          */
0140         cx_set(AUD_CTL, EN_I2SOUT_ENABLE);
0141         cx_write(AUD_I2SOUTPUTCNTL, 1);
0142         cx_write(AUD_I2SCNTL, 0);
0143         /* cx_write(AUD_APB_IN_RATE_ADJ, 0); */
0144     }
0145     if ((always_analog) || (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))) {
0146         ctl |= EN_DAC_ENABLE;
0147         cx_write(AUD_CTL, ctl);
0148     }
0149 
0150     /* finish programming */
0151     cx_write(AUD_SOFT_RESET, 0x0000);
0152 
0153     /* unmute */
0154     volume = cx_sread(SHADOW_AUD_VOL_CTL);
0155     cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume);
0156 
0157     core->last_change = jiffies;
0158 }
0159 
0160 /* ----------------------------------------------------------- */
0161 
0162 static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap,
0163                     u32 mode)
0164 {
0165     static const struct rlist btsc[] = {
0166         {AUD_AFE_12DB_EN, 0x00000001},
0167         {AUD_OUT1_SEL, 0x00000013},
0168         {AUD_OUT1_SHIFT, 0x00000000},
0169         {AUD_POLY0_DDS_CONSTANT, 0x0012010c},
0170         {AUD_DMD_RA_DDS, 0x00c3e7aa},
0171         {AUD_DBX_IN_GAIN, 0x00004734},
0172         {AUD_DBX_WBE_GAIN, 0x00004640},
0173         {AUD_DBX_SE_GAIN, 0x00008d31},
0174         {AUD_DCOC_0_SRC, 0x0000001a},
0175         {AUD_IIR1_4_SEL, 0x00000021},
0176         {AUD_DCOC_PASS_IN, 0x00000003},
0177         {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
0178         {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
0179         {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
0180         {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
0181         {AUD_DN0_FREQ, 0x0000283b},
0182         {AUD_DN2_SRC_SEL, 0x00000008},
0183         {AUD_DN2_FREQ, 0x00003000},
0184         {AUD_DN2_AFC, 0x00000002},
0185         {AUD_DN2_SHFT, 0x00000000},
0186         {AUD_IIR2_2_SEL, 0x00000020},
0187         {AUD_IIR2_2_SHIFT, 0x00000000},
0188         {AUD_IIR2_3_SEL, 0x0000001f},
0189         {AUD_IIR2_3_SHIFT, 0x00000000},
0190         {AUD_CRDC1_SRC_SEL, 0x000003ce},
0191         {AUD_CRDC1_SHIFT, 0x00000000},
0192         {AUD_CORDIC_SHIFT_1, 0x00000007},
0193         {AUD_DCOC_1_SRC, 0x0000001b},
0194         {AUD_DCOC1_SHIFT, 0x00000000},
0195         {AUD_RDSI_SEL, 0x00000008},
0196         {AUD_RDSQ_SEL, 0x00000008},
0197         {AUD_RDSI_SHIFT, 0x00000000},
0198         {AUD_RDSQ_SHIFT, 0x00000000},
0199         {AUD_POLYPH80SCALEFAC, 0x00000003},
0200         { /* end of list */ },
0201     };
0202     static const struct rlist btsc_sap[] = {
0203         {AUD_AFE_12DB_EN, 0x00000001},
0204         {AUD_DBX_IN_GAIN, 0x00007200},
0205         {AUD_DBX_WBE_GAIN, 0x00006200},
0206         {AUD_DBX_SE_GAIN, 0x00006200},
0207         {AUD_IIR1_1_SEL, 0x00000000},
0208         {AUD_IIR1_3_SEL, 0x00000001},
0209         {AUD_DN1_SRC_SEL, 0x00000007},
0210         {AUD_IIR1_4_SHIFT, 0x00000006},
0211         {AUD_IIR2_1_SHIFT, 0x00000000},
0212         {AUD_IIR2_2_SHIFT, 0x00000000},
0213         {AUD_IIR3_0_SHIFT, 0x00000000},
0214         {AUD_IIR3_1_SHIFT, 0x00000000},
0215         {AUD_IIR3_0_SEL, 0x0000000d},
0216         {AUD_IIR3_1_SEL, 0x0000000e},
0217         {AUD_DEEMPH1_SRC_SEL, 0x00000014},
0218         {AUD_DEEMPH1_SHIFT, 0x00000000},
0219         {AUD_DEEMPH1_G0, 0x00004000},
0220         {AUD_DEEMPH1_A0, 0x00000000},
0221         {AUD_DEEMPH1_B0, 0x00000000},
0222         {AUD_DEEMPH1_A1, 0x00000000},
0223         {AUD_DEEMPH1_B1, 0x00000000},
0224         {AUD_OUT0_SEL, 0x0000003f},
0225         {AUD_OUT1_SEL, 0x0000003f},
0226         {AUD_DN1_AFC, 0x00000002},
0227         {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
0228         {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
0229         {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
0230         {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
0231         {AUD_IIR1_0_SEL, 0x0000001d},
0232         {AUD_IIR1_2_SEL, 0x0000001e},
0233         {AUD_IIR2_1_SEL, 0x00000002},
0234         {AUD_IIR2_2_SEL, 0x00000004},
0235         {AUD_IIR3_2_SEL, 0x0000000f},
0236         {AUD_DCOC2_SHIFT, 0x00000001},
0237         {AUD_IIR3_2_SHIFT, 0x00000001},
0238         {AUD_DEEMPH0_SRC_SEL, 0x00000014},
0239         {AUD_CORDIC_SHIFT_1, 0x00000006},
0240         {AUD_POLY0_DDS_CONSTANT, 0x000e4db2},
0241         {AUD_DMD_RA_DDS, 0x00f696e6},
0242         {AUD_IIR2_3_SEL, 0x00000025},
0243         {AUD_IIR1_4_SEL, 0x00000021},
0244         {AUD_DN1_FREQ, 0x0000c965},
0245         {AUD_DCOC_PASS_IN, 0x00000003},
0246         {AUD_DCOC_0_SRC, 0x0000001a},
0247         {AUD_DCOC_1_SRC, 0x0000001b},
0248         {AUD_DCOC1_SHIFT, 0x00000000},
0249         {AUD_RDSI_SEL, 0x00000009},
0250         {AUD_RDSQ_SEL, 0x00000009},
0251         {AUD_RDSI_SHIFT, 0x00000000},
0252         {AUD_RDSQ_SHIFT, 0x00000000},
0253         {AUD_POLYPH80SCALEFAC, 0x00000003},
0254         { /* end of list */ },
0255     };
0256 
0257     mode |= EN_FMRADIO_EN_RDS;
0258 
0259     if (sap) {
0260         dprintk("%s SAP (status: unknown)\n", __func__);
0261         set_audio_start(core, SEL_SAP);
0262         set_audio_registers(core, btsc_sap);
0263         set_audio_finish(core, mode);
0264     } else {
0265         dprintk("%s (status: known-good)\n", __func__);
0266         set_audio_start(core, SEL_BTSC);
0267         set_audio_registers(core, btsc);
0268         set_audio_finish(core, mode);
0269     }
0270 }
0271 
0272 static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode)
0273 {
0274     static const struct rlist nicam_l[] = {
0275         {AUD_AFE_12DB_EN, 0x00000001},
0276         {AUD_RATE_ADJ1, 0x00000060},
0277         {AUD_RATE_ADJ2, 0x000000F9},
0278         {AUD_RATE_ADJ3, 0x000001CC},
0279         {AUD_RATE_ADJ4, 0x000002B3},
0280         {AUD_RATE_ADJ5, 0x00000726},
0281         {AUD_DEEMPHDENOM1_R, 0x0000F3D0},
0282         {AUD_DEEMPHDENOM2_R, 0x00000000},
0283         {AUD_ERRLOGPERIOD_R, 0x00000064},
0284         {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},
0285         {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},
0286         {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},
0287         {AUD_POLYPH80SCALEFAC, 0x00000003},
0288         {AUD_DMD_RA_DDS, 0x00C00000},
0289         {AUD_PLL_INT, 0x0000001E},
0290         {AUD_PLL_DDS, 0x00000000},
0291         {AUD_PLL_FRAC, 0x0000E542},
0292         {AUD_START_TIMER, 0x00000000},
0293         {AUD_DEEMPHNUMER1_R, 0x000353DE},
0294         {AUD_DEEMPHNUMER2_R, 0x000001B1},
0295         {AUD_PDF_DDS_CNST_BYTE2, 0x06},
0296         {AUD_PDF_DDS_CNST_BYTE1, 0x82},
0297         {AUD_PDF_DDS_CNST_BYTE0, 0x12},
0298         {AUD_QAM_MODE, 0x05},
0299         {AUD_PHACC_FREQ_8MSB, 0x34},
0300         {AUD_PHACC_FREQ_8LSB, 0x4C},
0301         {AUD_DEEMPHGAIN_R, 0x00006680},
0302         {AUD_RATE_THRES_DMD, 0x000000C0},
0303         { /* end of list */ },
0304     };
0305 
0306     static const struct rlist nicam_bgdki_common[] = {
0307         {AUD_AFE_12DB_EN, 0x00000001},
0308         {AUD_RATE_ADJ1, 0x00000010},
0309         {AUD_RATE_ADJ2, 0x00000040},
0310         {AUD_RATE_ADJ3, 0x00000100},
0311         {AUD_RATE_ADJ4, 0x00000400},
0312         {AUD_RATE_ADJ5, 0x00001000},
0313         {AUD_ERRLOGPERIOD_R, 0x00000fff},
0314         {AUD_ERRINTRPTTHSHLD1_R, 0x000003ff},
0315         {AUD_ERRINTRPTTHSHLD2_R, 0x000000ff},
0316         {AUD_ERRINTRPTTHSHLD3_R, 0x0000003f},
0317         {AUD_POLYPH80SCALEFAC, 0x00000003},
0318         {AUD_DEEMPHGAIN_R, 0x000023c2},
0319         {AUD_DEEMPHNUMER1_R, 0x0002a7bc},
0320         {AUD_DEEMPHNUMER2_R, 0x0003023e},
0321         {AUD_DEEMPHDENOM1_R, 0x0000f3d0},
0322         {AUD_DEEMPHDENOM2_R, 0x00000000},
0323         {AUD_PDF_DDS_CNST_BYTE2, 0x06},
0324         {AUD_PDF_DDS_CNST_BYTE1, 0x82},
0325         {AUD_QAM_MODE, 0x05},
0326         { /* end of list */ },
0327     };
0328 
0329     static const struct rlist nicam_i[] = {
0330         {AUD_PDF_DDS_CNST_BYTE0, 0x12},
0331         {AUD_PHACC_FREQ_8MSB, 0x3a},
0332         {AUD_PHACC_FREQ_8LSB, 0x93},
0333         { /* end of list */ },
0334     };
0335 
0336     static const struct rlist nicam_default[] = {
0337         {AUD_PDF_DDS_CNST_BYTE0, 0x16},
0338         {AUD_PHACC_FREQ_8MSB, 0x34},
0339         {AUD_PHACC_FREQ_8LSB, 0x4c},
0340         { /* end of list */ },
0341     };
0342 
0343     set_audio_start(core, SEL_NICAM);
0344     switch (core->tvaudio) {
0345     case WW_L:
0346         dprintk("%s SECAM-L NICAM (status: devel)\n", __func__);
0347         set_audio_registers(core, nicam_l);
0348         break;
0349     case WW_I:
0350         dprintk("%s PAL-I NICAM (status: known-good)\n", __func__);
0351         set_audio_registers(core, nicam_bgdki_common);
0352         set_audio_registers(core, nicam_i);
0353         break;
0354     case WW_NONE:
0355     case WW_BTSC:
0356     case WW_BG:
0357     case WW_DK:
0358     case WW_EIAJ:
0359     case WW_I2SPT:
0360     case WW_FM:
0361     case WW_I2SADC:
0362     case WW_M:
0363         dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__);
0364         set_audio_registers(core, nicam_bgdki_common);
0365         set_audio_registers(core, nicam_default);
0366         break;
0367     }
0368 
0369     mode |= EN_DMTRX_LR | EN_DMTRX_BYPASS;
0370     set_audio_finish(core, mode);
0371 }
0372 
0373 static void set_audio_standard_A2(struct cx88_core *core, u32 mode)
0374 {
0375     static const struct rlist a2_bgdk_common[] = {
0376         {AUD_ERRLOGPERIOD_R, 0x00000064},
0377         {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},
0378         {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},
0379         {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},
0380         {AUD_PDF_DDS_CNST_BYTE2, 0x06},
0381         {AUD_PDF_DDS_CNST_BYTE1, 0x82},
0382         {AUD_PDF_DDS_CNST_BYTE0, 0x12},
0383         {AUD_QAM_MODE, 0x05},
0384         {AUD_PHACC_FREQ_8MSB, 0x34},
0385         {AUD_PHACC_FREQ_8LSB, 0x4c},
0386         {AUD_RATE_ADJ1, 0x00000100},
0387         {AUD_RATE_ADJ2, 0x00000200},
0388         {AUD_RATE_ADJ3, 0x00000300},
0389         {AUD_RATE_ADJ4, 0x00000400},
0390         {AUD_RATE_ADJ5, 0x00000500},
0391         {AUD_THR_FR, 0x00000000},
0392         {AAGC_HYST, 0x0000001a},
0393         {AUD_PILOT_BQD_1_K0, 0x0000755b},
0394         {AUD_PILOT_BQD_1_K1, 0x00551340},
0395         {AUD_PILOT_BQD_1_K2, 0x006d30be},
0396         {AUD_PILOT_BQD_1_K3, 0xffd394af},
0397         {AUD_PILOT_BQD_1_K4, 0x00400000},
0398         {AUD_PILOT_BQD_2_K0, 0x00040000},
0399         {AUD_PILOT_BQD_2_K1, 0x002a4841},
0400         {AUD_PILOT_BQD_2_K2, 0x00400000},
0401         {AUD_PILOT_BQD_2_K3, 0x00000000},
0402         {AUD_PILOT_BQD_2_K4, 0x00000000},
0403         {AUD_MODE_CHG_TIMER, 0x00000040},
0404         {AUD_AFE_12DB_EN, 0x00000001},
0405         {AUD_CORDIC_SHIFT_0, 0x00000007},
0406         {AUD_CORDIC_SHIFT_1, 0x00000007},
0407         {AUD_DEEMPH0_G0, 0x00000380},
0408         {AUD_DEEMPH1_G0, 0x00000380},
0409         {AUD_DCOC_0_SRC, 0x0000001a},
0410         {AUD_DCOC0_SHIFT, 0x00000000},
0411         {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
0412         {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
0413         {AUD_DCOC_PASS_IN, 0x00000003},
0414         {AUD_IIR3_0_SEL, 0x00000021},
0415         {AUD_DN2_AFC, 0x00000002},
0416         {AUD_DCOC_1_SRC, 0x0000001b},
0417         {AUD_DCOC1_SHIFT, 0x00000000},
0418         {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
0419         {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
0420         {AUD_IIR3_1_SEL, 0x00000023},
0421         {AUD_RDSI_SEL, 0x00000017},
0422         {AUD_RDSI_SHIFT, 0x00000000},
0423         {AUD_RDSQ_SEL, 0x00000017},
0424         {AUD_RDSQ_SHIFT, 0x00000000},
0425         {AUD_PLL_INT, 0x0000001e},
0426         {AUD_PLL_DDS, 0x00000000},
0427         {AUD_PLL_FRAC, 0x0000e542},
0428         {AUD_POLYPH80SCALEFAC, 0x00000001},
0429         {AUD_START_TIMER, 0x00000000},
0430         { /* end of list */ },
0431     };
0432 
0433     static const struct rlist a2_bg[] = {
0434         {AUD_DMD_RA_DDS, 0x002a4f2f},
0435         {AUD_C1_UP_THR, 0x00007000},
0436         {AUD_C1_LO_THR, 0x00005400},
0437         {AUD_C2_UP_THR, 0x00005400},
0438         {AUD_C2_LO_THR, 0x00003000},
0439         { /* end of list */ },
0440     };
0441 
0442     static const struct rlist a2_dk[] = {
0443         {AUD_DMD_RA_DDS, 0x002a4f2f},
0444         {AUD_C1_UP_THR, 0x00007000},
0445         {AUD_C1_LO_THR, 0x00005400},
0446         {AUD_C2_UP_THR, 0x00005400},
0447         {AUD_C2_LO_THR, 0x00003000},
0448         {AUD_DN0_FREQ, 0x00003a1c},
0449         {AUD_DN2_FREQ, 0x0000d2e0},
0450         { /* end of list */ },
0451     };
0452 
0453     static const struct rlist a1_i[] = {
0454         {AUD_ERRLOGPERIOD_R, 0x00000064},
0455         {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},
0456         {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},
0457         {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},
0458         {AUD_PDF_DDS_CNST_BYTE2, 0x06},
0459         {AUD_PDF_DDS_CNST_BYTE1, 0x82},
0460         {AUD_PDF_DDS_CNST_BYTE0, 0x12},
0461         {AUD_QAM_MODE, 0x05},
0462         {AUD_PHACC_FREQ_8MSB, 0x3a},
0463         {AUD_PHACC_FREQ_8LSB, 0x93},
0464         {AUD_DMD_RA_DDS, 0x002a4f2f},
0465         {AUD_PLL_INT, 0x0000001e},
0466         {AUD_PLL_DDS, 0x00000004},
0467         {AUD_PLL_FRAC, 0x0000e542},
0468         {AUD_RATE_ADJ1, 0x00000100},
0469         {AUD_RATE_ADJ2, 0x00000200},
0470         {AUD_RATE_ADJ3, 0x00000300},
0471         {AUD_RATE_ADJ4, 0x00000400},
0472         {AUD_RATE_ADJ5, 0x00000500},
0473         {AUD_THR_FR, 0x00000000},
0474         {AUD_PILOT_BQD_1_K0, 0x0000755b},
0475         {AUD_PILOT_BQD_1_K1, 0x00551340},
0476         {AUD_PILOT_BQD_1_K2, 0x006d30be},
0477         {AUD_PILOT_BQD_1_K3, 0xffd394af},
0478         {AUD_PILOT_BQD_1_K4, 0x00400000},
0479         {AUD_PILOT_BQD_2_K0, 0x00040000},
0480         {AUD_PILOT_BQD_2_K1, 0x002a4841},
0481         {AUD_PILOT_BQD_2_K2, 0x00400000},
0482         {AUD_PILOT_BQD_2_K3, 0x00000000},
0483         {AUD_PILOT_BQD_2_K4, 0x00000000},
0484         {AUD_MODE_CHG_TIMER, 0x00000060},
0485         {AUD_AFE_12DB_EN, 0x00000001},
0486         {AAGC_HYST, 0x0000000a},
0487         {AUD_CORDIC_SHIFT_0, 0x00000007},
0488         {AUD_CORDIC_SHIFT_1, 0x00000007},
0489         {AUD_C1_UP_THR, 0x00007000},
0490         {AUD_C1_LO_THR, 0x00005400},
0491         {AUD_C2_UP_THR, 0x00005400},
0492         {AUD_C2_LO_THR, 0x00003000},
0493         {AUD_DCOC_0_SRC, 0x0000001a},
0494         {AUD_DCOC0_SHIFT, 0x00000000},
0495         {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
0496         {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
0497         {AUD_DCOC_PASS_IN, 0x00000003},
0498         {AUD_IIR3_0_SEL, 0x00000021},
0499         {AUD_DN2_AFC, 0x00000002},
0500         {AUD_DCOC_1_SRC, 0x0000001b},
0501         {AUD_DCOC1_SHIFT, 0x00000000},
0502         {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
0503         {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
0504         {AUD_IIR3_1_SEL, 0x00000023},
0505         {AUD_DN0_FREQ, 0x000035a3},
0506         {AUD_DN2_FREQ, 0x000029c7},
0507         {AUD_CRDC0_SRC_SEL, 0x00000511},
0508         {AUD_IIR1_0_SEL, 0x00000001},
0509         {AUD_IIR1_1_SEL, 0x00000000},
0510         {AUD_IIR3_2_SEL, 0x00000003},
0511         {AUD_IIR3_2_SHIFT, 0x00000000},
0512         {AUD_IIR3_0_SEL, 0x00000002},
0513         {AUD_IIR2_0_SEL, 0x00000021},
0514         {AUD_IIR2_0_SHIFT, 0x00000002},
0515         {AUD_DEEMPH0_SRC_SEL, 0x0000000b},
0516         {AUD_DEEMPH1_SRC_SEL, 0x0000000b},
0517         {AUD_POLYPH80SCALEFAC, 0x00000001},
0518         {AUD_START_TIMER, 0x00000000},
0519         { /* end of list */ },
0520     };
0521 
0522     static const struct rlist am_l[] = {
0523         {AUD_ERRLOGPERIOD_R, 0x00000064},
0524         {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},
0525         {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},
0526         {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},
0527         {AUD_PDF_DDS_CNST_BYTE2, 0x48},
0528         {AUD_PDF_DDS_CNST_BYTE1, 0x3D},
0529         {AUD_QAM_MODE, 0x00},
0530         {AUD_PDF_DDS_CNST_BYTE0, 0xf5},
0531         {AUD_PHACC_FREQ_8MSB, 0x3a},
0532         {AUD_PHACC_FREQ_8LSB, 0x4a},
0533         {AUD_DEEMPHGAIN_R, 0x00006680},
0534         {AUD_DEEMPHNUMER1_R, 0x000353DE},
0535         {AUD_DEEMPHNUMER2_R, 0x000001B1},
0536         {AUD_DEEMPHDENOM1_R, 0x0000F3D0},
0537         {AUD_DEEMPHDENOM2_R, 0x00000000},
0538         {AUD_FM_MODE_ENABLE, 0x00000007},
0539         {AUD_POLYPH80SCALEFAC, 0x00000003},
0540         {AUD_AFE_12DB_EN, 0x00000001},
0541         {AAGC_GAIN, 0x00000000},
0542         {AAGC_HYST, 0x00000018},
0543         {AAGC_DEF, 0x00000020},
0544         {AUD_DN0_FREQ, 0x00000000},
0545         {AUD_POLY0_DDS_CONSTANT, 0x000E4DB2},
0546         {AUD_DCOC_0_SRC, 0x00000021},
0547         {AUD_IIR1_0_SEL, 0x00000000},
0548         {AUD_IIR1_0_SHIFT, 0x00000007},
0549         {AUD_IIR1_1_SEL, 0x00000002},
0550         {AUD_IIR1_1_SHIFT, 0x00000000},
0551         {AUD_DCOC_1_SRC, 0x00000003},
0552         {AUD_DCOC1_SHIFT, 0x00000000},
0553         {AUD_DCOC_PASS_IN, 0x00000000},
0554         {AUD_IIR1_2_SEL, 0x00000023},
0555         {AUD_IIR1_2_SHIFT, 0x00000000},
0556         {AUD_IIR1_3_SEL, 0x00000004},
0557         {AUD_IIR1_3_SHIFT, 0x00000007},
0558         {AUD_IIR1_4_SEL, 0x00000005},
0559         {AUD_IIR1_4_SHIFT, 0x00000007},
0560         {AUD_IIR3_0_SEL, 0x00000007},
0561         {AUD_IIR3_0_SHIFT, 0x00000000},
0562         {AUD_DEEMPH0_SRC_SEL, 0x00000011},
0563         {AUD_DEEMPH0_SHIFT, 0x00000000},
0564         {AUD_DEEMPH0_G0, 0x00007000},
0565         {AUD_DEEMPH0_A0, 0x00000000},
0566         {AUD_DEEMPH0_B0, 0x00000000},
0567         {AUD_DEEMPH0_A1, 0x00000000},
0568         {AUD_DEEMPH0_B1, 0x00000000},
0569         {AUD_DEEMPH1_SRC_SEL, 0x00000011},
0570         {AUD_DEEMPH1_SHIFT, 0x00000000},
0571         {AUD_DEEMPH1_G0, 0x00007000},
0572         {AUD_DEEMPH1_A0, 0x00000000},
0573         {AUD_DEEMPH1_B0, 0x00000000},
0574         {AUD_DEEMPH1_A1, 0x00000000},
0575         {AUD_DEEMPH1_B1, 0x00000000},
0576         {AUD_OUT0_SEL, 0x0000003F},
0577         {AUD_OUT1_SEL, 0x0000003F},
0578         {AUD_DMD_RA_DDS, 0x00F5C285},
0579         {AUD_PLL_INT, 0x0000001E},
0580         {AUD_PLL_DDS, 0x00000000},
0581         {AUD_PLL_FRAC, 0x0000E542},
0582         {AUD_RATE_ADJ1, 0x00000100},
0583         {AUD_RATE_ADJ2, 0x00000200},
0584         {AUD_RATE_ADJ3, 0x00000300},
0585         {AUD_RATE_ADJ4, 0x00000400},
0586         {AUD_RATE_ADJ5, 0x00000500},
0587         {AUD_RATE_THRES_DMD, 0x000000C0},
0588         { /* end of list */ },
0589     };
0590 
0591     static const struct rlist a2_deemph50[] = {
0592         {AUD_DEEMPH0_G0, 0x00000380},
0593         {AUD_DEEMPH1_G0, 0x00000380},
0594         {AUD_DEEMPHGAIN_R, 0x000011e1},
0595         {AUD_DEEMPHNUMER1_R, 0x0002a7bc},
0596         {AUD_DEEMPHNUMER2_R, 0x0003023c},
0597         { /* end of list */ },
0598     };
0599 
0600     set_audio_start(core, SEL_A2);
0601     switch (core->tvaudio) {
0602     case WW_BG:
0603         dprintk("%s PAL-BG A1/2 (status: known-good)\n", __func__);
0604         set_audio_registers(core, a2_bgdk_common);
0605         set_audio_registers(core, a2_bg);
0606         set_audio_registers(core, a2_deemph50);
0607         break;
0608     case WW_DK:
0609         dprintk("%s PAL-DK A1/2 (status: known-good)\n", __func__);
0610         set_audio_registers(core, a2_bgdk_common);
0611         set_audio_registers(core, a2_dk);
0612         set_audio_registers(core, a2_deemph50);
0613         break;
0614     case WW_I:
0615         dprintk("%s PAL-I A1 (status: known-good)\n", __func__);
0616         set_audio_registers(core, a1_i);
0617         set_audio_registers(core, a2_deemph50);
0618         break;
0619     case WW_L:
0620         dprintk("%s AM-L (status: devel)\n", __func__);
0621         set_audio_registers(core, am_l);
0622         break;
0623     case WW_NONE:
0624     case WW_BTSC:
0625     case WW_EIAJ:
0626     case WW_I2SPT:
0627     case WW_FM:
0628     case WW_I2SADC:
0629     case WW_M:
0630         dprintk("%s Warning: wrong value\n", __func__);
0631         return;
0632     }
0633 
0634     mode |= EN_FMRADIO_EN_RDS | EN_DMTRX_SUMDIFF;
0635     set_audio_finish(core, mode);
0636 }
0637 
0638 static void set_audio_standard_EIAJ(struct cx88_core *core)
0639 {
0640     static const struct rlist eiaj[] = {
0641         /* TODO: eiaj register settings are not there yet ... */
0642 
0643         { /* end of list */ },
0644     };
0645     dprintk("%s (status: unknown)\n", __func__);
0646 
0647     set_audio_start(core, SEL_EIAJ);
0648     set_audio_registers(core, eiaj);
0649     set_audio_finish(core, EN_EIAJ_AUTO_STEREO);
0650 }
0651 
0652 static void set_audio_standard_FM(struct cx88_core *core,
0653                   enum cx88_deemph_type deemph)
0654 {
0655     static const struct rlist fm_deemph_50[] = {
0656         {AUD_DEEMPH0_G0, 0x0C45},
0657         {AUD_DEEMPH0_A0, 0x6262},
0658         {AUD_DEEMPH0_B0, 0x1C29},
0659         {AUD_DEEMPH0_A1, 0x3FC66},
0660         {AUD_DEEMPH0_B1, 0x399A},
0661 
0662         {AUD_DEEMPH1_G0, 0x0D80},
0663         {AUD_DEEMPH1_A0, 0x6262},
0664         {AUD_DEEMPH1_B0, 0x1C29},
0665         {AUD_DEEMPH1_A1, 0x3FC66},
0666         {AUD_DEEMPH1_B1, 0x399A},
0667 
0668         {AUD_POLYPH80SCALEFAC, 0x0003},
0669         { /* end of list */ },
0670     };
0671     static const struct rlist fm_deemph_75[] = {
0672         {AUD_DEEMPH0_G0, 0x091B},
0673         {AUD_DEEMPH0_A0, 0x6B68},
0674         {AUD_DEEMPH0_B0, 0x11EC},
0675         {AUD_DEEMPH0_A1, 0x3FC66},
0676         {AUD_DEEMPH0_B1, 0x399A},
0677 
0678         {AUD_DEEMPH1_G0, 0x0AA0},
0679         {AUD_DEEMPH1_A0, 0x6B68},
0680         {AUD_DEEMPH1_B0, 0x11EC},
0681         {AUD_DEEMPH1_A1, 0x3FC66},
0682         {AUD_DEEMPH1_B1, 0x399A},
0683 
0684         {AUD_POLYPH80SCALEFAC, 0x0003},
0685         { /* end of list */ },
0686     };
0687 
0688     /*
0689      * It is enough to leave default values?
0690      *
0691      * No, it's not!  The deemphasis registers are reset to the 75us
0692      * values by default.  Analyzing the spectrum of the decoded audio
0693      * reveals that "no deemphasis" is the same as 75 us, while the 50 us
0694      * setting results in less deemphasis.
0695      */
0696     static const struct rlist fm_no_deemph[] = {
0697         {AUD_POLYPH80SCALEFAC, 0x0003},
0698         { /* end of list */ },
0699     };
0700 
0701     dprintk("%s (status: unknown)\n", __func__);
0702     set_audio_start(core, SEL_FMRADIO);
0703 
0704     switch (deemph) {
0705     default:
0706     case FM_NO_DEEMPH:
0707         set_audio_registers(core, fm_no_deemph);
0708         break;
0709 
0710     case FM_DEEMPH_50:
0711         set_audio_registers(core, fm_deemph_50);
0712         break;
0713 
0714     case FM_DEEMPH_75:
0715         set_audio_registers(core, fm_deemph_75);
0716         break;
0717     }
0718 
0719     set_audio_finish(core, EN_FMRADIO_AUTO_STEREO);
0720 }
0721 
0722 /* ----------------------------------------------------------- */
0723 
0724 static int cx88_detect_nicam(struct cx88_core *core)
0725 {
0726     int i, j = 0;
0727 
0728     dprintk("start nicam autodetect.\n");
0729 
0730     for (i = 0; i < 6; i++) {
0731         /* if bit1=1 then nicam is detected */
0732         j += ((cx_read(AUD_NICAM_STATUS2) & 0x02) >> 1);
0733 
0734         if (j == 1) {
0735             dprintk("nicam is detected.\n");
0736             return 1;
0737         }
0738 
0739         /* wait a little bit for next reading status */
0740         usleep_range(10000, 20000);
0741     }
0742 
0743     dprintk("nicam is not detected.\n");
0744     return 0;
0745 }
0746 
0747 void cx88_set_tvaudio(struct cx88_core *core)
0748 {
0749     switch (core->tvaudio) {
0750     case WW_BTSC:
0751         set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);
0752         break;
0753     case WW_BG:
0754     case WW_DK:
0755     case WW_M:
0756     case WW_I:
0757     case WW_L:
0758         /* prepare all dsp registers */
0759         set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
0760 
0761         /*
0762          * set nicam mode - otherwise
0763          * AUD_NICAM_STATUS2 contains wrong values
0764          */
0765         set_audio_standard_NICAM(core, EN_NICAM_AUTO_STEREO);
0766         if (cx88_detect_nicam(core) == 0) {
0767             /* fall back to fm / am mono */
0768             set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
0769             core->audiomode_current = V4L2_TUNER_MODE_MONO;
0770             core->use_nicam = 0;
0771         } else {
0772             core->use_nicam = 1;
0773         }
0774         break;
0775     case WW_EIAJ:
0776         set_audio_standard_EIAJ(core);
0777         break;
0778     case WW_FM:
0779         set_audio_standard_FM(core, radio_deemphasis);
0780         break;
0781     case WW_I2SADC:
0782         set_audio_start(core, 0x01);
0783         /*
0784          * Slave/Philips/Autobaud
0785          * NB on Nova-S bit1 NPhilipsSony appears to be inverted:
0786          *  0= Sony, 1=Philips
0787          */
0788         cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl);
0789         /* Switch to "I2S ADC mode" */
0790         cx_write(AUD_I2SCNTL, 0x1);
0791         set_audio_finish(core, EN_I2SIN_ENABLE);
0792         break;
0793     case WW_NONE:
0794     case WW_I2SPT:
0795         pr_info("unknown tv audio mode [%d]\n", core->tvaudio);
0796         break;
0797     }
0798 }
0799 EXPORT_SYMBOL(cx88_set_tvaudio);
0800 
0801 void cx88_newstation(struct cx88_core *core)
0802 {
0803     core->audiomode_manual = UNSET;
0804     core->last_change = jiffies;
0805 }
0806 EXPORT_SYMBOL(cx88_newstation);
0807 
0808 void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
0809 {
0810     static const char * const m[] = { "stereo", "dual mono",
0811                       "mono",   "sap" };
0812     static const char * const p[] = { "no pilot", "pilot c1",
0813                       "pilot c2", "?" };
0814     u32 reg, mode, pilot;
0815 
0816     reg = cx_read(AUD_STATUS);
0817     mode = reg & 0x03;
0818     pilot = (reg >> 2) & 0x03;
0819 
0820     if (core->astat != reg)
0821         dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
0822             reg, m[mode], p[pilot],
0823             aud_ctl_names[cx_read(AUD_CTL) & 63]);
0824     core->astat = reg;
0825 
0826     t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
0827         V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
0828     t->rxsubchans = UNSET;
0829     t->audmode = V4L2_TUNER_MODE_MONO;
0830 
0831     switch (mode) {
0832     case 0:
0833         t->audmode = V4L2_TUNER_MODE_STEREO;
0834         break;
0835     case 1:
0836         t->audmode = V4L2_TUNER_MODE_LANG2;
0837         break;
0838     case 2:
0839         t->audmode = V4L2_TUNER_MODE_MONO;
0840         break;
0841     case 3:
0842         t->audmode = V4L2_TUNER_MODE_SAP;
0843         break;
0844     }
0845 
0846     switch (core->tvaudio) {
0847     case WW_BTSC:
0848     case WW_BG:
0849     case WW_DK:
0850     case WW_M:
0851     case WW_EIAJ:
0852         if (!core->use_nicam) {
0853             t->rxsubchans = cx88_dsp_detect_stereo_sap(core);
0854             break;
0855         }
0856         break;
0857     case WW_NONE:
0858     case WW_I:
0859     case WW_L:
0860     case WW_I2SPT:
0861     case WW_FM:
0862     case WW_I2SADC:
0863         /* nothing */
0864         break;
0865     }
0866 
0867     /* If software stereo detection is not supported... */
0868     if (t->rxsubchans == UNSET) {
0869         t->rxsubchans = V4L2_TUNER_SUB_MONO;
0870         /*
0871          * If the hardware itself detected stereo, also return
0872          * stereo as an available subchannel
0873          */
0874         if (t->audmode == V4L2_TUNER_MODE_STEREO)
0875             t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
0876     }
0877 }
0878 EXPORT_SYMBOL(cx88_get_stereo);
0879 
0880 
0881 void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual)
0882 {
0883     u32 ctl = UNSET;
0884     u32 mask = UNSET;
0885 
0886     if (manual) {
0887         core->audiomode_manual = mode;
0888     } else {
0889         if (core->audiomode_manual != UNSET)
0890             return;
0891     }
0892     core->audiomode_current = mode;
0893 
0894     switch (core->tvaudio) {
0895     case WW_BTSC:
0896         switch (mode) {
0897         case V4L2_TUNER_MODE_MONO:
0898             set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_MONO);
0899             break;
0900         case V4L2_TUNER_MODE_LANG1:
0901             set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);
0902             break;
0903         case V4L2_TUNER_MODE_LANG2:
0904             set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP);
0905             break;
0906         case V4L2_TUNER_MODE_STEREO:
0907         case V4L2_TUNER_MODE_LANG1_LANG2:
0908             set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO);
0909             break;
0910         }
0911         break;
0912     case WW_BG:
0913     case WW_DK:
0914     case WW_M:
0915     case WW_I:
0916     case WW_L:
0917         if (core->use_nicam == 1) {
0918             switch (mode) {
0919             case V4L2_TUNER_MODE_MONO:
0920             case V4L2_TUNER_MODE_LANG1:
0921                 set_audio_standard_NICAM(core,
0922                              EN_NICAM_FORCE_MONO1);
0923                 break;
0924             case V4L2_TUNER_MODE_LANG2:
0925                 set_audio_standard_NICAM(core,
0926                              EN_NICAM_FORCE_MONO2);
0927                 break;
0928             case V4L2_TUNER_MODE_STEREO:
0929             case V4L2_TUNER_MODE_LANG1_LANG2:
0930                 set_audio_standard_NICAM(core,
0931                              EN_NICAM_FORCE_STEREO);
0932                 break;
0933             }
0934         } else {
0935             if ((core->tvaudio == WW_I) ||
0936                 (core->tvaudio == WW_L)) {
0937                 /* fall back to fm / am mono */
0938                 set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
0939             } else {
0940                 /* TODO: Add A2 autodection */
0941                 mask = 0x3f;
0942                 switch (mode) {
0943                 case V4L2_TUNER_MODE_MONO:
0944                 case V4L2_TUNER_MODE_LANG1:
0945                     ctl = EN_A2_FORCE_MONO1;
0946                     break;
0947                 case V4L2_TUNER_MODE_LANG2:
0948                     ctl = EN_A2_FORCE_MONO2;
0949                     break;
0950                 case V4L2_TUNER_MODE_STEREO:
0951                 case V4L2_TUNER_MODE_LANG1_LANG2:
0952                     ctl = EN_A2_FORCE_STEREO;
0953                     break;
0954                 }
0955             }
0956         }
0957         break;
0958     case WW_FM:
0959         switch (mode) {
0960         case V4L2_TUNER_MODE_MONO:
0961             ctl = EN_FMRADIO_FORCE_MONO;
0962             mask = 0x3f;
0963             break;
0964         case V4L2_TUNER_MODE_STEREO:
0965             ctl = EN_FMRADIO_AUTO_STEREO;
0966             mask = 0x3f;
0967             break;
0968         }
0969         break;
0970     case WW_I2SADC:
0971     case WW_NONE:
0972     case WW_EIAJ:
0973     case WW_I2SPT:
0974         /* DO NOTHING */
0975         break;
0976     }
0977 
0978     if (ctl != UNSET) {
0979         dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x [status=0x%x,ctl=0x%x,vol=0x%x]\n",
0980             mask, ctl, cx_read(AUD_STATUS),
0981             cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));
0982         cx_andor(AUD_CTL, mask, ctl);
0983     }
0984 }
0985 EXPORT_SYMBOL(cx88_set_stereo);
0986 
0987 int cx88_audio_thread(void *data)
0988 {
0989     struct cx88_core *core = data;
0990     struct v4l2_tuner t;
0991     u32 mode = 0;
0992 
0993     dprintk("cx88: tvaudio thread started\n");
0994     set_freezable();
0995     for (;;) {
0996         msleep_interruptible(1000);
0997         if (kthread_should_stop())
0998             break;
0999         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);