aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx88
diff options
context:
space:
mode:
authorMarton Balint <cus@fazekas.hu>2009-03-31 18:01:51 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-06-16 18:14:17 -0400
commite878cf3a47a5d99635edc564423a9a4469c17810 (patch)
tree4c376eb123d18a00c406e14b55882655d8eb2709 /drivers/media/video/cx88
parent319afbf97f209e3a907981f76e382c02ea3ecff3 (diff)
V4L/DVB (11394): cx88: Add support for stereo and sap detection for A2
The patch implements reliable stereo and sap detection for the A2 sound standard. This is achieved by processing the samples of the audio RDS fifo of the cx2388x chip. A2M, EIAJ and BTSC stereo/sap detection is also possible with this new approach, but it's not implemented yet. Stereo detection when alsa handles the sound also does not work yet. Signed-off-by: Marton Balint <cus@fazekas.hu> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx88')
-rw-r--r--drivers/media/video/cx88/Makefile2
-rw-r--r--drivers/media/video/cx88/cx88-core.c26
-rw-r--r--drivers/media/video/cx88/cx88-dsp.c299
-rw-r--r--drivers/media/video/cx88/cx88-tvaudio.c54
-rw-r--r--drivers/media/video/cx88/cx88.h10
5 files changed, 381 insertions, 10 deletions
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
index b06b1275a9ec..5b7e26761f0a 100644
--- a/drivers/media/video/cx88/Makefile
+++ b/drivers/media/video/cx88/Makefile
@@ -1,5 +1,5 @@
1cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \ 1cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \
2 cx88-input.o 2 cx88-dsp.o cx88-input.o
3cx8800-objs := cx88-video.o cx88-vbi.o 3cx8800-objs := cx88-video.o cx88-vbi.o
4cx8802-objs := cx88-mpeg.o 4cx8802-objs := cx88-mpeg.o
5 5
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index b4049de071a2..cf634606ba9a 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -231,7 +231,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
231 * can use the whole SDRAM for the DMA fifos. To simplify things, we 231 * can use the whole SDRAM for the DMA fifos. To simplify things, we
232 * use a static memory layout. That surely will waste memory in case 232 * use a static memory layout. That surely will waste memory in case
233 * we don't use all DMA channels at the same time (which will be the 233 * we don't use all DMA channels at the same time (which will be the
234 * case most of the time). But that still gives us enougth FIFO space 234 * case most of the time). But that still gives us enough FIFO space
235 * to be able to deal with insane long pci latencies ... 235 * to be able to deal with insane long pci latencies ...
236 * 236 *
237 * FIFO space allocations: 237 * FIFO space allocations:
@@ -241,6 +241,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
241 * channel 24 (vbi) - 4.0k 241 * channel 24 (vbi) - 4.0k
242 * channels 25+26 (audio) - 4.0k 242 * channels 25+26 (audio) - 4.0k
243 * channel 28 (mpeg) - 4.0k 243 * channel 28 (mpeg) - 4.0k
244 * channel 27 (audio rds)- 3.0k
244 * TOTAL = 29.0k 245 * TOTAL = 29.0k
245 * 246 *
246 * Every channel has 160 bytes control data (64 bytes instruction 247 * Every channel has 160 bytes control data (64 bytes instruction
@@ -337,6 +338,18 @@ struct sram_channel cx88_sram_channels[] = {
337 .cnt1_reg = MO_DMA28_CNT1, 338 .cnt1_reg = MO_DMA28_CNT1,
338 .cnt2_reg = MO_DMA28_CNT2, 339 .cnt2_reg = MO_DMA28_CNT2,
339 }, 340 },
341 [SRAM_CH27] = {
342 .name = "audio rds",
343 .cmds_start = 0x1801C0,
344 .ctrl_start = 0x180860,
345 .cdt = 0x180860 + 64,
346 .fifo_start = 0x187400,
347 .fifo_size = 0x000C00,
348 .ptr1_reg = MO_DMA27_PTR1,
349 .ptr2_reg = MO_DMA27_PTR2,
350 .cnt1_reg = MO_DMA27_CNT1,
351 .cnt2_reg = MO_DMA27_CNT2,
352 },
340}; 353};
341 354
342int cx88_sram_channel_setup(struct cx88_core *core, 355int cx88_sram_channel_setup(struct cx88_core *core,
@@ -598,6 +611,7 @@ int cx88_reset(struct cx88_core *core)
598 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0); 611 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
599 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0); 612 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
600 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0); 613 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
614 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0);
601 615
602 /* misc init ... */ 616 /* misc init ... */
603 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable 617 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
@@ -796,6 +810,8 @@ int cx88_start_audio_dma(struct cx88_core *core)
796 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */ 810 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
797 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4; 811 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
798 812
813 int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES;
814
799 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ 815 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
800 if (cx_read(MO_AUD_DMACNTRL) & 0x10) 816 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
801 return 0; 817 return 0;
@@ -803,12 +819,14 @@ int cx88_start_audio_dma(struct cx88_core *core)
803 /* setup fifo + format */ 819 /* setup fifo + format */
804 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0); 820 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
805 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0); 821 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
822 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27],
823 rds_bpl, 0);
806 824
807 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */ 825 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
808 cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */ 826 cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */
809 827
810 /* start dma */ 828 /* enable Up, Down and Audio RDS fifo */
811 cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */ 829 cx_write(MO_AUD_DMACNTRL, 0x0007);
812 830
813 return 0; 831 return 0;
814} 832}
diff --git a/drivers/media/video/cx88/cx88-dsp.c b/drivers/media/video/cx88/cx88-dsp.c
new file mode 100644
index 000000000000..a78286e853c3
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-dsp.c
@@ -0,0 +1,299 @@
1/*
2 *
3 * Stereo and SAP detection for cx88
4 *
5 * Copyright (c) 2009 Marton Balint <cus@fazekas.hu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/jiffies.h>
25
26#include "cx88.h"
27#include "cx88-reg.h"
28
29#define INT_PI ((s32)(3.141592653589 * 32768.0))
30
31#define compat_remainder(a, b) \
32 ((float)(((s32)((a)*100))%((s32)((b)*100)))/100.0)
33
34#define baseband_freq(carrier, srate, tone) ((s32)( \
35 (compat_remainder(carrier + tone, srate)) / srate * 2 * INT_PI))
36
37/* We calculate the baseband frequencies of the carrier and the pilot tones
38 * based on the the sampling rate of the audio rds fifo. */
39
40#define FREQ_A2_CARRIER baseband_freq(54687.5, 2689.36, 0.0)
41#define FREQ_A2_DUAL baseband_freq(54687.5, 2689.36, 274.1)
42#define FREQ_A2_STEREO baseband_freq(54687.5, 2689.36, 117.5)
43
44/* The frequencies below are from the reference driver. They probably need
45 * further adjustments, because they are not tested at all. You may even need
46 * to play a bit with the registers of the chip to select the proper signal
47 * for the input of the audio rds fifo, and measure it's sampling rate to
48 * calculate the proper baseband frequencies... */
49
50#define FREQ_A2M_CARRIER ((s32)(2.114516 * 32768.0))
51#define FREQ_A2M_DUAL ((s32)(2.754916 * 32768.0))
52#define FREQ_A2M_STEREO ((s32)(2.462326 * 32768.0))
53
54#define FREQ_EIAJ_CARRIER ((s32)(1.963495 * 32768.0)) /* 5pi/8 */
55#define FREQ_EIAJ_DUAL ((s32)(2.562118 * 32768.0))
56#define FREQ_EIAJ_STEREO ((s32)(2.601053 * 32768.0))
57
58#define FREQ_BTSC_DUAL ((s32)(1.963495 * 32768.0)) /* 5pi/8 */
59#define FREQ_BTSC_DUAL_REF ((s32)(1.374446 * 32768.0)) /* 7pi/16 */
60
61#define FREQ_BTSC_SAP ((s32)(2.471532 * 32768.0))
62#define FREQ_BTSC_SAP_REF ((s32)(1.730072 * 32768.0))
63
64/* The spectrum of the signal should be empty between these frequencies. */
65#define FREQ_NOISE_START ((s32)(0.100000 * 32768.0))
66#define FREQ_NOISE_END ((s32)(1.200000 * 32768.0))
67
68static unsigned int dsp_debug;
69module_param(dsp_debug, int, 0644);
70MODULE_PARM_DESC(dsp_debug, "enable audio dsp debug messages");
71
72#define dprintk(level, fmt, arg...) if (dsp_debug >= level) \
73 printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
74
75static s32 int_cos(u32 x)
76{
77 u32 t2, t4, t6, t8;
78 s32 ret;
79 u16 period = x / INT_PI;
80 if (period % 2)
81 return -int_cos(x - INT_PI);
82 x = x % INT_PI;
83 if (x > INT_PI/2)
84 return -int_cos(INT_PI/2 - (x % (INT_PI/2)));
85 /* Now x is between 0 and INT_PI/2.
86 * To calculate cos(x) we use it's Taylor polinom. */
87 t2 = x*x/32768/2;
88 t4 = t2*x/32768*x/32768/3/4;
89 t6 = t4*x/32768*x/32768/5/6;
90 t8 = t6*x/32768*x/32768/7/8;
91 ret = 32768-t2+t4-t6+t8;
92 return ret;
93}
94
95static u32 int_goertzel(s16 x[], u32 N, u32 freq)
96{
97 /* We use the Goertzel algorithm to determine the power of the
98 * given frequency in the signal */
99 s32 s_prev = 0;
100 s32 s_prev2 = 0;
101 s32 coeff = 2*int_cos(freq);
102 u32 i;
103 for (i = 0; i < N; i++) {
104 s32 s = x[i] + ((s64)coeff*s_prev/32768) - s_prev2;
105 s_prev2 = s_prev;
106 s_prev = s;
107 }
108 return (u32)(((s64)s_prev2*s_prev2 + (s64)s_prev*s_prev -
109 (s64)coeff*s_prev2*s_prev/32768)/N/N);
110}
111
112static u32 freq_magnitude(s16 x[], u32 N, u32 freq)
113{
114 u32 sum = int_goertzel(x, N, freq);
115 return (u32)int_sqrt(sum);
116}
117
118static u32 noise_magnitude(s16 x[], u32 N, u32 freq_start, u32 freq_end)
119{
120 int i;
121 u32 sum = 0;
122 u32 freq_step;
123 int samples = 5;
124
125 if (N > 192) {
126 /* The last 192 samples are enough for noise detection */
127 x += (N-192);
128 N = 192;
129 }
130
131 freq_step = (freq_end - freq_start) / (samples - 1);
132
133 for (i = 0; i < samples; i++) {
134 sum += int_goertzel(x, N, freq_start);
135 freq_start += freq_step;
136 }
137
138 return (u32)int_sqrt(sum / samples);
139}
140
141static s32 detect_a2_a2m_eiaj(struct cx88_core *core, s16 x[], u32 N)
142{
143 s32 carrier, stereo, dual, noise;
144 s32 carrier_freq, stereo_freq, dual_freq;
145 s32 ret;
146
147 switch (core->tvaudio) {
148 case WW_BG:
149 case WW_DK:
150 carrier_freq = FREQ_A2_CARRIER;
151 stereo_freq = FREQ_A2_STEREO;
152 dual_freq = FREQ_A2_DUAL;
153 break;
154 case WW_M:
155 carrier_freq = FREQ_A2M_CARRIER;
156 stereo_freq = FREQ_A2M_STEREO;
157 dual_freq = FREQ_A2M_DUAL;
158 break;
159 case WW_EIAJ:
160 carrier_freq = FREQ_EIAJ_CARRIER;
161 stereo_freq = FREQ_EIAJ_STEREO;
162 dual_freq = FREQ_EIAJ_DUAL;
163 break;
164 default:
165 printk(KERN_WARNING "%s/0: unsupported audio mode %d for %s\n",
166 core->name, core->tvaudio, __func__);
167 return UNSET;
168 }
169
170 carrier = freq_magnitude(x, N, carrier_freq);
171 stereo = freq_magnitude(x, N, stereo_freq);
172 dual = freq_magnitude(x, N, dual_freq);
173 noise = noise_magnitude(x, N, FREQ_NOISE_START, FREQ_NOISE_END);
174
175 dprintk(1, "detect a2/a2m/eiaj: carrier=%d, stereo=%d, dual=%d, "
176 "noise=%d\n", carrier, stereo, dual, noise);
177
178 if (stereo > dual)
179 ret = V4L2_TUNER_SUB_STEREO;
180 else
181 ret = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
182
183 if (core->tvaudio == WW_EIAJ) {
184 /* EIAJ checks may need adjustments */
185 if ((carrier > max(stereo, dual)*2) &&
186 (carrier < max(stereo, dual)*6) &&
187 (carrier > 20 && carrier < 200) &&
188 (max(stereo, dual) > min(stereo, dual))) {
189 /* For EIAJ the carrier is always present,
190 so we probably don't need noise detection */
191 return ret;
192 }
193 } else {
194 if ((carrier > max(stereo, dual)*2) &&
195 (carrier < max(stereo, dual)*8) &&
196 (carrier > 20 && carrier < 200) &&
197 (noise < 10) &&
198 (max(stereo, dual) > min(stereo, dual)*2)) {
199 return ret;
200 }
201 }
202 return V4L2_TUNER_SUB_MONO;
203}
204
205static s32 detect_btsc(struct cx88_core *core, s16 x[], u32 N)
206{
207 s32 sap_ref = freq_magnitude(x, N, FREQ_BTSC_SAP_REF);
208 s32 sap = freq_magnitude(x, N, FREQ_BTSC_SAP);
209 s32 dual_ref = freq_magnitude(x, N, FREQ_BTSC_DUAL_REF);
210 s32 dual = freq_magnitude(x, N, FREQ_BTSC_DUAL);
211 dprintk(1, "detect btsc: dual_ref=%d, dual=%d, sap_ref=%d, sap=%d"
212 "\n", dual_ref, dual, sap_ref, sap);
213 /* FIXME: Currently not supported */
214 return UNSET;
215}
216
217static s16 *read_rds_samples(struct cx88_core *core, u32 *N)
218{
219 struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27];
220 s16 *samples;
221
222 unsigned int i;
223 unsigned int bpl = srch->fifo_size/AUD_RDS_LINES;
224 unsigned int spl = bpl/4;
225 unsigned int sample_count = spl*(AUD_RDS_LINES-1);
226
227 u32 current_address = cx_read(srch->ptr1_reg);
228 u32 offset = (current_address - srch->fifo_start + bpl);
229
230 dprintk(1, "read RDS samples: current_address=%08x (offset=%08x), "
231 "sample_count=%d, aud_intstat=%08x\n", current_address,
232 current_address - srch->fifo_start, sample_count,
233 cx_read(MO_AUD_INTSTAT));
234
235 samples = kmalloc(sizeof(s16)*sample_count, GFP_KERNEL);
236 if (!samples)
237 return NULL;
238
239 *N = sample_count;
240
241 for (i = 0; i < sample_count; i++) {
242 offset = offset % (AUD_RDS_LINES*bpl);
243 samples[i] = cx_read(srch->fifo_start + offset);
244 offset += 4;
245 }
246
247 if (dsp_debug >= 2) {
248 dprintk(2, "RDS samples dump: ");
249 for (i = 0; i < sample_count; i++)
250 printk("%hd ", samples[i]);
251 printk(".\n");
252 }
253
254 return samples;
255}
256
257s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core)
258{
259 s16 *samples;
260 u32 N = 0;
261 s32 ret = UNSET;
262
263 /* If audio RDS fifo is disabled, we can't read the samples */
264 if (!(cx_read(MO_AUD_DMACNTRL) & 0x04))
265 return ret;
266 if (!(cx_read(AUD_CTL) & EN_FMRADIO_EN_RDS))
267 return ret;
268
269 /* Wait at least 500 ms after an audio standard change */
270 if (time_before(jiffies, core->last_change + msecs_to_jiffies(500)))
271 return ret;
272
273 samples = read_rds_samples(core, &N);
274
275 if (!samples)
276 return ret;
277
278 switch (core->tvaudio) {
279 case WW_BG:
280 case WW_DK:
281 ret = detect_a2_a2m_eiaj(core, samples, N);
282 break;
283 case WW_BTSC:
284 ret = detect_btsc(core, samples, N);
285 break;
286 }
287
288 kfree(samples);
289
290 if (UNSET != ret)
291 dprintk(1, "stereo/sap detection result:%s%s%s\n",
292 (ret & V4L2_TUNER_SUB_MONO) ? " mono" : "",
293 (ret & V4L2_TUNER_SUB_STEREO) ? " stereo" : "",
294 (ret & V4L2_TUNER_SUB_LANG2) ? " dual" : "");
295
296 return ret;
297}
298EXPORT_SYMBOL(cx88_dsp_detect_stereo_sap);
299
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index 7dd506b987fe..f9501eb8557c 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -163,6 +163,8 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl)
163 /* unmute */ 163 /* unmute */
164 volume = cx_sread(SHADOW_AUD_VOL_CTL); 164 volume = cx_sread(SHADOW_AUD_VOL_CTL);
165 cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume); 165 cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume);
166
167 core->last_change = jiffies;
166} 168}
167 169
168/* ----------------------------------------------------------- */ 170/* ----------------------------------------------------------- */
@@ -745,6 +747,7 @@ void cx88_set_tvaudio(struct cx88_core *core)
745 break; 747 break;
746 case WW_BG: 748 case WW_BG:
747 case WW_DK: 749 case WW_DK:
750 case WW_M:
748 case WW_I: 751 case WW_I:
749 case WW_L: 752 case WW_L:
750 /* prepare all dsp registers */ 753 /* prepare all dsp registers */
@@ -756,6 +759,7 @@ void cx88_set_tvaudio(struct cx88_core *core)
756 if (0 == cx88_detect_nicam(core)) { 759 if (0 == cx88_detect_nicam(core)) {
757 /* fall back to fm / am mono */ 760 /* fall back to fm / am mono */
758 set_audio_standard_A2(core, EN_A2_FORCE_MONO1); 761 set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
762 core->audiomode_current = V4L2_TUNER_MODE_MONO;
759 core->use_nicam = 0; 763 core->use_nicam = 0;
760 } else { 764 } else {
761 core->use_nicam = 1; 765 core->use_nicam = 1;
@@ -787,6 +791,7 @@ void cx88_set_tvaudio(struct cx88_core *core)
787void cx88_newstation(struct cx88_core *core) 791void cx88_newstation(struct cx88_core *core)
788{ 792{
789 core->audiomode_manual = UNSET; 793 core->audiomode_manual = UNSET;
794 core->last_change = jiffies;
790} 795}
791 796
792void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) 797void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
@@ -805,12 +810,50 @@ void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
805 aud_ctl_names[cx_read(AUD_CTL) & 63]); 810 aud_ctl_names[cx_read(AUD_CTL) & 63]);
806 core->astat = reg; 811 core->astat = reg;
807 812
808/* TODO 813 t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
809 Reading from AUD_STATUS is not enough 814 V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
810 for auto-detecting sap/dual-fm/nicam. 815 t->rxsubchans = UNSET;
811 Add some code here later. 816 t->audmode = V4L2_TUNER_MODE_MONO;
812*/
813 817
818 switch (mode) {
819 case 0:
820 t->audmode = V4L2_TUNER_MODE_STEREO;
821 break;
822 case 1:
823 t->audmode = V4L2_TUNER_MODE_LANG2;
824 break;
825 case 2:
826 t->audmode = V4L2_TUNER_MODE_MONO;
827 break;
828 case 3:
829 t->audmode = V4L2_TUNER_MODE_SAP;
830 break;
831 }
832
833 switch (core->tvaudio) {
834 case WW_BTSC:
835 case WW_BG:
836 case WW_DK:
837 case WW_M:
838 case WW_EIAJ:
839 if (!core->use_nicam) {
840 t->rxsubchans = cx88_dsp_detect_stereo_sap(core);
841 break;
842 }
843 break;
844 default:
845 /* nothing */
846 break;
847 }
848
849 /* If software stereo detection is not supported... */
850 if (UNSET == t->rxsubchans) {
851 t->rxsubchans = V4L2_TUNER_SUB_MONO;
852 /* If the hardware itself detected stereo, also return
853 stereo as an available subchannel */
854 if (V4L2_TUNER_MODE_STEREO == t->audmode)
855 t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
856 }
814 return; 857 return;
815} 858}
816 859
@@ -847,6 +890,7 @@ void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual)
847 break; 890 break;
848 case WW_BG: 891 case WW_BG:
849 case WW_DK: 892 case WW_DK:
893 case WW_M:
850 case WW_I: 894 case WW_I:
851 case WW_L: 895 case WW_L:
852 if (1 == core->use_nicam) { 896 if (1 == core->use_nicam) {
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index f55e4eeed78c..9d83762163f5 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -65,6 +65,8 @@
65#define VBI_LINE_COUNT 17 65#define VBI_LINE_COUNT 17
66#define VBI_LINE_LENGTH 2048 66#define VBI_LINE_LENGTH 2048
67 67
68#define AUD_RDS_LINES 4
69
68/* need "shadow" registers for some write-only ones ... */ 70/* need "shadow" registers for some write-only ones ... */
69#define SHADOW_AUD_VOL_CTL 1 71#define SHADOW_AUD_VOL_CTL 1
70#define SHADOW_AUD_BAL_CTL 2 72#define SHADOW_AUD_BAL_CTL 2
@@ -132,6 +134,7 @@ struct cx88_ctrl {
132#define SRAM_CH25 4 /* audio */ 134#define SRAM_CH25 4 /* audio */
133#define SRAM_CH26 5 135#define SRAM_CH26 5
134#define SRAM_CH28 6 /* mpeg */ 136#define SRAM_CH28 6 /* mpeg */
137#define SRAM_CH27 7 /* audio rds */
135/* more */ 138/* more */
136 139
137struct sram_channel { 140struct sram_channel {
@@ -352,6 +355,7 @@ struct cx88_core {
352 u32 input; 355 u32 input;
353 u32 astat; 356 u32 astat;
354 u32 use_nicam; 357 u32 use_nicam;
358 unsigned long last_change;
355 359
356 /* IR remote control state */ 360 /* IR remote control state */
357 struct cx88_IR *ir; 361 struct cx88_IR *ir;
@@ -654,6 +658,7 @@ extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl);
654#define WW_I2SPT 8 658#define WW_I2SPT 8
655#define WW_FM 9 659#define WW_FM 9
656#define WW_I2SADC 10 660#define WW_I2SADC 10
661#define WW_M 11
657 662
658void cx88_set_tvaudio(struct cx88_core *core); 663void cx88_set_tvaudio(struct cx88_core *core);
659void cx88_newstation(struct cx88_core *core); 664void cx88_newstation(struct cx88_core *core);
@@ -667,6 +672,11 @@ struct cx8802_dev *cx8802_get_device(int minor);
667struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); 672struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
668 673
669/* ----------------------------------------------------------- */ 674/* ----------------------------------------------------------- */
675/* cx88-dsp.c */
676
677s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core);
678
679/* ----------------------------------------------------------- */
670/* cx88-input.c */ 680/* cx88-input.c */
671 681
672int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci); 682int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci);