1 |
62 |
marcus.erl |
/*
|
2 |
|
|
* Programming the mspx4xx sound processor family
|
3 |
|
|
*
|
4 |
|
|
* (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
|
5 |
|
|
*
|
6 |
|
|
* what works and what doesn't:
|
7 |
|
|
*
|
8 |
|
|
* AM-Mono
|
9 |
|
|
* Support for Hauppauge cards added (decoding handled by tuner) added by
|
10 |
|
|
* Frederic Crozat <fcrozat@mail.dotcom.fr>
|
11 |
|
|
*
|
12 |
|
|
* FM-Mono
|
13 |
|
|
* should work. The stereo modes are backward compatible to FM-mono,
|
14 |
|
|
* therefore FM-Mono should be allways available.
|
15 |
|
|
*
|
16 |
|
|
* FM-Stereo (B/G, used in germany)
|
17 |
|
|
* should work, with autodetect
|
18 |
|
|
*
|
19 |
|
|
* FM-Stereo (satellite)
|
20 |
|
|
* should work, no autodetect (i.e. default is mono, but you can
|
21 |
|
|
* switch to stereo -- untested)
|
22 |
|
|
*
|
23 |
|
|
* NICAM (B/G, L , used in UK, Scandinavia, Spain and France)
|
24 |
|
|
* should work, with autodetect. Support for NICAM was added by
|
25 |
|
|
* Pekka Pietikainen <pp@netppl.fi>
|
26 |
|
|
*
|
27 |
|
|
* TODO:
|
28 |
|
|
* - better SAT support
|
29 |
|
|
*
|
30 |
|
|
* 980623 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
31 |
|
|
* using soundcore instead of OSS
|
32 |
|
|
*
|
33 |
|
|
* This program is free software; you can redistribute it and/or
|
34 |
|
|
* modify it under the terms of the GNU General Public License
|
35 |
|
|
* as published by the Free Software Foundation; either version 2
|
36 |
|
|
* of the License, or (at your option) any later version.
|
37 |
|
|
*
|
38 |
|
|
* This program is distributed in the hope that it will be useful,
|
39 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
40 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
41 |
|
|
* GNU General Public License for more details.
|
42 |
|
|
*
|
43 |
|
|
* You should have received a copy of the GNU General Public License
|
44 |
|
|
* along with this program; if not, write to the Free Software
|
45 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
46 |
|
|
*/
|
47 |
|
|
|
48 |
|
|
|
49 |
|
|
#include <linux/kernel.h>
|
50 |
|
|
#include <linux/module.h>
|
51 |
|
|
#include <linux/slab.h>
|
52 |
|
|
#include <linux/i2c.h>
|
53 |
|
|
#include <linux/videodev.h>
|
54 |
|
|
#include <linux/videodev2.h>
|
55 |
|
|
#include <media/v4l2-common.h>
|
56 |
|
|
#include <media/tvaudio.h>
|
57 |
|
|
#include <media/msp3400.h>
|
58 |
|
|
#include <linux/kthread.h>
|
59 |
|
|
#include <linux/freezer.h>
|
60 |
|
|
#include "msp3400-driver.h"
|
61 |
|
|
|
62 |
|
|
/* ---------------------------------------------------------------------- */
|
63 |
|
|
|
64 |
|
|
MODULE_DESCRIPTION("device driver for msp34xx TV sound processor");
|
65 |
|
|
MODULE_AUTHOR("Gerd Knorr");
|
66 |
|
|
MODULE_LICENSE("GPL");
|
67 |
|
|
|
68 |
|
|
/* module parameters */
|
69 |
|
|
static int opmode = OPMODE_AUTO;
|
70 |
|
|
int msp_debug; /* msp_debug output */
|
71 |
|
|
int msp_once; /* no continous stereo monitoring */
|
72 |
|
|
int msp_amsound; /* hard-wire AM sound at 6.5 Hz (france),
|
73 |
|
|
the autoscan seems work well only with FM... */
|
74 |
|
|
int msp_standard = 1; /* Override auto detect of audio msp_standard, if needed. */
|
75 |
|
|
int msp_dolby;
|
76 |
|
|
|
77 |
|
|
int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual
|
78 |
|
|
(msp34xxg only) 0x00a0-0x03c0 */
|
79 |
|
|
|
80 |
|
|
/* read-only */
|
81 |
|
|
module_param(opmode, int, 0444);
|
82 |
|
|
|
83 |
|
|
/* read-write */
|
84 |
|
|
module_param_named(once,msp_once, bool, 0644);
|
85 |
|
|
module_param_named(debug,msp_debug, int, 0644);
|
86 |
|
|
module_param_named(stereo_threshold,msp_stereo_thresh, int, 0644);
|
87 |
|
|
module_param_named(standard,msp_standard, int, 0644);
|
88 |
|
|
module_param_named(amsound,msp_amsound, bool, 0644);
|
89 |
|
|
module_param_named(dolby,msp_dolby, bool, 0644);
|
90 |
|
|
|
91 |
|
|
MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect");
|
92 |
|
|
MODULE_PARM_DESC(once, "No continuous stereo monitoring");
|
93 |
|
|
MODULE_PARM_DESC(debug, "Enable debug messages [0-3]");
|
94 |
|
|
MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo");
|
95 |
|
|
MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect");
|
96 |
|
|
MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan");
|
97 |
|
|
MODULE_PARM_DESC(dolby, "Activates Dolby processsing");
|
98 |
|
|
|
99 |
|
|
/* ---------------------------------------------------------------------- */
|
100 |
|
|
|
101 |
|
|
/* control subaddress */
|
102 |
|
|
#define I2C_MSP_CONTROL 0x00
|
103 |
|
|
/* demodulator unit subaddress */
|
104 |
|
|
#define I2C_MSP_DEM 0x10
|
105 |
|
|
/* DSP unit subaddress */
|
106 |
|
|
#define I2C_MSP_DSP 0x12
|
107 |
|
|
|
108 |
|
|
/* Addresses to scan */
|
109 |
|
|
static unsigned short normal_i2c[] = { 0x80 >> 1, 0x88 >> 1, I2C_CLIENT_END };
|
110 |
|
|
I2C_CLIENT_INSMOD;
|
111 |
|
|
|
112 |
|
|
/* ----------------------------------------------------------------------- */
|
113 |
|
|
/* functions for talking to the MSP3400C Sound processor */
|
114 |
|
|
|
115 |
|
|
int msp_reset(struct i2c_client *client)
|
116 |
|
|
{
|
117 |
|
|
/* reset and read revision code */
|
118 |
|
|
static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 };
|
119 |
|
|
static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 };
|
120 |
|
|
static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e };
|
121 |
|
|
u8 read[2];
|
122 |
|
|
struct i2c_msg reset[2] = {
|
123 |
|
|
{ client->addr, I2C_M_IGNORE_NAK, 3, reset_off },
|
124 |
|
|
{ client->addr, I2C_M_IGNORE_NAK, 3, reset_on },
|
125 |
|
|
};
|
126 |
|
|
struct i2c_msg test[2] = {
|
127 |
|
|
{ client->addr, 0, 3, write },
|
128 |
|
|
{ client->addr, I2C_M_RD, 2, read },
|
129 |
|
|
};
|
130 |
|
|
|
131 |
|
|
v4l_dbg(3, msp_debug, client, "msp_reset\n");
|
132 |
|
|
if (i2c_transfer(client->adapter, &reset[0], 1) != 1 ||
|
133 |
|
|
i2c_transfer(client->adapter, &reset[1], 1) != 1 ||
|
134 |
|
|
i2c_transfer(client->adapter, test, 2) != 2) {
|
135 |
|
|
v4l_err(client, "chip reset failed\n");
|
136 |
|
|
return -1;
|
137 |
|
|
}
|
138 |
|
|
return 0;
|
139 |
|
|
}
|
140 |
|
|
|
141 |
|
|
static int msp_read(struct i2c_client *client, int dev, int addr)
|
142 |
|
|
{
|
143 |
|
|
int err, retval;
|
144 |
|
|
u8 write[3];
|
145 |
|
|
u8 read[2];
|
146 |
|
|
struct i2c_msg msgs[2] = {
|
147 |
|
|
{ client->addr, 0, 3, write },
|
148 |
|
|
{ client->addr, I2C_M_RD, 2, read }
|
149 |
|
|
};
|
150 |
|
|
|
151 |
|
|
write[0] = dev + 1;
|
152 |
|
|
write[1] = addr >> 8;
|
153 |
|
|
write[2] = addr & 0xff;
|
154 |
|
|
|
155 |
|
|
for (err = 0; err < 3; err++) {
|
156 |
|
|
if (i2c_transfer(client->adapter, msgs, 2) == 2)
|
157 |
|
|
break;
|
158 |
|
|
v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err,
|
159 |
|
|
dev, addr);
|
160 |
|
|
schedule_timeout_interruptible(msecs_to_jiffies(10));
|
161 |
|
|
}
|
162 |
|
|
if (err == 3) {
|
163 |
|
|
v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n");
|
164 |
|
|
msp_reset(client);
|
165 |
|
|
return -1;
|
166 |
|
|
}
|
167 |
|
|
retval = read[0] << 8 | read[1];
|
168 |
|
|
v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", dev, addr, retval);
|
169 |
|
|
return retval;
|
170 |
|
|
}
|
171 |
|
|
|
172 |
|
|
int msp_read_dem(struct i2c_client *client, int addr)
|
173 |
|
|
{
|
174 |
|
|
return msp_read(client, I2C_MSP_DEM, addr);
|
175 |
|
|
}
|
176 |
|
|
|
177 |
|
|
int msp_read_dsp(struct i2c_client *client, int addr)
|
178 |
|
|
{
|
179 |
|
|
return msp_read(client, I2C_MSP_DSP, addr);
|
180 |
|
|
}
|
181 |
|
|
|
182 |
|
|
static int msp_write(struct i2c_client *client, int dev, int addr, int val)
|
183 |
|
|
{
|
184 |
|
|
int err;
|
185 |
|
|
u8 buffer[5];
|
186 |
|
|
|
187 |
|
|
buffer[0] = dev;
|
188 |
|
|
buffer[1] = addr >> 8;
|
189 |
|
|
buffer[2] = addr & 0xff;
|
190 |
|
|
buffer[3] = val >> 8;
|
191 |
|
|
buffer[4] = val & 0xff;
|
192 |
|
|
|
193 |
|
|
v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", dev, addr, val);
|
194 |
|
|
for (err = 0; err < 3; err++) {
|
195 |
|
|
if (i2c_master_send(client, buffer, 5) == 5)
|
196 |
|
|
break;
|
197 |
|
|
v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err,
|
198 |
|
|
dev, addr);
|
199 |
|
|
schedule_timeout_interruptible(msecs_to_jiffies(10));
|
200 |
|
|
}
|
201 |
|
|
if (err == 3) {
|
202 |
|
|
v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n");
|
203 |
|
|
msp_reset(client);
|
204 |
|
|
return -1;
|
205 |
|
|
}
|
206 |
|
|
return 0;
|
207 |
|
|
}
|
208 |
|
|
|
209 |
|
|
int msp_write_dem(struct i2c_client *client, int addr, int val)
|
210 |
|
|
{
|
211 |
|
|
return msp_write(client, I2C_MSP_DEM, addr, val);
|
212 |
|
|
}
|
213 |
|
|
|
214 |
|
|
int msp_write_dsp(struct i2c_client *client, int addr, int val)
|
215 |
|
|
{
|
216 |
|
|
return msp_write(client, I2C_MSP_DSP, addr, val);
|
217 |
|
|
}
|
218 |
|
|
|
219 |
|
|
/* ----------------------------------------------------------------------- *
|
220 |
|
|
* bits 9 8 5 - SCART DSP input Select:
|
221 |
|
|
* 0 0 0 - SCART 1 to DSP input (reset position)
|
222 |
|
|
* 0 1 0 - MONO to DSP input
|
223 |
|
|
* 1 0 0 - SCART 2 to DSP input
|
224 |
|
|
* 1 1 1 - Mute DSP input
|
225 |
|
|
*
|
226 |
|
|
* bits 11 10 6 - SCART 1 Output Select:
|
227 |
|
|
* 0 0 0 - undefined (reset position)
|
228 |
|
|
* 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS)
|
229 |
|
|
* 1 0 0 - MONO input to SCART 1 Output
|
230 |
|
|
* 1 1 0 - SCART 1 DA to SCART 1 Output
|
231 |
|
|
* 0 0 1 - SCART 2 DA to SCART 1 Output
|
232 |
|
|
* 0 1 1 - SCART 1 Input to SCART 1 Output
|
233 |
|
|
* 1 1 1 - Mute SCART 1 Output
|
234 |
|
|
*
|
235 |
|
|
* bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART):
|
236 |
|
|
* 0 0 0 - SCART 1 DA to SCART 2 Output (reset position)
|
237 |
|
|
* 0 1 0 - SCART 1 Input to SCART 2 Output
|
238 |
|
|
* 1 0 0 - MONO input to SCART 2 Output
|
239 |
|
|
* 0 0 1 - SCART 2 DA to SCART 2 Output
|
240 |
|
|
* 0 1 1 - SCART 2 Input to SCART 2 Output
|
241 |
|
|
* 1 1 0 - Mute SCART 2 Output
|
242 |
|
|
*
|
243 |
|
|
* Bits 4 to 0 should be zero.
|
244 |
|
|
* ----------------------------------------------------------------------- */
|
245 |
|
|
|
246 |
|
|
static int scarts[3][9] = {
|
247 |
|
|
/* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */
|
248 |
|
|
/* SCART DSP Input select */
|
249 |
|
|
{ 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 },
|
250 |
|
|
/* SCART1 Output select */
|
251 |
|
|
{ 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 },
|
252 |
|
|
/* SCART2 Output select */
|
253 |
|
|
{ 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 },
|
254 |
|
|
};
|
255 |
|
|
|
256 |
|
|
static char *scart_names[] = {
|
257 |
|
|
"in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute"
|
258 |
|
|
};
|
259 |
|
|
|
260 |
|
|
void msp_set_scart(struct i2c_client *client, int in, int out)
|
261 |
|
|
{
|
262 |
|
|
struct msp_state *state = i2c_get_clientdata(client);
|
263 |
|
|
|
264 |
|
|
state->in_scart = in;
|
265 |
|
|
|
266 |
|
|
if (in >= 0 && in <= 7 && out >= 0 && out <= 2) {
|
267 |
|
|
if (-1 == scarts[out][in + 1])
|
268 |
|
|
return;
|
269 |
|
|
|
270 |
|
|
state->acb &= ~scarts[out][0];
|
271 |
|
|
state->acb |= scarts[out][in + 1];
|
272 |
|
|
} else
|
273 |
|
|
state->acb = 0xf60; /* Mute Input and SCART 1 Output */
|
274 |
|
|
|
275 |
|
|
v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n",
|
276 |
|
|
scart_names[in], out, state->acb);
|
277 |
|
|
msp_write_dsp(client, 0x13, state->acb);
|
278 |
|
|
|
279 |
|
|
/* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */
|
280 |
|
|
if (state->has_i2s_conf)
|
281 |
|
|
msp_write_dem(client, 0x40, state->i2s_mode);
|
282 |
|
|
}
|
283 |
|
|
|
284 |
|
|
void msp_set_audio(struct i2c_client *client)
|
285 |
|
|
{
|
286 |
|
|
struct msp_state *state = i2c_get_clientdata(client);
|
287 |
|
|
int bal = 0, bass, treble, loudness;
|
288 |
|
|
int val = 0;
|
289 |
|
|
int reallymuted = state->muted | state->scan_in_progress;
|
290 |
|
|
|
291 |
|
|
if (!reallymuted)
|
292 |
|
|
val = (state->volume * 0x7f / 65535) << 8;
|
293 |
|
|
|
294 |
|
|
v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
|
295 |
|
|
state->muted ? "on" : "off", state->scan_in_progress ? "yes" : "no",
|
296 |
|
|
state->volume);
|
297 |
|
|
|
298 |
|
|
msp_write_dsp(client, 0x0000, val);
|
299 |
|
|
msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
|
300 |
|
|
if (state->has_scart2_out_volume)
|
301 |
|
|
msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
|
302 |
|
|
if (state->has_headphones)
|
303 |
|
|
msp_write_dsp(client, 0x0006, val);
|
304 |
|
|
if (!state->has_sound_processing)
|
305 |
|
|
return;
|
306 |
|
|
|
307 |
|
|
if (val)
|
308 |
|
|
bal = (u8)((state->balance / 256) - 128);
|
309 |
|
|
bass = ((state->bass - 32768) * 0x60 / 65535) << 8;
|
310 |
|
|
treble = ((state->treble - 32768) * 0x60 / 65535) << 8;
|
311 |
|
|
loudness = state->loudness ? ((5 * 4) << 8) : 0;
|
312 |
|
|
|
313 |
|
|
v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n",
|
314 |
|
|
state->balance, state->bass, state->treble, state->loudness);
|
315 |
|
|
|
316 |
|
|
msp_write_dsp(client, 0x0001, bal << 8);
|
317 |
|
|
msp_write_dsp(client, 0x0002, bass);
|
318 |
|
|
msp_write_dsp(client, 0x0003, treble);
|
319 |
|
|
msp_write_dsp(client, 0x0004, loudness);
|
320 |
|
|
if (!state->has_headphones)
|
321 |
|
|
return;
|
322 |
|
|
msp_write_dsp(client, 0x0030, bal << 8);
|
323 |
|
|
msp_write_dsp(client, 0x0031, bass);
|
324 |
|
|
msp_write_dsp(client, 0x0032, treble);
|
325 |
|
|
msp_write_dsp(client, 0x0033, loudness);
|
326 |
|
|
}
|
327 |
|
|
|
328 |
|
|
/* ------------------------------------------------------------------------ */
|
329 |
|
|
|
330 |
|
|
|
331 |
|
|
static void msp_wake_thread(struct i2c_client *client)
|
332 |
|
|
{
|
333 |
|
|
struct msp_state *state = i2c_get_clientdata(client);
|
334 |
|
|
|
335 |
|
|
if (NULL == state->kthread)
|
336 |
|
|
return;
|
337 |
|
|
state->watch_stereo = 0;
|
338 |
|
|
state->restart = 1;
|
339 |
|
|
wake_up_interruptible(&state->wq);
|
340 |
|
|
}
|
341 |
|
|
|
342 |
|
|
int msp_sleep(struct msp_state *state, int timeout)
|
343 |
|
|
{
|
344 |
|
|
DECLARE_WAITQUEUE(wait, current);
|
345 |
|
|
|
346 |
|
|
add_wait_queue(&state->wq, &wait);
|
347 |
|
|
if (!kthread_should_stop()) {
|
348 |
|
|
if (timeout < 0) {
|
349 |
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
350 |
|
|
schedule();
|
351 |
|
|
} else {
|
352 |
|
|
schedule_timeout_interruptible
|
353 |
|
|
(msecs_to_jiffies(timeout));
|
354 |
|
|
}
|
355 |
|
|
}
|
356 |
|
|
|
357 |
|
|
remove_wait_queue(&state->wq, &wait);
|
358 |
|
|
try_to_freeze();
|
359 |
|
|
return state->restart;
|
360 |
|
|
}
|
361 |
|
|
|
362 |
|
|
/* ------------------------------------------------------------------------ */
|
363 |
|
|
#ifdef CONFIG_VIDEO_V4L1
|
364 |
|
|
static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode)
|
365 |
|
|
{
|
366 |
|
|
if (rxsubchans == V4L2_TUNER_SUB_MONO)
|
367 |
|
|
return VIDEO_SOUND_MONO;
|
368 |
|
|
if (rxsubchans == V4L2_TUNER_SUB_STEREO)
|
369 |
|
|
return VIDEO_SOUND_STEREO;
|
370 |
|
|
if (audmode == V4L2_TUNER_MODE_LANG2)
|
371 |
|
|
return VIDEO_SOUND_LANG2;
|
372 |
|
|
return VIDEO_SOUND_LANG1;
|
373 |
|
|
}
|
374 |
|
|
|
375 |
|
|
static int msp_mode_v4l1_to_v4l2(int mode)
|
376 |
|
|
{
|
377 |
|
|
if (mode & VIDEO_SOUND_STEREO)
|
378 |
|
|
return V4L2_TUNER_MODE_STEREO;
|
379 |
|
|
if (mode & VIDEO_SOUND_LANG2)
|
380 |
|
|
return V4L2_TUNER_MODE_LANG2;
|
381 |
|
|
if (mode & VIDEO_SOUND_LANG1)
|
382 |
|
|
return V4L2_TUNER_MODE_LANG1;
|
383 |
|
|
return V4L2_TUNER_MODE_MONO;
|
384 |
|
|
}
|
385 |
|
|
#endif
|
386 |
|
|
|
387 |
|
|
static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
|
388 |
|
|
{
|
389 |
|
|
struct msp_state *state = i2c_get_clientdata(client);
|
390 |
|
|
|
391 |
|
|
switch (ctrl->id) {
|
392 |
|
|
case V4L2_CID_AUDIO_VOLUME:
|
393 |
|
|
ctrl->value = state->volume;
|
394 |
|
|
break;
|
395 |
|
|
|
396 |
|
|
case V4L2_CID_AUDIO_MUTE:
|
397 |
|
|
ctrl->value = state->muted;
|
398 |
|
|
break;
|
399 |
|
|
|
400 |
|
|
case V4L2_CID_AUDIO_BALANCE:
|
401 |
|
|
if (!state->has_sound_processing)
|
402 |
|
|
return -EINVAL;
|
403 |
|
|
ctrl->value = state->balance;
|
404 |
|
|
break;
|
405 |
|
|
|
406 |
|
|
case V4L2_CID_AUDIO_BASS:
|
407 |
|
|
if (!state->has_sound_processing)
|
408 |
|
|
return -EINVAL;
|
409 |
|
|
ctrl->value = state->bass;
|
410 |
|
|
break;
|
411 |
|
|
|
412 |
|
|
case V4L2_CID_AUDIO_TREBLE:
|
413 |
|
|
if (!state->has_sound_processing)
|
414 |
|
|
return -EINVAL;
|
415 |
|
|
ctrl->value = state->treble;
|
416 |
|
|
break;
|
417 |
|
|
|
418 |
|
|
case V4L2_CID_AUDIO_LOUDNESS:
|
419 |
|
|
if (!state->has_sound_processing)
|
420 |
|
|
return -EINVAL;
|
421 |
|
|
ctrl->value = state->loudness;
|
422 |
|
|
break;
|
423 |
|
|
|
424 |
|
|
default:
|
425 |
|
|
return -EINVAL;
|
426 |
|
|
}
|
427 |
|
|
return 0;
|
428 |
|
|
}
|
429 |
|
|
|
430 |
|
|
static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
|
431 |
|
|
{
|
432 |
|
|
struct msp_state *state = i2c_get_clientdata(client);
|
433 |
|
|
|
434 |
|
|
switch (ctrl->id) {
|
435 |
|
|
case V4L2_CID_AUDIO_VOLUME:
|
436 |
|
|
state->volume = ctrl->value;
|
437 |
|
|
if (state->volume == 0)
|
438 |
|
|
state->balance = 32768;
|
439 |
|
|
break;
|
440 |
|
|
|
441 |
|
|
case V4L2_CID_AUDIO_MUTE:
|
442 |
|
|
if (ctrl->value < 0 || ctrl->value >= 2)
|
443 |
|
|
return -ERANGE;
|
444 |
|
|
state->muted = ctrl->value;
|
445 |
|
|
break;
|
446 |
|
|
|
447 |
|
|
case V4L2_CID_AUDIO_BASS:
|
448 |
|
|
if (!state->has_sound_processing)
|
449 |
|
|
return -EINVAL;
|
450 |
|
|
state->bass = ctrl->value;
|
451 |
|
|
break;
|
452 |
|
|
|
453 |
|
|
case V4L2_CID_AUDIO_TREBLE:
|
454 |
|
|
if (!state->has_sound_processing)
|
455 |
|
|
return -EINVAL;
|
456 |
|
|
state->treble = ctrl->value;
|
457 |
|
|
break;
|
458 |
|
|
|
459 |
|
|
case V4L2_CID_AUDIO_LOUDNESS:
|
460 |
|
|
if (!state->has_sound_processing)
|
461 |
|
|
return -EINVAL;
|
462 |
|
|
state->loudness = ctrl->value;
|
463 |
|
|
break;
|
464 |
|
|
|
465 |
|
|
case V4L2_CID_AUDIO_BALANCE:
|
466 |
|
|
if (!state->has_sound_processing)
|
467 |
|
|
return -EINVAL;
|
468 |
|
|
state->balance = ctrl->value;
|
469 |
|
|
break;
|
470 |
|
|
|
471 |
|
|
default:
|
472 |
|
|
return -EINVAL;
|
473 |
|
|
}
|
474 |
|
|
msp_set_audio(client);
|
475 |
|
|
return 0;
|
476 |
|
|
}
|
477 |
|
|
|
478 |
|
|
static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
479 |
|
|
{
|
480 |
|
|
struct msp_state *state = i2c_get_clientdata(client);
|
481 |
|
|
|
482 |
|
|
if (msp_debug >= 2)
|
483 |
|
|
v4l_i2c_print_ioctl(client, cmd);
|
484 |
|
|
|
485 |
|
|
switch (cmd) {
|
486 |
|
|
case AUDC_SET_RADIO:
|
487 |
|
|
if (state->radio)
|
488 |
|
|
return 0;
|
489 |
|
|
state->radio = 1;
|
490 |
|
|
v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
|
491 |
|
|
state->watch_stereo = 0;
|
492 |
|
|
switch (state->opmode) {
|
493 |
|
|
case OPMODE_MANUAL:
|
494 |
|
|
/* set msp3400 to FM radio mode */
|
495 |
|
|
msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
|
496 |
|
|
msp3400c_set_carrier(client, MSP_CARRIER(10.7),
|
497 |
|
|
MSP_CARRIER(10.7));
|
498 |
|
|
msp_set_audio(client);
|
499 |
|
|
break;
|
500 |
|
|
case OPMODE_AUTODETECT:
|
501 |
|
|
case OPMODE_AUTOSELECT:
|
502 |
|
|
/* the thread will do for us */
|
503 |
|
|
msp_wake_thread(client);
|
504 |
|
|
break;
|
505 |
|
|
}
|
506 |
|
|
break;
|
507 |
|
|
|
508 |
|
|
/* --- v4l ioctls --- */
|
509 |
|
|
/* take care: bttv does userspace copying, we'll get a
|
510 |
|
|
kernel pointer here... */
|
511 |
|
|
#ifdef CONFIG_VIDEO_V4L1
|
512 |
|
|
case VIDIOCGAUDIO:
|
513 |
|
|
{
|
514 |
|
|
struct video_audio *va = arg;
|
515 |
|
|
|
516 |
|
|
va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_MUTABLE;
|
517 |
|
|
if (state->has_sound_processing)
|
518 |
|
|
va->flags |= VIDEO_AUDIO_BALANCE |
|
519 |
|
|
VIDEO_AUDIO_BASS |
|
520 |
|
|
VIDEO_AUDIO_TREBLE;
|
521 |
|
|
if (state->muted)
|
522 |
|
|
va->flags |= VIDEO_AUDIO_MUTE;
|
523 |
|
|
va->volume = state->volume;
|
524 |
|
|
va->balance = state->volume ? state->balance : 32768;
|
525 |
|
|
va->bass = state->bass;
|
526 |
|
|
va->treble = state->treble;
|
527 |
|
|
|
528 |
|
|
if (state->radio)
|
529 |
|
|
break;
|
530 |
|
|
if (state->opmode == OPMODE_AUTOSELECT)
|
531 |
|
|
msp_detect_stereo(client);
|
532 |
|
|
va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode);
|
533 |
|
|
break;
|
534 |
|
|
}
|
535 |
|
|
|
536 |
|
|
case VIDIOCSAUDIO:
|
537 |
|
|
{
|
538 |
|
|
struct video_audio *va = arg;
|
539 |
|
|
|
540 |
|
|
state->muted = (va->flags & VIDEO_AUDIO_MUTE);
|
541 |
|
|
state->volume = va->volume;
|
542 |
|
|
state->balance = va->balance;
|
543 |
|
|
state->bass = va->bass;
|
544 |
|
|
state->treble = va->treble;
|
545 |
|
|
msp_set_audio(client);
|
546 |
|
|
|
547 |
|
|
if (va->mode != 0 && state->radio == 0 &&
|
548 |
|
|
state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) {
|
549 |
|
|
state->audmode = msp_mode_v4l1_to_v4l2(va->mode);
|
550 |
|
|
msp_set_audmode(client);
|
551 |
|
|
}
|
552 |
|
|
break;
|
553 |
|
|
}
|
554 |
|
|
|
555 |
|
|
case VIDIOCSCHAN:
|
556 |
|
|
{
|
557 |
|
|
struct video_channel *vc = arg;
|
558 |
|
|
int update = 0;
|
559 |
|
|
v4l2_std_id std;
|
560 |
|
|
|
561 |
|
|
if (state->radio)
|
562 |
|
|
update = 1;
|
563 |
|
|
state->radio = 0;
|
564 |
|
|
if (vc->norm == VIDEO_MODE_PAL)
|
565 |
|
|
std = V4L2_STD_PAL;
|
566 |
|
|
else if (vc->norm == VIDEO_MODE_SECAM)
|
567 |
|
|
std = V4L2_STD_SECAM;
|
568 |
|
|
else
|
569 |
|
|
std = V4L2_STD_NTSC;
|
570 |
|
|
if (std != state->v4l2_std) {
|
571 |
|
|
state->v4l2_std = std;
|
572 |
|
|
update = 1;
|
573 |
|
|
}
|
574 |
|
|
if (update)
|
575 |
|
|
msp_wake_thread(client);
|
576 |
|
|
break;
|
577 |
|
|
}
|
578 |
|
|
|
579 |
|
|
case VIDIOCSFREQ:
|
580 |
|
|
{
|
581 |
|
|
/* new channel -- kick audio carrier scan */
|
582 |
|
|
msp_wake_thread(client);
|
583 |
|
|
break;
|
584 |
|
|
}
|
585 |
|
|
#endif
|
586 |
|
|
case VIDIOC_S_FREQUENCY:
|
587 |
|
|
{
|
588 |
|
|
/* new channel -- kick audio carrier scan */
|
589 |
|
|
msp_wake_thread(client);
|
590 |
|
|
break;
|
591 |
|
|
}
|
592 |
|
|
|
593 |
|
|
/* --- v4l2 ioctls --- */
|
594 |
|
|
case VIDIOC_S_STD:
|
595 |
|
|
{
|
596 |
|
|
v4l2_std_id *id = arg;
|
597 |
|
|
int update = state->radio || state->v4l2_std != *id;
|
598 |
|
|
|
599 |
|
|
state->v4l2_std = *id;
|
600 |
|
|
state->radio = 0;
|
601 |
|
|
if (update)
|
602 |
|
|
msp_wake_thread(client);
|
603 |
|
|
return 0;
|
604 |
|
|
}
|
605 |
|
|
|
606 |
|
|
case VIDIOC_INT_G_AUDIO_ROUTING:
|
607 |
|
|
{
|
608 |
|
|
struct v4l2_routing *rt = arg;
|
609 |
|
|
|
610 |
|
|
*rt = state->routing;
|
611 |
|
|
break;
|
612 |
|
|
}
|
613 |
|
|
|
614 |
|
|
case VIDIOC_INT_S_AUDIO_ROUTING:
|
615 |
|
|
{
|
616 |
|
|
struct v4l2_routing *rt = arg;
|
617 |
|
|
int tuner = (rt->input >> 3) & 1;
|
618 |
|
|
int sc_in = rt->input & 0x7;
|
619 |
|
|
int sc1_out = rt->output & 0xf;
|
620 |
|
|
int sc2_out = (rt->output >> 4) & 0xf;
|
621 |
|
|
u16 val, reg;
|
622 |
|
|
int i;
|
623 |
|
|
int extern_input = 1;
|
624 |
|
|
|
625 |
|
|
if (state->routing.input == rt->input &&
|
626 |
|
|
state->routing.output == rt->output)
|
627 |
|
|
break;
|
628 |
|
|
state->routing = *rt;
|
629 |
|
|
/* check if the tuner input is used */
|
630 |
|
|
for (i = 0; i < 5; i++) {
|
631 |
|
|
if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
|
632 |
|
|
extern_input = 0;
|
633 |
|
|
}
|
634 |
|
|
state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
|
635 |
|
|
state->rxsubchans = V4L2_TUNER_SUB_STEREO;
|
636 |
|
|
msp_set_scart(client, sc_in, 0);
|
637 |
|
|
msp_set_scart(client, sc1_out, 1);
|
638 |
|
|
msp_set_scart(client, sc2_out, 2);
|
639 |
|
|
msp_set_audmode(client);
|
640 |
|
|
reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
|
641 |
|
|
val = msp_read_dem(client, reg);
|
642 |
|
|
msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
|
643 |
|
|
/* wake thread when a new input is chosen */
|
644 |
|
|
msp_wake_thread(client);
|
645 |
|
|
break;
|
646 |
|
|
}
|
647 |
|
|
|
648 |
|
|
case VIDIOC_G_TUNER:
|
649 |
|
|
{
|
650 |
|
|
struct v4l2_tuner *vt = arg;
|
651 |
|
|
|
652 |
|
|
if (state->radio)
|
653 |
|
|
break;
|
654 |
|
|
if (state->opmode == OPMODE_AUTOSELECT)
|
655 |
|
|
msp_detect_stereo(client);
|
656 |
|
|
vt->audmode = state->audmode;
|
657 |
|
|
vt->rxsubchans = state->rxsubchans;
|
658 |
|
|
vt->capability |= V4L2_TUNER_CAP_STEREO |
|
659 |
|
|
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
|
660 |
|
|
break;
|
661 |
|
|
}
|
662 |
|
|
|
663 |
|
|
case VIDIOC_S_TUNER:
|
664 |
|
|
{
|
665 |
|
|
struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
|
666 |
|
|
|
667 |
|
|
if (state->radio) /* TODO: add mono/stereo support for radio */
|
668 |
|
|
break;
|
669 |
|
|
if (state->audmode == vt->audmode)
|
670 |
|
|
break;
|
671 |
|
|
state->audmode = vt->audmode;
|
672 |
|
|
/* only set audmode */
|
673 |
|
|
msp_set_audmode(client);
|
674 |
|
|
break;
|
675 |
|
|
}
|
676 |
|
|
|
677 |
|
|
case VIDIOC_INT_I2S_CLOCK_FREQ:
|
678 |
|
|
{
|
679 |
|
|
u32 *a = (u32 *)arg;
|
680 |
|
|
|
681 |
|
|
v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a);
|
682 |
|
|
|
683 |
|
|
switch (*a) {
|
684 |
|
|
case 1024000:
|
685 |
|
|
state->i2s_mode = 0;
|
686 |
|
|
break;
|
687 |
|
|
case 2048000:
|
688 |
|
|
state->i2s_mode = 1;
|
689 |
|
|
break;
|
690 |
|
|
default:
|
691 |
|
|
return -EINVAL;
|
692 |
|
|
}
|
693 |
|
|
break;
|
694 |
|
|
}
|
695 |
|
|
|
696 |
|
|
case VIDIOC_QUERYCTRL:
|
697 |
|
|
{
|
698 |
|
|
struct v4l2_queryctrl *qc = arg;
|
699 |
|
|
|
700 |
|
|
switch (qc->id) {
|
701 |
|
|
case V4L2_CID_AUDIO_VOLUME:
|
702 |
|
|
case V4L2_CID_AUDIO_MUTE:
|
703 |
|
|
return v4l2_ctrl_query_fill_std(qc);
|
704 |
|
|
default:
|
705 |
|
|
break;
|
706 |
|
|
}
|
707 |
|
|
if (!state->has_sound_processing)
|
708 |
|
|
return -EINVAL;
|
709 |
|
|
switch (qc->id) {
|
710 |
|
|
case V4L2_CID_AUDIO_LOUDNESS:
|
711 |
|
|
case V4L2_CID_AUDIO_BALANCE:
|
712 |
|
|
case V4L2_CID_AUDIO_BASS:
|
713 |
|
|
case V4L2_CID_AUDIO_TREBLE:
|
714 |
|
|
return v4l2_ctrl_query_fill_std(qc);
|
715 |
|
|
default:
|
716 |
|
|
return -EINVAL;
|
717 |
|
|
}
|
718 |
|
|
}
|
719 |
|
|
|
720 |
|
|
case VIDIOC_G_CTRL:
|
721 |
|
|
return msp_get_ctrl(client, arg);
|
722 |
|
|
|
723 |
|
|
case VIDIOC_S_CTRL:
|
724 |
|
|
return msp_set_ctrl(client, arg);
|
725 |
|
|
|
726 |
|
|
case VIDIOC_LOG_STATUS:
|
727 |
|
|
{
|
728 |
|
|
const char *p;
|
729 |
|
|
|
730 |
|
|
if (state->opmode == OPMODE_AUTOSELECT)
|
731 |
|
|
msp_detect_stereo(client);
|
732 |
|
|
v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
|
733 |
|
|
client->name, state->rev1, state->rev2);
|
734 |
|
|
v4l_info(client, "Audio: volume %d%s\n",
|
735 |
|
|
state->volume, state->muted ? " (muted)" : "");
|
736 |
|
|
if (state->has_sound_processing) {
|
737 |
|
|
v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
|
738 |
|
|
state->balance, state->bass, state->treble,
|
739 |
|
|
state->loudness ? "on" : "off");
|
740 |
|
|
}
|
741 |
|
|
switch (state->mode) {
|
742 |
|
|
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
|
743 |
|
|
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
|
744 |
|
|
case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono + FM-stereo"; break;
|
745 |
|
|
case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break;
|
746 |
|
|
case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break;
|
747 |
|
|
case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break;
|
748 |
|
|
case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break;
|
749 |
|
|
case MSP_MODE_BTSC: p = "BTSC"; break;
|
750 |
|
|
case MSP_MODE_EXTERN: p = "External input"; break;
|
751 |
|
|
default: p = "unknown"; break;
|
752 |
|
|
}
|
753 |
|
|
if (state->mode == MSP_MODE_EXTERN) {
|
754 |
|
|
v4l_info(client, "Mode: %s\n", p);
|
755 |
|
|
} else if (state->opmode == OPMODE_MANUAL) {
|
756 |
|
|
v4l_info(client, "Mode: %s (%s%s)\n", p,
|
757 |
|
|
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
|
758 |
|
|
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
|
759 |
|
|
} else {
|
760 |
|
|
if (state->opmode == OPMODE_AUTODETECT)
|
761 |
|
|
v4l_info(client, "Mode: %s\n", p);
|
762 |
|
|
v4l_info(client, "Standard: %s (%s%s)\n",
|
763 |
|
|
msp_standard_std_name(state->std),
|
764 |
|
|
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
|
765 |
|
|
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
|
766 |
|
|
}
|
767 |
|
|
v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
|
768 |
|
|
v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
|
769 |
|
|
state->routing.input, state->routing.output);
|
770 |
|
|
v4l_info(client, "ACB: 0x%04x\n", state->acb);
|
771 |
|
|
break;
|
772 |
|
|
}
|
773 |
|
|
|
774 |
|
|
case VIDIOC_G_CHIP_IDENT:
|
775 |
|
|
return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2);
|
776 |
|
|
|
777 |
|
|
default:
|
778 |
|
|
/* unknown */
|
779 |
|
|
return -EINVAL;
|
780 |
|
|
}
|
781 |
|
|
return 0;
|
782 |
|
|
}
|
783 |
|
|
|
784 |
|
|
static int msp_suspend(struct i2c_client *client, pm_message_t state)
|
785 |
|
|
{
|
786 |
|
|
|
787 |
|
|
v4l_dbg(1, msp_debug, client, "suspend\n");
|
788 |
|
|
msp_reset(client);
|
789 |
|
|
return 0;
|
790 |
|
|
}
|
791 |
|
|
|
792 |
|
|
static int msp_resume(struct i2c_client *client)
|
793 |
|
|
{
|
794 |
|
|
|
795 |
|
|
v4l_dbg(1, msp_debug, client, "resume\n");
|
796 |
|
|
msp_wake_thread(client);
|
797 |
|
|
return 0;
|
798 |
|
|
}
|
799 |
|
|
|
800 |
|
|
/* ----------------------------------------------------------------------- */
|
801 |
|
|
|
802 |
|
|
static struct i2c_driver i2c_driver;
|
803 |
|
|
|
804 |
|
|
static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
|
805 |
|
|
{
|
806 |
|
|
struct i2c_client *client;
|
807 |
|
|
struct msp_state *state;
|
808 |
|
|
int (*thread_func)(void *data) = NULL;
|
809 |
|
|
int msp_hard;
|
810 |
|
|
int msp_family;
|
811 |
|
|
int msp_revision;
|
812 |
|
|
int msp_product, msp_prod_hi, msp_prod_lo;
|
813 |
|
|
int msp_rom;
|
814 |
|
|
|
815 |
|
|
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
816 |
|
|
if (!client)
|
817 |
|
|
return -ENOMEM;
|
818 |
|
|
|
819 |
|
|
client->addr = address;
|
820 |
|
|
client->adapter = adapter;
|
821 |
|
|
client->driver = &i2c_driver;
|
822 |
|
|
snprintf(client->name, sizeof(client->name) - 1, "msp3400");
|
823 |
|
|
|
824 |
|
|
if (msp_reset(client) == -1) {
|
825 |
|
|
v4l_dbg(1, msp_debug, client, "msp3400 not found\n");
|
826 |
|
|
kfree(client);
|
827 |
|
|
return 0;
|
828 |
|
|
}
|
829 |
|
|
|
830 |
|
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
831 |
|
|
if (!state) {
|
832 |
|
|
kfree(client);
|
833 |
|
|
return -ENOMEM;
|
834 |
|
|
}
|
835 |
|
|
|
836 |
|
|
i2c_set_clientdata(client, state);
|
837 |
|
|
|
838 |
|
|
state->v4l2_std = V4L2_STD_NTSC;
|
839 |
|
|
state->audmode = V4L2_TUNER_MODE_STEREO;
|
840 |
|
|
state->volume = 58880; /* 0db gain */
|
841 |
|
|
state->balance = 32768; /* 0db gain */
|
842 |
|
|
state->bass = 32768;
|
843 |
|
|
state->treble = 32768;
|
844 |
|
|
state->loudness = 0;
|
845 |
|
|
state->input = -1;
|
846 |
|
|
state->muted = 0;
|
847 |
|
|
state->i2s_mode = 0;
|
848 |
|
|
init_waitqueue_head(&state->wq);
|
849 |
|
|
/* These are the reset input/output positions */
|
850 |
|
|
state->routing.input = MSP_INPUT_DEFAULT;
|
851 |
|
|
state->routing.output = MSP_OUTPUT_DEFAULT;
|
852 |
|
|
|
853 |
|
|
state->rev1 = msp_read_dsp(client, 0x1e);
|
854 |
|
|
if (state->rev1 != -1)
|
855 |
|
|
state->rev2 = msp_read_dsp(client, 0x1f);
|
856 |
|
|
v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", state->rev1, state->rev2);
|
857 |
|
|
if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {
|
858 |
|
|
v4l_dbg(1, msp_debug, client, "not an msp3400 (cannot read chip version)\n");
|
859 |
|
|
kfree(state);
|
860 |
|
|
kfree(client);
|
861 |
|
|
return 0;
|
862 |
|
|
}
|
863 |
|
|
|
864 |
|
|
msp_set_audio(client);
|
865 |
|
|
|
866 |
|
|
msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
|
867 |
|
|
msp_product = (state->rev2 >> 8) & 0xff;
|
868 |
|
|
msp_prod_hi = msp_product / 10;
|
869 |
|
|
msp_prod_lo = msp_product % 10;
|
870 |
|
|
msp_revision = (state->rev1 & 0x0f) + '@';
|
871 |
|
|
msp_hard = ((state->rev1 >> 8) & 0xff) + '@';
|
872 |
|
|
msp_rom = state->rev2 & 0x1f;
|
873 |
|
|
snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d",
|
874 |
|
|
msp_family, msp_product,
|
875 |
|
|
msp_revision, msp_hard, msp_rom);
|
876 |
|
|
/* Rev B=2, C=3, D=4, G=7 */
|
877 |
|
|
state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@';
|
878 |
|
|
|
879 |
|
|
/* Has NICAM support: all mspx41x and mspx45x products have NICAM */
|
880 |
|
|
state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5;
|
881 |
|
|
/* Has radio support: was added with revision G */
|
882 |
|
|
state->has_radio = msp_revision >= 'G';
|
883 |
|
|
/* Has headphones output: not for stripped down products */
|
884 |
|
|
state->has_headphones = msp_prod_lo < 5;
|
885 |
|
|
/* Has scart2 input: not in stripped down products of the '3' family */
|
886 |
|
|
state->has_scart2 = msp_family >= 4 || msp_prod_lo < 7;
|
887 |
|
|
/* Has scart3 input: not in stripped down products of the '3' family */
|
888 |
|
|
state->has_scart3 = msp_family >= 4 || msp_prod_lo < 5;
|
889 |
|
|
/* Has scart4 input: not in pre D revisions, not in stripped D revs */
|
890 |
|
|
state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);
|
891 |
|
|
/* Has scart2 output: not in stripped down products of the '3' family */
|
892 |
|
|
state->has_scart2_out = msp_family >= 4 || msp_prod_lo < 5;
|
893 |
|
|
/* Has scart2 a volume control? Not in pre-D revisions. */
|
894 |
|
|
state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart2_out;
|
895 |
|
|
/* Has a configurable i2s out? */
|
896 |
|
|
state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7;
|
897 |
|
|
/* Has subwoofer output: not in pre-D revs and not in stripped down products */
|
898 |
|
|
state->has_subwoofer = msp_revision >= 'D' && msp_prod_lo < 5;
|
899 |
|
|
/* Has soundprocessing (bass/treble/balance/loudness/equalizer): not in
|
900 |
|
|
stripped down products */
|
901 |
|
|
state->has_sound_processing = msp_prod_lo < 7;
|
902 |
|
|
/* Has Virtual Dolby Surround: only in msp34x1 */
|
903 |
|
|
state->has_virtual_dolby_surround = msp_revision == 'G' && msp_prod_lo == 1;
|
904 |
|
|
/* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
|
905 |
|
|
state->has_dolby_pro_logic = msp_revision == 'G' && msp_prod_lo == 2;
|
906 |
|
|
/* The msp343xG supports BTSC only and cannot do Automatic Standard Detection. */
|
907 |
|
|
state->force_btsc = msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3;
|
908 |
|
|
|
909 |
|
|
state->opmode = opmode;
|
910 |
|
|
if (state->opmode == OPMODE_AUTO) {
|
911 |
|
|
/* MSP revision G and up have both autodetect and autoselect */
|
912 |
|
|
if (msp_revision >= 'G')
|
913 |
|
|
state->opmode = OPMODE_AUTOSELECT;
|
914 |
|
|
/* MSP revision D and up have autodetect */
|
915 |
|
|
else if (msp_revision >= 'D')
|
916 |
|
|
state->opmode = OPMODE_AUTODETECT;
|
917 |
|
|
else
|
918 |
|
|
state->opmode = OPMODE_MANUAL;
|
919 |
|
|
}
|
920 |
|
|
|
921 |
|
|
/* hello world :-) */
|
922 |
|
|
v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, address << 1, adapter->name);
|
923 |
|
|
v4l_info(client, "%s ", client->name);
|
924 |
|
|
if (state->has_nicam && state->has_radio)
|
925 |
|
|
printk("supports nicam and radio, ");
|
926 |
|
|
else if (state->has_nicam)
|
927 |
|
|
printk("supports nicam, ");
|
928 |
|
|
else if (state->has_radio)
|
929 |
|
|
printk("supports radio, ");
|
930 |
|
|
printk("mode is ");
|
931 |
|
|
|
932 |
|
|
/* version-specific initialization */
|
933 |
|
|
switch (state->opmode) {
|
934 |
|
|
case OPMODE_MANUAL:
|
935 |
|
|
printk("manual");
|
936 |
|
|
thread_func = msp3400c_thread;
|
937 |
|
|
break;
|
938 |
|
|
case OPMODE_AUTODETECT:
|
939 |
|
|
printk("autodetect");
|
940 |
|
|
thread_func = msp3410d_thread;
|
941 |
|
|
break;
|
942 |
|
|
case OPMODE_AUTOSELECT:
|
943 |
|
|
printk("autodetect and autoselect");
|
944 |
|
|
thread_func = msp34xxg_thread;
|
945 |
|
|
break;
|
946 |
|
|
}
|
947 |
|
|
printk("\n");
|
948 |
|
|
|
949 |
|
|
/* startup control thread if needed */
|
950 |
|
|
if (thread_func) {
|
951 |
|
|
state->kthread = kthread_run(thread_func, client, "msp34xx");
|
952 |
|
|
|
953 |
|
|
if (IS_ERR(state->kthread))
|
954 |
|
|
v4l_warn(client, "kernel_thread() failed\n");
|
955 |
|
|
msp_wake_thread(client);
|
956 |
|
|
}
|
957 |
|
|
|
958 |
|
|
/* done */
|
959 |
|
|
i2c_attach_client(client);
|
960 |
|
|
|
961 |
|
|
return 0;
|
962 |
|
|
}
|
963 |
|
|
|
964 |
|
|
static int msp_probe(struct i2c_adapter *adapter)
|
965 |
|
|
{
|
966 |
|
|
if (adapter->class & I2C_CLASS_TV_ANALOG)
|
967 |
|
|
return i2c_probe(adapter, &addr_data, msp_attach);
|
968 |
|
|
return 0;
|
969 |
|
|
}
|
970 |
|
|
|
971 |
|
|
static int msp_detach(struct i2c_client *client)
|
972 |
|
|
{
|
973 |
|
|
struct msp_state *state = i2c_get_clientdata(client);
|
974 |
|
|
int err;
|
975 |
|
|
|
976 |
|
|
/* shutdown control thread */
|
977 |
|
|
if (state->kthread) {
|
978 |
|
|
state->restart = 1;
|
979 |
|
|
kthread_stop(state->kthread);
|
980 |
|
|
}
|
981 |
|
|
msp_reset(client);
|
982 |
|
|
|
983 |
|
|
err = i2c_detach_client(client);
|
984 |
|
|
if (err) {
|
985 |
|
|
return err;
|
986 |
|
|
}
|
987 |
|
|
|
988 |
|
|
kfree(state);
|
989 |
|
|
kfree(client);
|
990 |
|
|
return 0;
|
991 |
|
|
}
|
992 |
|
|
|
993 |
|
|
/* ----------------------------------------------------------------------- */
|
994 |
|
|
|
995 |
|
|
/* i2c implementation */
|
996 |
|
|
static struct i2c_driver i2c_driver = {
|
997 |
|
|
.id = I2C_DRIVERID_MSP3400,
|
998 |
|
|
.attach_adapter = msp_probe,
|
999 |
|
|
.detach_client = msp_detach,
|
1000 |
|
|
.suspend = msp_suspend,
|
1001 |
|
|
.resume = msp_resume,
|
1002 |
|
|
.command = msp_command,
|
1003 |
|
|
.driver = {
|
1004 |
|
|
.name = "msp3400",
|
1005 |
|
|
},
|
1006 |
|
|
};
|
1007 |
|
|
|
1008 |
|
|
static int __init msp3400_init_module(void)
|
1009 |
|
|
{
|
1010 |
|
|
return i2c_add_driver(&i2c_driver);
|
1011 |
|
|
}
|
1012 |
|
|
|
1013 |
|
|
static void __exit msp3400_cleanup_module(void)
|
1014 |
|
|
{
|
1015 |
|
|
i2c_del_driver(&i2c_driver);
|
1016 |
|
|
}
|
1017 |
|
|
|
1018 |
|
|
module_init(msp3400_init_module);
|
1019 |
|
|
module_exit(msp3400_cleanup_module);
|
1020 |
|
|
|
1021 |
|
|
/*
|
1022 |
|
|
* Overrides for Emacs so that we follow Linus's tabbing style.
|
1023 |
|
|
* ---------------------------------------------------------------------------
|
1024 |
|
|
* Local variables:
|
1025 |
|
|
* c-basic-offset: 8
|
1026 |
|
|
* End:
|
1027 |
|
|
*/
|