xref: /dragonfly/sys/dev/video/cxm/cxm_msp34xxx.c (revision 9437e5dc5caba072e02cf20d3cb0af3e14aa3a80)
1 /*
2  * Copyright (c) 2003, 2004, 2005
3  *        John Wehle <john@feith.com>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *        This product includes software developed by John Wehle.
16  * 4. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED.      IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Audio decoder routines for the Conexant MPEG-2 Codec driver.
34  *
35  * Ideally these routines should be implemented as a separate
36  * driver which has a generic audio decoder interface so that
37  * it's not necessary for each multimedia driver to re-invent
38  * the wheel.
39  */
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/conf.h>
44 #include <sys/uio.h>
45 #include <sys/kernel.h>
46 #include <sys/poll.h>
47 #include <sys/select.h>
48 #include <sys/resource.h>
49 #include <sys/bus.h>
50 #include <sys/rman.h>
51 
52 #include <machine/clock.h>
53 
54 #include <dev/video/cxm/cxm.h>
55 
56 #include <bus/iicbus/iiconf.h>
57 #include <bus/iicbus/iicbus.h>
58 
59 #include "iicbb_if.h"
60 
61 
62 static const struct cxm_msp_command
63 msp34x5G_init = {
64           5,
65           {
66                     /* Enable Automatic Sound Select */
67                     { CXM_MSP3400C_DEM, 0x0030, { 0x20, 0x03 } },
68                     /* SCART Prescale = 0 dB */
69                     { CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } },
70                     /* FM / AM Prescale = 100 Khz and FM Matrix = Sound A Mono  */
71                     { CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x03 } },
72                     /* NICAM Prescale = 9 dB  */
73                     { CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } },
74                     /* Enable Automatic Standard Select */
75                     { CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } },
76           }
77 };
78 
79 static const struct cxm_msp_command
80 msp34x5G_select_tuner = {
81           3,
82           {
83                     /* Loudspeaker Source = demodulator (St or A), Matrix = St */
84                     { CXM_MSP3400C_DFP, 0x0008, { 0x03, 0x20 } },
85                     /* SCART1_L/R Source = demodulator (St or A), Matrix = St */
86                     { CXM_MSP3400C_DFP, 0x000a, { 0x03, 0x20 } },
87                     /* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */
88                     { CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } }
89           }
90 };
91 
92 static const struct cxm_msp_command
93 msp34x5D_init = {
94           4,
95           {
96                     /* Enable Automatic NICAM-FM/AM Switching */
97                     { CXM_MSP3400C_DEM, 0x0021, { 0x00, 0x01 } },
98                     /* SCART Prescale = 0 dB */
99                     { CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } },
100                     /* NICAM Prescale = 9 dB  */
101                     { CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } },
102                     /* Enable Automatic Standard Select */
103                     { CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } },
104           }
105 };
106 
107 static const struct cxm_msp_command
108 msp34x5D_select_tuner = {
109           5,
110           {
111                     /* Loudspeaker Source = demodulator (NICAM), Matrix = St */
112                     { CXM_MSP3400C_DFP, 0x0008, { 0x01, 0x20 } },
113                     /* SCART1_L/R Source = demodulator (NICAM), Matrix = St */
114                     { CXM_MSP3400C_DFP, 0x000a, { 0x01, 0x20 } },
115                     /* FM / AM Prescale = 100 Khz and FM Matrix = No Matrix  */
116                     { CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x00 } },
117                     /* FM Deemphasis = 50 us */
118                     { CXM_MSP3400C_DFP, 0x000f, { 0x00, 0x00 } },
119                     /* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */
120                     { CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } }
121           }
122 };
123 
124 static const struct cxm_msp_command
125 msp34xxx_mute = {
126           2,
127           {
128                     /* Loudspeaker volume = mute */
129                     { CXM_MSP3400C_DFP, 0x0000, { 0x00, 0x00 } },
130                     /* SC1_OUT_L/R volume = mute */
131                     { CXM_MSP3400C_DFP, 0x0007, { 0x00, 0x01 } }
132           }
133 };
134 
135 static const struct cxm_msp_command
136 msp34xxx_unmute = {
137           2,
138           {
139                     /* Loudspeaker volume = 0 db */
140                     { CXM_MSP3400C_DFP, 0x0000, { 0x73, 0x00 } },
141                     /* SC1_OUT_L/R volume = 0 db */
142                     { CXM_MSP3400C_DFP, 0x0007, { 0x73, 0x01 } }
143           }
144 };
145 
146 static const struct cxm_msp_command
147 msp34xxx_select_fm = {
148           3,
149           {
150                     /* Loudspeaker Source = SCART, Matrix = STEREO */
151                     { CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } },
152                     /* SCART1_L/R Source = SCART, Matrix = STEREO */
153                     { CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } },
154                     /* DSP In = SC2_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */
155                     { CXM_MSP3400C_DFP, 0x0013, { 0x0e, 0x00 } }
156           }
157 };
158 
159 static const struct cxm_msp_command
160 msp34xxx_select_line_in = {
161           3,
162           {
163                     /* Loudspeaker Source = SCART, Matrix = STEREO */
164                     { CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } },
165                     /* SCART1_L/R Source = SCART, Matrix = STEREO */
166                     { CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } },
167                     /* DSP In = SC1_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */
168                     { CXM_MSP3400C_DFP, 0x0013, { 0x0c, 0x00 } }
169           }
170 };
171 
172 
173 /* Reset the MSP or DPL chip */
174 static int
cxm_msp_dpl_reset(device_t iicbus,int i2c_addr)175 cxm_msp_dpl_reset(device_t iicbus, int i2c_addr)
176 {
177           unsigned char msg[3];
178           int sent;
179 
180           /* put into reset mode */
181           msg[0] = 0x00;
182           msg[1] = 0x80;
183           msg[2] = 0x00;
184 
185           if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
186                     return -1;
187 
188           if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
189               || sent != sizeof(msg))
190                     goto fail;
191 
192           iicbus_stop(iicbus);
193 
194           /* put back to operational mode */
195           msg[0] = 0x00;
196           msg[1] = 0x00;
197           msg[2] = 0x00;
198 
199           if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
200                     return -1;
201 
202           if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
203               || sent != sizeof(msg))
204                     goto fail;
205 
206           iicbus_stop(iicbus);
207 
208           return 0;
209 
210 fail:
211           iicbus_stop(iicbus);
212           return -1;
213 }
214 
215 
216 /* Read from the MSP or DPL registers */
217 static int
cxm_msp_dpl_read(device_t iicbus,int i2c_addr,unsigned char dev,unsigned int addr,char * buf,int len)218 cxm_msp_dpl_read(device_t iicbus, int i2c_addr,
219                       unsigned char dev, unsigned int addr,
220                       char *buf, int len)
221 {
222           unsigned char msg[3];
223           int received;
224           int sent;
225 
226           msg[0] = (unsigned char)(dev + 1);
227           msg[1] = (unsigned char)(addr >> 8);
228           msg[2] = (unsigned char)addr;
229 
230           if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
231                     return -1;
232 
233           if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
234               || sent != sizeof(msg))
235                     goto fail;
236 
237           if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
238                     goto fail;
239 
240           if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
241                     goto fail;
242 
243           iicbus_stop(iicbus);
244 
245           return received;
246 
247 fail:
248           iicbus_stop(iicbus);
249           return -1;
250 }
251 
252 
253 /* Write to the MSP or DPL registers */
254 static int
cxm_msp_dpl_write(device_t iicbus,int i2c_addr,unsigned char dev,unsigned int addr,const char * buf,int len)255 cxm_msp_dpl_write(device_t iicbus, int i2c_addr,
256                        unsigned char dev, unsigned int addr,
257                        const char *buf, int len)
258 {
259           unsigned char msg[3];
260           int sent;
261 
262           msg[0] = dev;
263           msg[1] = (unsigned char)(addr >> 8);
264           msg[2] = (unsigned char)addr;
265 
266           if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
267                     return -1;
268 
269           if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
270               || sent != sizeof(msg))
271                     goto fail;
272 
273           if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
274                     goto fail;
275 
276           iicbus_stop(iicbus);
277 
278           return sent;
279 
280 fail:
281           iicbus_stop(iicbus);
282           return -1;
283 }
284 
285 
286 int
cxm_msp_init(struct cxm_softc * sc)287 cxm_msp_init(struct cxm_softc *sc)
288 {
289           unsigned char rev1[2];
290           unsigned char rev2[2];
291           unsigned int i;
292           unsigned int nsettings;
293           const struct cxm_msp_setting *settings;
294 
295           if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
296                     return -1;
297 
298           if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
299                                    0x001e, rev1, sizeof(rev1)) != sizeof(rev1))
300                     return -1;
301 
302           if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
303                                    0x001f, rev2, sizeof(rev2)) != sizeof(rev2))
304                     return -1;
305 
306           ksnprintf(sc->msp_name, sizeof(sc->msp_name), "%c4%02d%c-%c%d",
307                      ((rev1[1] >> 4) & 0x0f) + '3', rev2[0],
308                      (rev1[1] & 0x0f) + '@', rev1[0] + '@', rev2[1] & 0x1f);
309 
310           /*
311            * MSP 34x5D, 34x5G, and MSP 44x8G are the
312            * only audio decoders currently supported.
313            */
314 
315           if (strncmp(&sc->msp_name[0], "34", 2) == 0
316               && strncmp(&sc->msp_name[3], "5D", 2) == 0)
317             ;
318           else if (strncmp(&sc->msp_name[0], "34", 2) == 0
319                      && strncmp(&sc->msp_name[3], "5G", 2) == 0)
320             ;
321           else if (strncmp(&sc->msp_name[0], "44", 2) == 0
322                      && strncmp(&sc->msp_name[3], "8G", 2) == 0)
323             ;
324           else {
325                     device_printf(sc->dev, "unknown audio decoder MSP%s\n",
326                         sc->msp_name);
327                     return -1;
328           }
329 
330           nsettings = msp34x5G_init.nsettings;
331           settings = msp34x5G_init.settings;
332           if (sc->msp_name[4] == 'D') {
333                     nsettings = msp34x5D_init.nsettings;
334                     settings = msp34x5D_init.settings;
335             }
336 
337           for (i = 0; i < nsettings; i++)
338                     if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
339                                               settings[i].dev, settings[i].addr,
340                                               settings[i].value,
341                                               sizeof(settings[i].value))
342                         != sizeof(settings[i].value))
343                               return -1;
344 
345           if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
346                     return -1;
347 
348           device_printf(sc->dev, "MSP%s audio decoder\n", sc->msp_name);
349 
350           return 0;
351 }
352 
353 
354 int
cxm_msp_mute(struct cxm_softc * sc)355 cxm_msp_mute(struct cxm_softc *sc)
356 {
357           unsigned int i;
358           unsigned int nsettings;
359           const struct cxm_msp_setting *settings;
360 
361           nsettings = msp34xxx_mute.nsettings;
362           settings = msp34xxx_mute.settings;
363 
364           for (i = 0; i < nsettings; i++)
365                     if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
366                                               settings[i].dev, settings[i].addr,
367                                               settings[i].value,
368                                               sizeof(settings[i].value))
369                         != sizeof(settings[i].value))
370                               return -1;
371 
372           return 0;
373 }
374 
375 
376 int
cxm_msp_unmute(struct cxm_softc * sc)377 cxm_msp_unmute(struct cxm_softc *sc)
378 {
379           unsigned int i;
380           unsigned int nsettings;
381           const struct cxm_msp_setting *settings;
382 
383           nsettings = msp34xxx_unmute.nsettings;
384           settings = msp34xxx_unmute.settings;
385 
386           for (i = 0; i < nsettings; i++)
387                     if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
388                                               settings[i].dev, settings[i].addr,
389                                               settings[i].value,
390                                               sizeof(settings[i].value))
391                         != sizeof(settings[i].value))
392                               return -1;
393 
394           return 0;
395 }
396 
397 
398 int
cxm_msp_is_muted(struct cxm_softc * sc)399 cxm_msp_is_muted(struct cxm_softc *sc)
400 {
401           unsigned char volume[2];
402 
403           if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
404                                    0x0000, volume, sizeof(volume)) != sizeof(volume))
405                     return -1;
406 
407           return volume[0] == 0x00 || volume[0] == 0xff ? 1 : 0;
408 }
409 
410 
411 int
cxm_msp_select_source(struct cxm_softc * sc,enum cxm_source source)412 cxm_msp_select_source(struct cxm_softc *sc, enum cxm_source source)
413 {
414           unsigned int i;
415           unsigned int nsettings;
416           const struct cxm_msp_setting *settings;
417 
418           switch (source) {
419           case cxm_fm_source:
420                     nsettings = msp34xxx_select_fm.nsettings;
421                     settings = msp34xxx_select_fm.settings;
422                     break;
423 
424           case cxm_line_in_source_composite:
425           case cxm_line_in_source_svideo:
426                     nsettings = msp34xxx_select_line_in.nsettings;
427                     settings = msp34xxx_select_line_in.settings;
428                     break;
429 
430           case cxm_tuner_source:
431                     nsettings = msp34x5G_select_tuner.nsettings;
432                     settings = msp34x5G_select_tuner.settings;
433                     if (sc->msp_name[4] == 'D') {
434                               nsettings = msp34x5D_select_tuner.nsettings;
435                               settings = msp34x5D_select_tuner.settings;
436                       }
437                     break;
438 
439           default:
440                     return -1;
441           }
442 
443           for (i = 0; i < nsettings; i++)
444                     if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
445                                               settings[i].dev, settings[i].addr,
446                                               settings[i].value,
447                                               sizeof(settings[i].value))
448                         != sizeof(settings[i].value))
449                               return -1;
450 
451           return 0;
452 }
453 
454 
455 enum cxm_source
cxm_msp_selected_source(struct cxm_softc * sc)456 cxm_msp_selected_source(struct cxm_softc *sc)
457 {
458           unsigned char dsp[2];
459           unsigned char source[2];
460 
461           if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
462                                    0x0008, source, sizeof(source)) != sizeof(source))
463                     return cxm_unknown_source;
464 
465           switch (source[0]) {
466           case 0: /* FM / AM mono signal */
467           case 1: /* Stereo or A / B */
468           case 3: /* Stereo or A */
469           case 4: /* Stereo or B */
470                     return cxm_tuner_source;
471 
472           case 2: /* SCART */
473                     break;
474 
475           default:
476                     return cxm_unknown_source;
477           }
478 
479           if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
480                                    0x0013, dsp, sizeof(dsp)) != sizeof(dsp))
481                     return cxm_unknown_source;
482 
483           if (dsp[1] & 0x20)
484                     return cxm_unknown_source;
485 
486           switch (dsp[0] & 0x03) {
487           case 0:
488                     return cxm_line_in_source_composite;
489 
490           case 2:
491                     return cxm_fm_source;
492 
493           default:
494                      return cxm_unknown_source;
495           }
496 }
497 
498 
499 int
cxm_msp_autodetect_standard(struct cxm_softc * sc)500 cxm_msp_autodetect_standard(struct cxm_softc *sc)
501 {
502           unsigned int i;
503           int locked;
504           unsigned int nsettings;
505           const struct cxm_msp_setting *settings;
506 
507           switch (cxm_msp_selected_source(sc)) {
508           case cxm_tuner_source:
509                     break;
510 
511           case cxm_fm_source:
512           case cxm_line_in_source_composite:
513           case cxm_line_in_source_svideo:
514                     return 1;
515 
516           default:
517                     return -1;
518           }
519 
520           /*
521            * Section 3.3.2.2 of the data sheet states:
522            *
523            *   A general refresh of the STANDARD SELECT
524            *   register is not allowed.
525            */
526 
527           if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
528                     return -1;
529 
530           nsettings = msp34x5G_init.nsettings;
531           settings = msp34x5G_init.settings;
532           if (sc->msp_name[4] == 'D') {
533                     nsettings = msp34x5D_init.nsettings;
534                     settings = msp34x5D_init.settings;
535             }
536 
537           for (i = 0; i < nsettings; i++)
538                     if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
539                                               settings[i].dev, settings[i].addr,
540                                               settings[i].value,
541                                               sizeof(settings[i].value))
542                         != sizeof(settings[i].value))
543                               return -1;
544 
545           locked = cxm_msp_wait_for_lock(sc);
546 
547           if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
548                     return -1;
549 
550           return locked;
551 }
552 
553 
554 int
cxm_msp_is_locked(struct cxm_softc * sc)555 cxm_msp_is_locked(struct cxm_softc *sc)
556 {
557           unsigned char source[2];
558           unsigned char standard[2];
559 
560           if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
561                                    0x0008, source, sizeof(source)) != sizeof(source))
562                     return -1;
563 
564           switch (source[0]) {
565           case 0: /* FM / AM mono signal */
566           case 1: /* Stereo or A / B */
567           case 3: /* Stereo or A */
568           case 4: /* Stereo or B */
569                     break;
570 
571           default:
572                     return 1;
573           }
574 
575           if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DEM,
576                                    0x007e, standard, sizeof(standard))
577               != sizeof(standard))
578                     return -1;
579 
580           if (standard[0] >= 8 || (standard[0] == 0 && standard[1] == 0))
581                     return 0;
582 
583           return 1;
584 }
585 
586 
587 int
cxm_msp_wait_for_lock(struct cxm_softc * sc)588 cxm_msp_wait_for_lock(struct cxm_softc *sc)
589 {
590           unsigned int i;
591 
592           /*
593            * Section 3.3.2.1 of the data sheet states:
594            *
595            *   Within 0.5 s the detection and setup of the actual
596            *   TV sound standard is performed.  The detected result
597            *   can be read out of the STANDARD RESULT register by
598            *   the control processor.
599            */
600 
601           for (i = 0; i < 10; i++) {
602 
603                     /*
604                      * The input may have just changed (prior to
605                      * cxm_msp_wait_for_lock) so start with the
606                      * delay to give the audio decoder a chance
607                      * to update its status.
608                      */
609 
610                     tsleep(&sc->iicbus, 0, "audio", hz / 20);
611 
612                     switch (cxm_msp_is_locked(sc)) {
613                     case 1:
614                               return 1;
615 
616                     case 0:
617                               break;
618 
619                     default:
620                               return -1;
621                     }
622           }
623 
624           device_printf(sc->dev, "audio decoder failed to lock\n");
625 
626           return 0;
627 }
628