msp34xx.c revision 172836
1155324Simp/*-
2155324Simp * Copyright (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
3155324Simp * All rights reserved.
4155324Simp *
5155324Simp * Redistribution and use in source and binary forms, with or without
6155324Simp * modification, are permitted provided that the following conditions
7155324Simp * are met:
8155324Simp * 1. Redistributions of source code must retain the above copyright
9155324Simp *    notice, this list of conditions and the following disclaimer.
10155324Simp * 2. Redistributions in binary form must reproduce the above copyright
11155324Simp *    notice, this list of conditions and the following disclaimer in the
12155324Simp *    documentation and/or other materials provided with the distribution.
13155324Simp *
14155324Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15155324Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16155324Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17155324Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18155324Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19155324Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20155324Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21155324Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22155324Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23155324Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24155324Simp * SUCH DAMAGE.
25155324Simp *
26155324Simp * $FreeBSD: head/sys/dev/bktr/msp34xx.c 172836 2007-10-20 23:23:23Z julian $
27155324Simp */
28155324Simp
29155324Simp/*
30155324Simp * programming the msp34* sound processor family
31155324Simp *
32163522Simp * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
33155324Simp *
34155324Simp * what works and what doesn't:
35155324Simp *
36155324Simp *  AM-Mono
37155324Simp *      Support for Hauppauge cards added (decoding handled by tuner) added by
38155324Simp *      Frederic Crozat <fcrozat@mail.dotcom.fr>
39155324Simp *
40155324Simp *  FM-Mono
41155324Simp *      should work. The stereo modes are backward compatible to FM-mono,
42155324Simp *      therefore FM-Mono should be allways available.
43155324Simp *
44155324Simp *  FM-Stereo (B/G, used in germany)
45155324Simp *      should work, with autodetect
46155324Simp *
47155324Simp *  FM-Stereo (satellite)
48163522Simp *      should work, no autodetect (i.e. default is mono, but you can
49155324Simp *      switch to stereo -- untested)
50155324Simp *
51155324Simp *  NICAM (B/G, L , used in UK, Scandinavia, Spain and France)
52155324Simp *      should work, with autodetect. Support for NICAM was added by
53155324Simp *      Pekka Pietikainen <pp@netppl.fi>
54155324Simp *
55155324Simp *
56155324Simp * TODO:
57155324Simp *   - better SAT support
58155324Simp *
59155324Simp *
60155324Simp * 980623  Thomas Sailer (sailer@ife.ee.ethz.ch)
61155324Simp *         using soundcore instead of OSS
62155324Simp *
63155324Simp *
64155324Simp * The FreeBSD modifications by Alexander Langer <alex@FreeBSD.org>
65155324Simp * are in the public domain.  Please contact me (Alex) and not Gerd for
66155324Simp * any problems you encounter under FreeBSD.
67155324Simp *
68155324Simp * FreeBSD TODO:
69155324Simp * - mutex handling (currently not mp-safe)
70155324Simp * - the various options here as loader tunables or compile time or whatever
71155324Simp * - how does the new dolby flag work with the current dpl_* stuff?
72155324Simp *   Maybe it's just enough to set the dolby flag to 1 and it works.
73155324Simp *   As I don't have a dolby card myself, I can't test it, though.
74155324Simp */
75165778Sticso
76156831Simp#include "opt_bktr.h"			/* Include any kernel config options */
77155324Simp#ifdef BKTR_NEW_MSP34XX_DRIVER		/* file only needed for new driver */
78155324Simp
79155324Simp#include <sys/param.h>
80155324Simp#include <sys/systm.h>
81155324Simp#include <sys/kernel.h>
82155324Simp#include <sys/vnode.h>
83155324Simp
84155324Simp#include <sys/unistd.h>
85155324Simp#include <sys/kthread.h>
86155324Simp#include <sys/malloc.h>
87155324Simp
88155324Simp#ifdef BKTR_USE_FREEBSD_SMBUS
89155324Simp#include <sys/bus.h>			/* required by bktr_reg.h */
90157562Simp#endif
91155324Simp
92155324Simp#include <machine/bus.h>		/* required by bktr_reg.h */
93157562Simp
94157562Simp#include <dev/bktr/ioctl_meteor.h>
95155324Simp#include <dev/bktr/ioctl_bt848.h>	/* extensions to ioctl_meteor.h */
96155324Simp#include <dev/bktr/bktr_reg.h>
97155324Simp#include <dev/bktr/bktr_tuner.h>
98155324Simp#include <dev/bktr/bktr_audio.h>
99155324Simp#include <dev/bktr/bktr_core.h>
100159708Simp
101155324Simp#define VIDEO_MODE_PAL          0
102155324Simp#define VIDEO_MODE_NTSC         1
103155324Simp#define VIDEO_MODE_SECAM        2
104155324Simp#define VIDEO_MODE_AUTO         3
105155324Simp
106155324Simp#define VIDEO_SOUND_MONO	1
107155324Simp#define VIDEO_SOUND_STEREO	2
108155324Simp#define VIDEO_SOUND_LANG1	4
109155324Simp#define VIDEO_SOUND_LANG2	8
110155324Simp
111155324Simp#define DFP_COUNT 0x41
112155324Simpstatic const int bl_dfp[] = {
113155324Simp	0x00, 0x01, 0x02, 0x03,  0x06, 0x08, 0x09, 0x0a,
114155324Simp	0x0b, 0x0d, 0x0e, 0x10
115155324Simp};
116155324Simp
117155324Simpstruct msp3400c {
118155324Simp	int simple;
119155324Simp	int nicam;
120155324Simp	int mode;
121155324Simp	int norm;
122155324Simp	int stereo;
123155324Simp	int nicam_on;
124155324Simp	int acb;
125155324Simp	int main, second;	/* sound carrier */
126155324Simp	int input;
127155324Simp
128155324Simp	int muted;
129155324Simp	int left, right;	/* volume */
130155324Simp	int bass, treble;
131155324Simp
132155324Simp	/* shadow register set */
133155324Simp	int dfp_regs[DFP_COUNT];
134155324Simp
135155324Simp	/* thread */
136155324Simp	struct proc	    *kthread;
137155324Simp	char                *threaddesc;
138155324Simp
139155324Simp	int                  active,restart,rmmod;
140155324Simp
141155324Simp	int                  watch_stereo;
142155324Simp	int		     halt_thread;
143155324Simp};
144155324Simp
145155324Simp#define VIDEO_MODE_RADIO 16      /* norm magic for radio mode */
146155324Simp
147155324Simp/* ---------------------------------------------------------------------- */
148155324Simp
149155324Simp#define dprintk(...) do {						\
150155445Scognet	if (bootverbose) {						\
151155324Simp		printf("%s: ", bktr_name(client));			\
152155324Simp		printf(__VA_ARGS__);					\
153155324Simp	}								\
154155324Simp} while (0)
155155324Simp
156155324Simp/* ---------------------------------------------------------------------- */
157155324Simp
158155324Simp#define I2C_MSP3400C       0x80
159155324Simp#define I2C_MSP3400C_DEM   0x10
160155324Simp#define I2C_MSP3400C_DFP   0x12
161155324Simp
162155324Simp/* ----------------------------------------------------------------------- */
163155324Simp/* functions for talking to the MSP3400C Sound processor                   */
164155324Simp
165155324Simpstatic int msp3400c_reset(bktr_ptr_t client)
166155324Simp{
167155324Simp	/* use our own which handles config(8) options */
168155324Simp	msp_dpl_reset(client, client->msp_addr);
169155324Simp
170163522Simp        return 0;
171163522Simp}
172155324Simp
173155324Simpstatic int
174155324Simpmsp3400c_read(bktr_ptr_t client, int dev, int addr)
175155324Simp{
176155324Simp	/* use our own */
177155324Simp	return(msp_dpl_read(client, client->msp_addr, dev, addr));
178155324Simp}
179155324Simp
180159708Simpstatic int
181159708Simpmsp3400c_write(bktr_ptr_t client, int dev, int addr, int val)
182163522Simp{
183163522Simp	/* use our own */
184163522Simp	msp_dpl_write(client, client->msp_addr, dev, addr, val);
185163522Simp
186163522Simp	return(0);
187163522Simp}
188163522Simp
189155324Simp/* ------------------------------------------------------------------------ */
190155324Simp
191155324Simp/* This macro is allowed for *constants* only, gcc must calculate it
192155324Simp   at compile time.  Remember -- no floats in kernel mode */
193155324Simp#define MSP_CARRIER(freq) ((int)((float)(freq/18.432)*(1<<24)))
194155324Simp
195155445Scognet#define MSP_MODE_AM_DETECT   0
196155324Simp#define MSP_MODE_FM_RADIO    2
197155405Scognet#define MSP_MODE_FM_TERRA    3
198155324Simp#define MSP_MODE_FM_SAT      4
199155324Simp#define MSP_MODE_FM_NICAM1   5
200155324Simp#define MSP_MODE_FM_NICAM2   6
201155324Simp#define MSP_MODE_AM_NICAM    7
202155324Simp#define MSP_MODE_BTSC        8
203155324Simp#define MSP_MODE_EXTERN      9
204155324Simp
205155324Simpstatic struct MSP_INIT_DATA_DEM {
206155324Simp	int fir1[6];
207155324Simp	int fir2[6];
208165779Sticso	int cdo1;
209165779Sticso	int cdo2;
210155324Simp	int ad_cv;
211155324Simp	int mode_reg;
212155324Simp	int dfp_src;
213155324Simp	int dfp_matrix;
214155324Simp} msp_init_data[] = {
215155324Simp	/* AM (for carrier detect / msp3400) */
216155324Simp	{ { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 },
217155324Simp	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
218155324Simp	  0x00d0, 0x0500,   0x0020, 0x3000},
219155324Simp
220155324Simp	/* AM (for carrier detect / msp3410) */
221155324Simp	{ { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 },
222155324Simp	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
223155324Simp	  0x00d0, 0x0100,   0x0020, 0x3000},
224155324Simp
225155324Simp	/* FM Radio */
226155324Simp	{ { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 },
227155324Simp	  MSP_CARRIER(10.7), MSP_CARRIER(10.7),
228155324Simp	  0x00d0, 0x0480, 0x0020, 0x3000 },
229155324Simp
230155324Simp	/* Terrestial FM-mono + FM-stereo */
231155324Simp	{ {  3, 18, 27, 48, 66, 72 }, {  3, 18, 27, 48, 66, 72 },
232155324Simp	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
233155324Simp	  0x00d0, 0x0480,   0x0030, 0x3000},
234155324Simp
235155324Simp	/* Sat FM-mono */
236155324Simp	{ {  1,  9, 14, 24, 33, 37 }, {  3, 18, 27, 48, 66, 72 },
237155324Simp	  MSP_CARRIER(6.5), MSP_CARRIER(6.5),
238155324Simp	  0x00c6, 0x0480,   0x0000, 0x3000},
239155324Simp
240155324Simp	/* NICAM/FM --  B/G (5.5/5.85), D/K (6.5/5.85) */
241155324Simp	{ { -2, -8, -10, 10, 50, 86 }, {  3, 18, 27, 48, 66, 72 },
242155324Simp	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
243155324Simp	  0x00d0, 0x0040,   0x0120, 0x3000},
244155324Simp
245155324Simp	/* NICAM/FM -- I (6.0/6.552) */
246155324Simp	{ {  2, 4, -6, -4, 40, 94 }, {  3, 18, 27, 48, 66, 72 },
247155324Simp	  MSP_CARRIER(6.0), MSP_CARRIER(6.0),
248155324Simp	  0x00d0, 0x0040,   0x0120, 0x3000},
249155324Simp
250155324Simp	/* NICAM/AM -- L (6.5/5.85) */
251155324Simp	{ {  -2, -8, -10, 10, 50, 86 }, {  -4, -12, -9, 23, 79, 126 },
252155324Simp	  MSP_CARRIER(6.5), MSP_CARRIER(6.5),
253155324Simp	  0x00c6, 0x0140,   0x0120, 0x7c03},
254155324Simp};
255155324Simp
256155324Simpstruct CARRIER_DETECT {
257155324Simp	int   cdo;
258157562Simp	char *name;
259157562Simp};
260157562Simp
261157562Simpstatic struct CARRIER_DETECT carrier_detect_main[] = {
262157562Simp	/* main carrier */
263157562Simp	{ MSP_CARRIER(4.5),        "4.5   NTSC"                   },
264157562Simp	{ MSP_CARRIER(5.5),        "5.5   PAL B/G"                },
265157562Simp	{ MSP_CARRIER(6.0),        "6.0   PAL I"                  },
266157562Simp	{ MSP_CARRIER(6.5),        "6.5   PAL D/K + SAT + SECAM"  }
267157562Simp};
268157562Simp
269157562Simpstatic struct CARRIER_DETECT carrier_detect_55[] = {
270157562Simp	/* PAL B/G */
271157562Simp	{ MSP_CARRIER(5.7421875),  "5.742 PAL B/G FM-stereo"     },
272157562Simp	{ MSP_CARRIER(5.85),       "5.85  PAL B/G NICAM"         }
273163937Simp};
274157562Simp
275157562Simpstatic struct CARRIER_DETECT carrier_detect_65[] = {
276157562Simp	/* PAL SAT / SECAM */
277157562Simp	{ MSP_CARRIER(5.85),       "5.85  PAL D/K + SECAM NICAM" },
278163937Simp	{ MSP_CARRIER(6.2578125),  "6.25  PAL D/K1 FM-stereo" },
279157562Simp	{ MSP_CARRIER(6.7421875),  "6.74  PAL D/K2 FM-stereo" },
280157562Simp	{ MSP_CARRIER(7.02),       "7.02  PAL SAT FM-stereo s/b" },
281157562Simp	{ MSP_CARRIER(7.20),       "7.20  PAL SAT FM-stereo s"   },
282157562Simp	{ MSP_CARRIER(7.38),       "7.38  PAL SAT FM-stereo b"   },
283157562Simp};
284155324Simp
285155324Simp#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT))
286155324Simp
287155324Simp/* ----------------------------------------------------------------------- */
288155324Simp
289155324Simp#define SCART_MASK    0
290155324Simp#define SCART_IN1     1
291155324Simp#define SCART_IN2     2
292155324Simp#define SCART_IN1_DA  3
293155324Simp#define SCART_IN2_DA  4
294155324Simp#define SCART_IN3     5
295155324Simp#define SCART_IN4     6
296155324Simp#define SCART_MONO    7
297155324Simp#define SCART_MUTE    8
298155324Simp
299155324Simpstatic int scarts[3][9] = {
300155324Simp  /* MASK    IN1     IN2     IN1_DA  IN2_DA  IN3     IN4     MONO    MUTE   */
301155324Simp  {  0x0320, 0x0000, 0x0200, -1,     -1,     0x0300, 0x0020, 0x0100, 0x0320 },
302155324Simp  {  0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 },
303155324Simp  {  0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 },
304155324Simp};
305155324Simp
306155324Simpstatic char *scart_names[] = {
307155324Simp  "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute"
308155324Simp};
309155324Simp
310155324Simpstatic void
311155324Simpmsp3400c_set_scart(bktr_ptr_t client, int in, int out)
312155324Simp{
313155324Simp	struct msp3400c *msp = client->msp3400c_info;
314155324Simp
315155324Simp	if (-1 == scarts[out][in])
316155324Simp		return;
317155324Simp
318155324Simp	dprintk("msp34xx: scart switch: %s => %d\n",scart_names[in],out);
319155324Simp	msp->acb &= ~scarts[out][SCART_MASK];
320155324Simp	msp->acb |=  scarts[out][in];
321155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, msp->acb);
322155324Simp}
323155324Simp
324155324Simp/* ------------------------------------------------------------------------ */
325155324Simp
326155324Simpstatic void msp3400c_setcarrier(bktr_ptr_t client, int cdo1, int cdo2)
327155324Simp{
328155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff);
329155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12);
330155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff);
331155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12);
332155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/
333155324Simp}
334155324Simp
335155324Simpstatic void msp3400c_setvolume(bktr_ptr_t client,
336155324Simp			       int muted, int left, int right)
337163522Simp{
338155324Simp	int vol = 0,val = 0,balance = 0;
339155324Simp
340155324Simp	if (!muted) {
341155324Simp		vol     = (left > right) ? left : right;
342155324Simp		val     = (vol * 0x73 / 65535) << 8;
343155324Simp	}
344155324Simp	if (vol > 0) {
345155324Simp		balance = ((right-left) * 127) / vol;
346155324Simp	}
347155324Simp
348155324Simp	dprintk("msp34xx: setvolume: mute=%s %d:%d  v=0x%02x b=0x%02x\n",
349155324Simp		muted ? "on" : "off", left, right, val>>8, balance);
350155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */
351155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones  */
352155324Simp	/* scart - on/off only */
353155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0);
354155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8);
355155324Simp}
356155324Simp
357155324Simpstatic void msp3400c_setbass(bktr_ptr_t client, int bass)
358155324Simp{
359155324Simp	int val = ((bass-32768) * 0x60 / 65535) << 8;
360155324Simp
361155324Simp	dprintk("msp34xx: setbass: %d 0x%02x\n",bass, val>>8);
362155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */
363155324Simp}
364155324Simp
365155324Simpstatic void msp3400c_settreble(bktr_ptr_t client, int treble)
366155324Simp{
367155324Simp	int val = ((treble-32768) * 0x60 / 65535) << 8;
368155324Simp
369155324Simp	dprintk("msp34xx: settreble: %d 0x%02x\n",treble, val>>8);
370155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */
371155324Simp}
372155324Simp
373155324Simpstatic void msp3400c_setmode(bktr_ptr_t client, int type)
374155324Simp{
375156831Simp	struct msp3400c *msp = client->msp3400c_info;
376156831Simp	int i;
377155324Simp
378157562Simp	dprintk("msp3400: setmode: %d\n",type);
379155324Simp	msp->mode   = type;
380155324Simp	msp->stereo = VIDEO_SOUND_MONO;
381155324Simp
382155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb,          /* ad_cv */
383155324Simp		       msp_init_data[type].ad_cv);
384157562Simp
385157562Simp	for (i = 5; i >= 0; i--)                                   /* fir 1 */
386157562Simp		msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001,
387155324Simp			       msp_init_data[type].fir1[i]);
388157562Simp
389157562Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */
390157562Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040);
391155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000);
392157562Simp	for (i = 5; i >= 0; i--)
393156831Simp		msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005,
394155324Simp			       msp_init_data[type].fir2[i]);
395155324Simp
396155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083,     /* MODE_REG */
397155324Simp		       msp_init_data[type].mode_reg);
398155324Simp
399155324Simp	msp3400c_setcarrier(client, msp_init_data[type].cdo1,
400155324Simp			    msp_init_data[type].cdo2);
401155324Simp
402155324Simp	msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/
403155324Simp
404155324Simp	if (client->dolby) {
405155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,
406155324Simp			       0x0520); /* I2S1 */
407155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,
408155324Simp			       0x0620); /* I2S2 */
409155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,
410155324Simp			       msp_init_data[type].dfp_src);
411155324Simp	} else {
412155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,
413155324Simp			       msp_init_data[type].dfp_src);
414155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,
415155324Simp			       msp_init_data[type].dfp_src);
416155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,
417155324Simp			       msp_init_data[type].dfp_src);
418155324Simp	}
419155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,
420155324Simp		       msp_init_data[type].dfp_src);
421155324Simp	msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e,
422155324Simp		       msp_init_data[type].dfp_matrix);
423155324Simp
424155324Simp	if (msp->nicam) {
425155324Simp		/* nicam prescale */
426155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */
427155324Simp	}
428155324Simp}
429155324Simp
430155324Simp/* turn on/off nicam + stereo */
431155324Simpstatic void msp3400c_setstereo(bktr_ptr_t client, int mode)
432155324Simp{
433155324Simp	static char *strmode[] = { "0", "mono", "stereo", "3",
434155324Simp				   "lang1", "5", "6", "7", "lang2" };
435155324Simp	struct msp3400c *msp = client->msp3400c_info;
436155324Simp	int nicam=0; /* channel source: FM/AM or nicam */
437155324Simp	int src=0;
438155324Simp
439155324Simp	/* switch demodulator */
440155324Simp	switch (msp->mode) {
441155324Simp	case MSP_MODE_FM_TERRA:
442155324Simp		dprintk("msp3400: FM setstereo: %s\n",strmode[mode]);
443155324Simp		msp3400c_setcarrier(client,msp->second,msp->main);
444155324Simp		switch (mode) {
445155324Simp		case VIDEO_SOUND_STEREO:
446155324Simp			msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001);
447155324Simp			break;
448155324Simp		case VIDEO_SOUND_MONO:
449155324Simp		case VIDEO_SOUND_LANG1:
450155324Simp		case VIDEO_SOUND_LANG2:
451155324Simp			msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000);
452155324Simp			break;
453155324Simp		}
454155324Simp		break;
455155324Simp	case MSP_MODE_FM_SAT:
456155324Simp		dprintk("msp3400: SAT setstereo: %s\n",strmode[mode]);
457155324Simp		switch (mode) {
458155324Simp		case VIDEO_SOUND_MONO:
459155324Simp			msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5));
460155324Simp			break;
461155324Simp		case VIDEO_SOUND_STEREO:
462155324Simp			msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02));
463155324Simp			break;
464155324Simp		case VIDEO_SOUND_LANG1:
465155324Simp			msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
466155324Simp			break;
467155324Simp		case VIDEO_SOUND_LANG2:
468155324Simp			msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
469155324Simp			break;
470155324Simp		}
471155324Simp		break;
472155324Simp	case MSP_MODE_FM_NICAM1:
473155324Simp	case MSP_MODE_FM_NICAM2:
474155324Simp	case MSP_MODE_AM_NICAM:
475155324Simp		dprintk("msp3400: NICAM setstereo: %s\n",strmode[mode]);
476155324Simp		msp3400c_setcarrier(client,msp->second,msp->main);
477155324Simp		if (msp->nicam_on)
478155324Simp			nicam=0x0100;
479155324Simp		break;
480155324Simp	case MSP_MODE_BTSC:
481155324Simp		dprintk("msp3400: BTSC setstereo: %s\n",strmode[mode]);
482155324Simp		nicam=0x0300;
483155324Simp		break;
484155324Simp	case MSP_MODE_EXTERN:
485155324Simp		dprintk("msp3400: extern setstereo: %s\n",strmode[mode]);
486155324Simp		nicam = 0x0200;
487155324Simp		break;
488155324Simp	case MSP_MODE_FM_RADIO:
489155324Simp		dprintk("msp3400: FM-Radio setstereo: %s\n",strmode[mode]);
490155324Simp		break;
491155324Simp	default:
492155324Simp		dprintk("msp3400: mono setstereo\n");
493163937Simp		return;
494163937Simp	}
495163937Simp
496163937Simp	/* switch audio */
497163937Simp	switch (mode) {
498163937Simp	case VIDEO_SOUND_STEREO:
499163937Simp		src = 0x0020 | nicam;
500163937Simp#if 0
501163937Simp		/* spatial effect */
502163937Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000);
503163937Simp#endif
504163937Simp		break;
505163937Simp	case VIDEO_SOUND_MONO:
506163937Simp		if (msp->mode == MSP_MODE_AM_NICAM) {
507163937Simp			dprintk("msp3400: switching to AM mono\n");
508163937Simp			/* AM mono decoding is handled by tuner, not MSP chip */
509163937Simp			/* SCART switching control register */
510155324Simp			msp3400c_set_scart(client,SCART_MONO,0);
511155324Simp			src = 0x0200;
512155324Simp			break;
513163937Simp		}
514155324Simp	case VIDEO_SOUND_LANG1:
515155324Simp		src = 0x0000 | nicam;
516163937Simp		break;
517155324Simp	case VIDEO_SOUND_LANG2:
518155324Simp		src = 0x0010 | nicam;
519155324Simp		break;
520155324Simp	}
521155324Simp	dprintk("msp3400: setstereo final source/matrix = 0x%x\n", src);
522155324Simp
523155324Simp	if (client->dolby) {
524155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520);
525155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620);
526155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src);
527155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src);
528155324Simp	} else {
529163937Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src);
530163937Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src);
531155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src);
532155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src);
533155324Simp	}
534155324Simp}
535155324Simp
536155324Simpstatic void
537155324Simpmsp3400c_print_mode(struct msp3400c *msp)
538155324Simp{
539155324Simp	if (msp->main == msp->second) {
540155324Simp		printf("bktr: msp3400: mono sound carrier: %d.%03d MHz\n",
541163937Simp		       msp->main/910000,(msp->main/910)%1000);
542163937Simp	} else {
543155324Simp		printf("bktr: msp3400: main sound carrier: %d.%03d MHz\n",
544155324Simp		       msp->main/910000,(msp->main/910)%1000);
545155324Simp	}
546163937Simp	if (msp->mode == MSP_MODE_FM_NICAM1 ||
547163937Simp	    msp->mode == MSP_MODE_FM_NICAM2)
548163937Simp		printf("bktr: msp3400: NICAM/FM carrier   : %d.%03d MHz\n",
549163937Simp		       msp->second/910000,(msp->second/910)%1000);
550163937Simp	if (msp->mode == MSP_MODE_AM_NICAM)
551163937Simp		printf("bktr: msp3400: NICAM/AM carrier   : %d.%03d MHz\n",
552155324Simp		       msp->second/910000,(msp->second/910)%1000);
553155324Simp	if (msp->mode == MSP_MODE_FM_TERRA &&
554163937Simp	    msp->main != msp->second) {
555163937Simp		printf("bktr: msp3400: FM-stereo carrier : %d.%03d MHz\n",
556163937Simp		       msp->second/910000,(msp->second/910)%1000);
557163937Simp	}
558163937Simp}
559163937Simp
560155324Simpstatic void
561155324Simpmsp3400c_restore_dfp(bktr_ptr_t client)
562155324Simp{
563155324Simp	struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info;
564155324Simp	int i;
565155324Simp
566155324Simp	for (i = 0; i < DFP_COUNT; i++) {
567163937Simp		if (-1 == msp->dfp_regs[i])
568155324Simp			continue;
569155324Simp		msp3400c_write(client,I2C_MSP3400C_DFP, i, msp->dfp_regs[i]);
570155324Simp	}
571155324Simp}
572155324Simp
573155324Simp/* ----------------------------------------------------------------------- */
574155324Simp
575155324Simpstruct REGISTER_DUMP {
576155324Simp	int   addr;
577155324Simp	char *name;
578155445Scognet};
579155445Scognet
580155445Scognetstruct REGISTER_DUMP d1[] = {
581155445Scognet	{ 0x007e, "autodetect" },
582155445Scognet	{ 0x0023, "C_AD_BITS " },
583155445Scognet	{ 0x0038, "ADD_BITS  " },
584155445Scognet	{ 0x003e, "CIB_BITS  " },
585155445Scognet	{ 0x0057, "ERROR_RATE" },
586155445Scognet};
587155324Simp
588155324Simpstatic int
589155324Simpautodetect_stereo(bktr_ptr_t client)
590155324Simp{
591155324Simp	struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info;
592163937Simp	int val;
593163937Simp	int newstereo = msp->stereo;
594163937Simp	int newnicam  = msp->nicam_on;
595155324Simp	int update = 0;
596155324Simp
597155324Simp	switch (msp->mode) {
598155324Simp	case MSP_MODE_FM_TERRA:
599155324Simp		val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18);
600155324Simp		if (val > 32767)
601155324Simp			val -= 65536;
602155324Simp		dprintk("msp34xx: stereo detect register: %d\n",val);
603155324Simp
604155324Simp		if (val > 4096) {
605155324Simp			newstereo = VIDEO_SOUND_STEREO | VIDEO_SOUND_MONO;
606155324Simp		} else if (val < -4096) {
607155324Simp			newstereo = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
608155324Simp		} else {
609155324Simp			newstereo = VIDEO_SOUND_MONO;
610163937Simp		}
611155324Simp		newnicam = 0;
612155324Simp		break;
613157562Simp	case MSP_MODE_FM_NICAM1:
614157562Simp	case MSP_MODE_FM_NICAM2:
615157562Simp	case MSP_MODE_AM_NICAM:
616156831Simp		val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23);
617155324Simp		dprintk("msp34xx: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1);
618155324Simp
619155324Simp		if (val & 1) {
620155324Simp			/* nicam synced */
621155324Simp			switch ((val & 0x1e) >> 1)  {
622155324Simp			case 0:
623157562Simp			case 8:
624157562Simp				newstereo = VIDEO_SOUND_STEREO;
625157562Simp				break;
626157562Simp			case 1:
627156831Simp			case 9:
628156831Simp				newstereo = VIDEO_SOUND_MONO
629156831Simp					| VIDEO_SOUND_LANG1;
630163937Simp				break;
631163937Simp			case 2:
632157562Simp			case 10:
633156831Simp				newstereo = VIDEO_SOUND_MONO
634163937Simp					| VIDEO_SOUND_LANG1
635156831Simp					| VIDEO_SOUND_LANG2;
636155324Simp				break;
637156831Simp			default:
638156831Simp				newstereo = VIDEO_SOUND_MONO;
639156831Simp				break;
640163937Simp			}
641163937Simp			newnicam=1;
642156831Simp		} else {
643156831Simp			newnicam = 0;
644156831Simp			newstereo = VIDEO_SOUND_MONO;
645156831Simp		}
646156831Simp		break;
647157562Simp	case MSP_MODE_BTSC:
648157562Simp		val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200);
649163937Simp		dprintk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n",
650163937Simp			val,
651163937Simp			(val & 0x0002) ? "no"     : "yes",
652157562Simp			(val & 0x0004) ? "no"     : "yes",
653157562Simp			(val & 0x0040) ? "stereo" : "mono",
654163937Simp			(val & 0x0080) ? ", nicam 2nd mono" : "",
655156831Simp			(val & 0x0100) ? ", bilingual/SAP"  : "");
656156831Simp		newstereo = VIDEO_SOUND_MONO;
657163937Simp		if (val & 0x0040) newstereo |= VIDEO_SOUND_STEREO;
658163937Simp		if (val & 0x0100) newstereo |= VIDEO_SOUND_LANG1;
659163937Simp		break;
660163937Simp	}
661163937Simp	if (newstereo != msp->stereo) {
662155324Simp		update = 1;
663155324Simp		dprintk("msp34xx: watch: stereo %d => %d\n",
664155324Simp			msp->stereo,newstereo);
665156831Simp		msp->stereo   = newstereo;
666163937Simp	}
667156831Simp	if (newnicam != msp->nicam_on) {
668163937Simp		update = 1;
669163937Simp		dprintk("msp34xx: watch: nicam %d => %d\n",
670155324Simp			msp->nicam_on,newnicam);
671163937Simp		msp->nicam_on = newnicam;
672156831Simp	}
673156831Simp	return update;
674155324Simp}
675155324Simp
676163937Simp/*
677163937Simp * A kernel thread for msp3400 control -- we don't want to block the
678155324Simp * in the ioctl while doing the sound carrier & stereo detect
679163937Simp */
680155324Simp
681155324Simp/* stereo/multilang monitoring */
682155324Simpstatic void watch_stereo(bktr_ptr_t client)
683155324Simp{
684155324Simp	struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info;
685155324Simp
686155324Simp	if (autodetect_stereo(client)) {
687155324Simp		if (msp->stereo & VIDEO_SOUND_STEREO)
688155324Simp			msp3400c_setstereo(client,VIDEO_SOUND_STEREO);
689155324Simp		else if (msp->stereo & VIDEO_SOUND_LANG1)
690155324Simp			msp3400c_setstereo(client,VIDEO_SOUND_LANG1);
691156831Simp		else
692156831Simp			msp3400c_setstereo(client,VIDEO_SOUND_MONO);
693156831Simp	}
694156831Simp	if (client->stereo_once)
695156831Simp		msp->watch_stereo = 0;
696156831Simp
697156831Simp}
698155324Simp
699155324Simpstatic void msp3400c_thread(void *data)
700156831Simp{
701155324Simp	bktr_ptr_t client = data;
702155324Simp	struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info;
703155324Simp
704155324Simp	struct CARRIER_DETECT *cd;
705155324Simp	int count, max1,max2,val1,val2, val,this;
706155324Simp
707155324Simp	dprintk("msp3400: thread started\n");
708155324Simp
709155324Simp	mtx_lock(&Giant);
710155324Simp	for (;;) {
711155324Simp		if (msp->rmmod)
712155324Simp			goto done;
713155324Simp		tsleep(msp->kthread, PRIBIO, "idle", 0);
714155324Simp		if (msp->rmmod)
715163937Simp			goto done;
716155324Simp		if (msp->halt_thread) {
717155324Simp			msp->watch_stereo = 0;
718155324Simp			msp->halt_thread = 0;
719155324Simp			continue;
720155324Simp		}
721155324Simp
722155324Simp		if (VIDEO_MODE_RADIO == msp->norm ||
723155324Simp		    MSP_MODE_EXTERN  == msp->mode)
724155324Simp			continue;  /* nothing to do */
725155324Simp
726155324Simp		msp->active = 1;
727155324Simp
728155324Simp		if (msp->watch_stereo) {
729155324Simp			watch_stereo(client);
730155324Simp			msp->active = 0;
731155324Simp			continue;
732155324Simp		}
733155324Simp
734159708Simp		/* some time for the tuner to sync */
735159708Simp		tsleep(msp->kthread, PRIBIO, "tuner sync", hz/2);
736159708Simp
737159708Simp	restart:
738159708Simp		if (VIDEO_MODE_RADIO == msp->norm ||
739155324Simp		    MSP_MODE_EXTERN  == msp->mode)
740155324Simp			continue;  /* nothing to do */
741155324Simp		msp->restart = 0;
742155324Simp		msp3400c_setvolume(client, msp->muted, 0, 0);
743155324Simp		msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ );
744155324Simp		val1 = val2 = 0;
745155324Simp		max1 = max2 = -1;
746155324Simp		msp->watch_stereo = 0;
747156831Simp
748155324Simp		/* carrier detect pass #1 -- main carrier */
749155324Simp		cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main);
750155324Simp
751155324Simp		if (client->amsound && (msp->norm == VIDEO_MODE_SECAM)) {
752155324Simp			/* autodetect doesn't work well with AM ... */
753155324Simp			max1 = 3;
754155324Simp			count = 0;
755155324Simp			dprintk("msp3400: AM sound override\n");
756155324Simp		}
757165779Sticso
758165779Sticso		for (this = 0; this < count; this++) {
759165779Sticso			msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo);
760155324Simp
761155324Simp			tsleep(msp->kthread, PRIBIO, "carrier detect", hz/100);
762155324Simp
763155324Simp			if (msp->restart)
764155324Simp				msp->restart = 0;
765155324Simp
766163937Simp			val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b);
767163937Simp			if (val > 32767)
768163937Simp				val -= 65536;
769163937Simp			if (val1 < val)
770155324Simp				val1 = val, max1 = this;
771155324Simp			dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name);
772155324Simp		}
773155324Simp
774155324Simp		/* carrier detect pass #2 -- second (stereo) carrier */
775155324Simp		switch (max1) {
776155324Simp		case 1: /* 5.5 */
777155324Simp			cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55);
778155324Simp			break;
779155324Simp		case 3: /* 6.5 */
780155324Simp			cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65);
781155324Simp			break;
782155324Simp		case 0: /* 4.5 */
783155324Simp		case 2: /* 6.0 */
784163937Simp		default:
785155324Simp			cd = NULL; count = 0;
786155324Simp			break;
787155324Simp		}
788155324Simp
789155324Simp		if (client->amsound && (msp->norm == VIDEO_MODE_SECAM)) {
790156831Simp			/* autodetect doesn't work well with AM ... */
791156831Simp			cd = NULL; count = 0; max2 = 0;
792156831Simp		}
793156831Simp		for (this = 0; this < count; this++) {
794156831Simp			msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo);
795156831Simp
796156831Simp			tsleep(msp->kthread, PRIBIO, "carrier detection", hz/100);
797156831Simp			if (msp->restart)
798156831Simp				goto restart;
799156831Simp
800156831Simp			val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b);
801156831Simp			if (val > 32767)
802156831Simp				val -= 65536;
803156831Simp			if (val2 < val)
804156831Simp				val2 = val, max2 = this;
805156831Simp			dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name);
806163937Simp		}
807163937Simp
808163937Simp		/* programm the msp3400 according to the results */
809163937Simp		msp->main   = carrier_detect_main[max1].cdo;
810163937Simp		switch (max1) {
811163937Simp		case 1: /* 5.5 */
812163937Simp			if (max2 == 0) {
813163937Simp				/* B/G FM-stereo */
814163937Simp				msp->second = carrier_detect_55[max2].cdo;
815163937Simp				msp3400c_setmode(client, MSP_MODE_FM_TERRA);
816163937Simp				msp->nicam_on = 0;
817156831Simp				/* XXX why mono? this probably can do stereo... - Alex*/
818163937Simp				msp3400c_setstereo(client, VIDEO_SOUND_MONO);
819156831Simp				msp->watch_stereo = 1;
820156831Simp			} else if (max2 == 1 && msp->nicam) {
821156831Simp				/* B/G NICAM */
822156831Simp				msp->second = carrier_detect_55[max2].cdo;
823156831Simp				msp3400c_setmode(client, MSP_MODE_FM_NICAM1);
824155324Simp				msp->nicam_on = 1;
825156831Simp				msp3400c_setcarrier(client, msp->second, msp->main);
826156831Simp				msp->watch_stereo = 1;
827156831Simp			} else {
828156831Simp				goto no_second;
829156831Simp			}
830155324Simp			break;
831156831Simp		case 2: /* 6.0 */
832156831Simp			/* PAL I NICAM */
833156831Simp			msp->second = MSP_CARRIER(6.552);
834156831Simp			msp3400c_setmode(client, MSP_MODE_FM_NICAM2);
835155324Simp			msp->nicam_on = 1;
836156831Simp			msp3400c_setcarrier(client, msp->second, msp->main);
837156831Simp			msp->watch_stereo = 1;
838156831Simp			break;
839155324Simp		case 3: /* 6.5 */
840155324Simp			if (max2 == 1 || max2 == 2) {
841155324Simp				/* D/K FM-stereo */
842155324Simp				msp->second = carrier_detect_65[max2].cdo;
843155324Simp				msp3400c_setmode(client, MSP_MODE_FM_TERRA);
844155324Simp				msp->nicam_on = 0;
845155324Simp				msp3400c_setstereo(client, VIDEO_SOUND_MONO);
846155324Simp				msp->watch_stereo = 1;
847155324Simp			} else if (max2 == 0 &&
848155324Simp				   msp->norm == VIDEO_MODE_SECAM) {
849155324Simp				/* L NICAM or AM-mono */
850155324Simp				msp->second = carrier_detect_65[max2].cdo;
851155324Simp				msp3400c_setmode(client, MSP_MODE_AM_NICAM);
852155324Simp				msp->nicam_on = 0;
853155324Simp				msp3400c_setstereo(client, VIDEO_SOUND_MONO);
854155324Simp				msp3400c_setcarrier(client, msp->second, msp->main);
855155324Simp				/* volume prescale for SCART (AM mono input) */
856155324Simp				msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900);
857155324Simp				msp->watch_stereo = 1;
858155324Simp			} else if (max2 == 0 && msp->nicam) {
859155324Simp				/* D/K NICAM */
860155324Simp				msp->second = carrier_detect_65[max2].cdo;
861155324Simp				msp3400c_setmode(client, MSP_MODE_FM_NICAM1);
862155324Simp				msp->nicam_on = 1;
863155324Simp				msp3400c_setcarrier(client, msp->second, msp->main);
864155324Simp				msp->watch_stereo = 1;
865155324Simp			} else {
866155324Simp				goto no_second;
867155324Simp			}
868155324Simp			break;
869155324Simp		case 0: /* 4.5 */
870155324Simp		default:
871155324Simp		no_second:
872155324Simp			msp->second = carrier_detect_main[max1].cdo;
873155324Simp			msp3400c_setmode(client, MSP_MODE_FM_TERRA);
874155324Simp			msp->nicam_on = 0;
875155324Simp			msp3400c_setcarrier(client, msp->second, msp->main);
876155324Simp			msp->stereo = VIDEO_SOUND_MONO;
877155324Simp			msp3400c_setstereo(client, VIDEO_SOUND_MONO);
878155405Scognet			break;
879155324Simp		}
880155324Simp
881155324Simp		if (msp->watch_stereo)
882155324Simp			watch_stereo(client);
883155324Simp
884155324Simp		/* unmute + restore dfp registers */
885155324Simp		msp3400c_setvolume(client, msp->muted, msp->left, msp->right);
886155324Simp		msp3400c_restore_dfp(client);
887155324Simp
888155324Simp		if (bootverbose)
889155324Simp			msp3400c_print_mode(msp);
890155324Simp
891155324Simp		msp->active = 0;
892155324Simp	}
893155324Simp
894155324Simpdone:
895155324Simp	dprintk("msp3400: thread: exit\n");
896155324Simp	msp->active = 0;
897155324Simp
898155324Simp	msp->kthread = NULL;
899155324Simp	wakeup(&msp->kthread);
900155324Simp	mtx_unlock(&Giant);
901155324Simp
902155324Simp	kproc_exit(0);
903155324Simp}
904155324Simp
905155324Simp/* ----------------------------------------------------------------------- */
906155324Simp/* this one uses the automatic sound standard detection of newer           */
907155324Simp/* msp34xx chip versions                                                   */
908155324Simp
909155324Simpstatic struct MODES {
910155324Simp	int retval;
911155324Simp	int main, second;
912155324Simp	char *name;
913155324Simp} modelist[] = {
914155324Simp	{ 0x0000, 0, 0, "ERROR" },
915155324Simp	{ 0x0001, 0, 0, "autodetect start" },
916155324Simp	{ 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72  M Dual FM-Stereo" },
917155324Simp	{ 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74  B/G Dual FM-Stereo" },
918155324Simp	{ 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25  D/K1 Dual FM-Stereo" },
919155324Simp	{ 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74  D/K2 Dual FM-Stereo" },
920157562Simp	{ 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5  D/K FM-Mono (HDEV3)" },
921157562Simp	{ 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85  B/G NICAM FM" },
922165779Sticso	{ 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85  L NICAM AM" },
923155324Simp	{ 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55  I NICAM FM" },
924155324Simp	{ 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85  D/K NICAM FM" },
925155324Simp	{ 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85  D/K NICAM FM (HDEV2)" },
926155324Simp	{ 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5  M BTSC-Stereo" },
927155324Simp	{ 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5  M BTSC-Mono + SAP" },
928155324Simp	{ 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5  M EIA-J Japan Stereo" },
929155324Simp	{ 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7  FM-Stereo Radio" },
930155324Simp	{ 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5  SAT-Mono" },
931155324Simp	{ 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20  SAT-Stereo" },
932155324Simp	{ 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2  SAT ADR" },
933155324Simp	{     -1, 0, 0, NULL }, /* EOF */
934155324Simp};
935155324Simp
936155324Simpstatic void msp3410d_thread(void *data)
937155324Simp{
938155324Simp	bktr_ptr_t client = data;
939155324Simp	struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info;
940155324Simp	int mode,val,i,std;
941157562Simp	int timo = 0;
942155324Simp
943157562Simp	dprintk("msp3410: thread started\n");
944155324Simp
945155324Simp	mtx_lock(&Giant);
946155324Simp	for (;;) {
947157562Simp		if (msp->rmmod)
948157562Simp			goto done;
949157562Simp		if (!msp->watch_stereo)
950157562Simp			timo = 0;
951157562Simp		else
952165779Sticso			timo = 10*hz;
953165779Sticso		tsleep(msp->kthread, PRIBIO, "idle", timo);
954165779Sticso		if (msp->rmmod)
955165779Sticso			goto done;
956165779Sticso		if (msp->halt_thread) {
957165779Sticso			msp->watch_stereo = 0;
958165779Sticso			msp->halt_thread = 0;
959165779Sticso			dprintk("msp3410: thread halted\n");
960165779Sticso			continue;
961165779Sticso		}
962165779Sticso
963165779Sticso		if (msp->mode == MSP_MODE_EXTERN)
964165779Sticso			continue;
965155324Simp
966155324Simp		msp->active = 1;
967155324Simp
968155324Simp		if (msp->watch_stereo) {
969155324Simp			watch_stereo(client);
970155324Simp			msp->active = 0;
971155324Simp			continue;
972155324Simp		}
973155324Simp
974155324Simp		/* some time for the tuner to sync */
975155324Simp		tsleep(msp->kthread, PRIBIO, "tuner sync", hz/2);
976155324Simp
977155324Simp	restart:
978155324Simp		if (msp->mode == MSP_MODE_EXTERN)
979155324Simp			continue;
980155324Simp		msp->restart = 0;
981155324Simp		msp->watch_stereo = 0;
982155324Simp
983155324Simp		/* put into sane state (and mute) */
984155324Simp		msp3400c_reset(client);
985155324Simp
986155324Simp		/* start autodetect */
987155324Simp		switch (msp->norm) {
988155324Simp		case VIDEO_MODE_PAL:
989155324Simp			mode = 0x1003;
990155324Simp			std  = 1;
991155324Simp			break;
992155324Simp		case VIDEO_MODE_NTSC:  /* BTSC */
993155324Simp			mode = 0x2003;
994155324Simp			std  = 0x0020;
995155324Simp			break;
996155324Simp		case VIDEO_MODE_SECAM:
997155324Simp			mode = 0x0003;
998155324Simp			std  = 1;
999155324Simp			break;
1000155324Simp		case VIDEO_MODE_RADIO:
1001155324Simp			mode = 0x0003;
1002155324Simp			std  = 0x0040;
1003155324Simp			break;
1004155324Simp		default:
1005155324Simp			mode = 0x0003;
1006155324Simp			std  = 1;
1007155324Simp			break;
1008155324Simp		}
1009155324Simp		msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode);
1010155324Simp		msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std);
1011155324Simp
1012155324Simp		if (bootverbose) {
1013155324Simp			int i;
1014155324Simp			for (i = 0; modelist[i].name != NULL; i++)
1015155324Simp				if (modelist[i].retval == std)
1016155324Simp					break;
1017155324Simp			dprintk("msp3410: setting mode: %s (0x%04x)\n",
1018155324Simp			       modelist[i].name ? modelist[i].name : "unknown",std);
1019155324Simp		}
1020155324Simp
1021155324Simp		if (std != 1) {
1022155324Simp			/* programmed some specific mode */
1023155324Simp			val = std;
1024155324Simp		} else {
1025155324Simp			/* triggered autodetect */
1026155324Simp			for (;;) {
1027155324Simp				tsleep(msp->kthread, PRIBIO, "autodetection", hz/10);
1028155324Simp				if (msp->restart)
1029155324Simp					goto restart;
1030155324Simp
1031155324Simp				/* check results */
1032155324Simp				val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e);
1033155324Simp				if (val < 0x07ff)
1034155324Simp					break;
1035155324Simp				dprintk("msp3410: detection still in progress\n");
1036155324Simp			}
1037155324Simp		}
1038155324Simp		for (i = 0; modelist[i].name != NULL; i++)
1039155324Simp			if (modelist[i].retval == val)
1040155324Simp				break;
1041155324Simp		dprintk("msp3410: current mode: %s (0x%04x)\n",
1042155324Simp			modelist[i].name ? modelist[i].name : "unknown",
1043155324Simp			val);
1044155324Simp		msp->main   = modelist[i].main;
1045155324Simp		msp->second = modelist[i].second;
1046155324Simp
1047155324Simp		if (client->amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) {
1048155324Simp			/* autodetection has failed, let backup */
1049155324Simp			dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n",
1050155324Simp				modelist[8].name ? modelist[8].name : "unknown",val);
1051			val = 0x0009;
1052			msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val);
1053		}
1054
1055		/* set various prescales */
1056		msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */
1057		msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */
1058		msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */
1059
1060		/* set stereo */
1061		switch (val) {
1062		case 0x0008: /* B/G NICAM */
1063		case 0x000a: /* I NICAM */
1064			if (val == 0x0008)
1065				msp->mode = MSP_MODE_FM_NICAM1;
1066			else
1067				msp->mode = MSP_MODE_FM_NICAM2;
1068			/* just turn on stereo */
1069			msp->stereo = VIDEO_SOUND_STEREO;
1070			msp->nicam_on = 1;
1071			msp->watch_stereo = 1;
1072			msp3400c_setstereo(client,VIDEO_SOUND_STEREO);
1073			break;
1074		case 0x0009:
1075			msp->mode = MSP_MODE_AM_NICAM;
1076			msp->stereo = VIDEO_SOUND_MONO;
1077			msp->nicam_on = 1;
1078			msp3400c_setstereo(client,VIDEO_SOUND_MONO);
1079			msp->watch_stereo = 1;
1080			break;
1081		case 0x0020: /* BTSC */
1082			/* just turn on stereo */
1083			msp->mode   = MSP_MODE_BTSC;
1084			msp->stereo = VIDEO_SOUND_STEREO;
1085			msp->nicam_on = 0;
1086			msp->watch_stereo = 1;
1087			msp3400c_setstereo(client,VIDEO_SOUND_STEREO);
1088			break;
1089		case 0x0040: /* FM radio */
1090			msp->mode   = MSP_MODE_FM_RADIO;
1091			msp->stereo = VIDEO_SOUND_STEREO;
1092			msp->nicam_on = 0;
1093			msp->watch_stereo = 0;
1094			/* scart routing */
1095			msp3400c_set_scart(client,SCART_IN2,0);
1096			msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220);
1097			msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220);
1098			msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220);
1099			break;
1100		case 0x0003:
1101			msp->mode   = MSP_MODE_FM_TERRA;
1102			msp->stereo = VIDEO_SOUND_STEREO;
1103			msp->nicam_on = 0;
1104			msp->watch_stereo = 1;
1105			msp3400c_setstereo(client,VIDEO_SOUND_STEREO);
1106			break;
1107		}
1108
1109		if (msp->watch_stereo)
1110			watch_stereo(client);
1111
1112		/* unmute + restore dfp registers */
1113		msp3400c_setbass(client, msp->bass);
1114		msp3400c_settreble(client, msp->treble);
1115		msp3400c_setvolume(client, msp->muted, msp->left, msp->right);
1116		msp3400c_restore_dfp(client);
1117
1118		msp->active = 0;
1119	}
1120
1121done:
1122	dprintk("msp3410: thread: exit\n");
1123	msp->active = 0;
1124
1125	msp->kthread = NULL;
1126	wakeup(&msp->kthread);
1127	mtx_unlock(&Giant);
1128
1129	kproc_exit(0);
1130}
1131
1132int msp_attach(bktr_ptr_t bktr)
1133{
1134	struct msp3400c *msp;
1135	int              rev1,rev2,i;
1136	int		 err;
1137	char		 buf[20];
1138
1139	msp = (struct msp3400c *) malloc(sizeof(struct msp3400c), M_DEVBUF, M_NOWAIT);
1140	if (msp == NULL)
1141                return ENOMEM;
1142	bktr->msp3400c_info = msp;
1143
1144	memset(msp,0,sizeof(struct msp3400c));
1145	msp->left   = 65535;
1146	msp->right  = 65535;
1147	msp->bass   = 32768;
1148	msp->treble = 32768;
1149	msp->input  = -1;
1150	msp->threaddesc = malloc(15 * sizeof(char), M_DEVBUF, M_NOWAIT);
1151	if (msp->threaddesc == NULL) {
1152		free(msp, M_DEVBUF);
1153                return ENOMEM;
1154	}
1155	snprintf(msp->threaddesc, 14, "%s_msp34xx_thread", bktr->bktr_xname);
1156
1157	for (i = 0; i < DFP_COUNT; i++)
1158		msp->dfp_regs[i] = -1;
1159
1160	msp3400c_reset(bktr);
1161
1162	rev1 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1e);
1163	if (-1 != rev1)
1164		rev2 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1f);
1165	if ((-1 == rev1) || (0 == rev1 && 0 == rev2)) {
1166		free(msp->threaddesc, M_DEVBUF);
1167		free(msp, M_DEVBUF);
1168		bktr->msp3400c_info = NULL;
1169		printf("%s: msp3400: error while reading chip version\n", bktr_name(bktr));
1170		return ENXIO;
1171	}
1172
1173#if 0
1174	/* this will turn on a 1kHz beep - might be useful for debugging... */
1175	msp3400c_write(bktr,I2C_MSP3400C_DFP, 0x0014, 0x1040);
1176#endif
1177
1178	sprintf(buf,"MSP34%02d%c-%c%d",
1179		(rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f);
1180	msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0;
1181
1182	if (bktr->mspsimple == -1) {
1183		/* default mode */
1184		/* msp->simple = (((rev2>>8)&0xff) == 0) ? 0 : 1; */
1185		msp->simple = ((rev1&0xff)+'@' > 'C');
1186	} else {
1187		/* use kenv value */
1188		msp->simple = bktr->mspsimple;
1189	}
1190
1191	/* hello world :-) */
1192	if (bootverbose) {
1193		printf("%s: msp34xx: init: chip=%s", bktr_name(bktr), buf);
1194		if (msp->nicam)
1195			printf(", has NICAM support");
1196		printf("\n");
1197	}
1198
1199	/* startup control thread */
1200	err = kproc_create(msp->simple ? msp3410d_thread : msp3400c_thread,
1201			     bktr, &msp->kthread, (RFFDG | RFPROC), 0,
1202			     msp->threaddesc);
1203	if (err) {
1204		printf("%s: Error returned by kproc_create: %d", bktr_name(bktr), err);
1205		free(msp->threaddesc, M_DEVBUF);
1206		free(msp, M_DEVBUF);
1207		bktr->msp3400c_info = NULL;
1208		return ENXIO;
1209	}
1210	wakeup(msp->kthread);
1211
1212	/* done */
1213	return 0;
1214}
1215
1216int msp_detach(bktr_ptr_t client)
1217{
1218	struct msp3400c *msp  = (struct msp3400c*)client->msp3400c_info;
1219
1220	/* shutdown control thread */
1221	if (msp->kthread)
1222	{
1223		/* XXX mutex lock required */
1224		mtx_lock(&Giant);
1225		msp->rmmod = 1;
1226		msp->watch_stereo = 0;
1227		wakeup(msp->kthread);
1228
1229		while (msp->kthread)
1230			tsleep(&msp->kthread, PRIBIO, "wait for kthread", hz/10);
1231		mtx_unlock(&Giant);
1232	}
1233
1234	if (client->msp3400c_info != NULL) {
1235		free(client->msp3400c_info, M_DEVBUF);
1236		client->msp3400c_info = NULL;
1237	}
1238
1239    	msp3400c_reset(client);
1240
1241	return 0;
1242}
1243
1244void msp_wake_thread(bktr_ptr_t client)
1245{
1246	struct msp3400c *msp  = (struct msp3400c*)client->msp3400c_info;
1247
1248	msp3400c_setvolume(client,msp->muted,0,0);
1249	msp->watch_stereo=0;
1250	if (msp->active)
1251		msp->restart = 1;
1252	wakeup(msp->kthread);
1253}
1254
1255void msp_halt_thread(bktr_ptr_t client)
1256{
1257	struct msp3400c *msp  = (struct msp3400c*)client->msp3400c_info;
1258
1259	msp3400c_setvolume(client,msp->muted,0,0);
1260	if (msp->active)
1261		msp->restart = 1;
1262	msp->halt_thread = 1;
1263	wakeup(msp->kthread);
1264}
1265#endif /* BKTR_NEW_MSP34XX_DRIVER */
1266