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