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