aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/Kconfig23
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/radio-timb.c244
-rw-r--r--drivers/media/radio/saa7706h.c451
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c8
6 files changed, 726 insertions, 4 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 3f40f375981b..83567b898d09 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -417,6 +417,18 @@ config RADIO_TEA5764_XTAL
417 Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N 417 Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N
418 here if TEA5764 reference frequency is connected in FREQIN. 418 here if TEA5764 reference frequency is connected in FREQIN.
419 419
420config RADIO_SAA7706H
421 tristate "SAA7706H Car Radio DSP"
422 depends on I2C && VIDEO_V4L2
423 ---help---
424 Say Y here if you want to use the SAA7706H Car radio Digital
425 Signal Processor, found for instance on the Russellville development
426 board. On the russellville the device is connected to internal
427 timberdale I2C bus.
428
429 To compile this driver as a module, choose M here: the
430 module will be called SAA7706H.
431
420config RADIO_TEF6862 432config RADIO_TEF6862
421 tristate "TEF6862 Car Radio Enhanced Selectivity Tuner" 433 tristate "TEF6862 Car Radio Enhanced Selectivity Tuner"
422 depends on I2C && VIDEO_V4L2 434 depends on I2C && VIDEO_V4L2
@@ -429,4 +441,15 @@ config RADIO_TEF6862
429 To compile this driver as a module, choose M here: the 441 To compile this driver as a module, choose M here: the
430 module will be called TEF6862. 442 module will be called TEF6862.
431 443
444config RADIO_TIMBERDALE
445 tristate "Enable the Timberdale radio driver"
446 depends on MFD_TIMBERDALE && VIDEO_V4L2
447 depends on I2C # for RADIO_SAA7706H
448 select RADIO_TEF6862
449 select RADIO_SAA7706H
450 ---help---
451 This is a kind of umbrella driver for the Radio Tuner and DSP
452 found behind the Timberdale FPGA on the Russellville board.
453 Enabling this driver will automatically select the DSP and tuner.
454
432endif # RADIO_ADAPTERS 455endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 01922ada6914..f615583b4837 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -23,6 +23,8 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o
23obj-$(CONFIG_RADIO_SI470X) += si470x/ 23obj-$(CONFIG_RADIO_SI470X) += si470x/
24obj-$(CONFIG_USB_MR800) += radio-mr800.o 24obj-$(CONFIG_USB_MR800) += radio-mr800.o
25obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o 25obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
26obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o
26obj-$(CONFIG_RADIO_TEF6862) += tef6862.o 27obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
28obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
27 29
28EXTRA_CFLAGS += -Isound 30EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
new file mode 100644
index 000000000000..0de457f6e6eb
--- /dev/null
+++ b/drivers/media/radio/radio-timb.c
@@ -0,0 +1,244 @@
1/*
2 * radio-timb.c Timberdale FPGA Radio driver
3 * Copyright (c) 2009 Intel Corporation
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <linux/version.h>
20#include <linux/io.h>
21#include <media/v4l2-ioctl.h>
22#include <media/v4l2-device.h>
23#include <linux/platform_device.h>
24#include <linux/interrupt.h>
25#include <linux/i2c.h>
26#include <media/timb_radio.h>
27
28#define DRIVER_NAME "timb-radio"
29
30struct timbradio {
31 struct timb_radio_platform_data pdata;
32 struct v4l2_subdev *sd_tuner;
33 struct v4l2_subdev *sd_dsp;
34 struct video_device video_dev;
35 struct v4l2_device v4l2_dev;
36};
37
38
39static int timbradio_vidioc_querycap(struct file *file, void *priv,
40 struct v4l2_capability *v)
41{
42 strlcpy(v->driver, DRIVER_NAME, sizeof(v->driver));
43 strlcpy(v->card, "Timberdale Radio", sizeof(v->card));
44 snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME);
45 v->version = KERNEL_VERSION(0, 0, 1);
46 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
47 return 0;
48}
49
50static int timbradio_vidioc_g_tuner(struct file *file, void *priv,
51 struct v4l2_tuner *v)
52{
53 struct timbradio *tr = video_drvdata(file);
54 return v4l2_subdev_call(tr->sd_tuner, tuner, g_tuner, v);
55}
56
57static int timbradio_vidioc_s_tuner(struct file *file, void *priv,
58 struct v4l2_tuner *v)
59{
60 struct timbradio *tr = video_drvdata(file);
61 return v4l2_subdev_call(tr->sd_tuner, tuner, s_tuner, v);
62}
63
64static int timbradio_vidioc_g_input(struct file *filp, void *priv,
65 unsigned int *i)
66{
67 *i = 0;
68 return 0;
69}
70
71static int timbradio_vidioc_s_input(struct file *filp, void *priv,
72 unsigned int i)
73{
74 return i ? -EINVAL : 0;
75}
76
77static int timbradio_vidioc_g_audio(struct file *file, void *priv,
78 struct v4l2_audio *a)
79{
80 a->index = 0;
81 strlcpy(a->name, "Radio", sizeof(a->name));
82 a->capability = V4L2_AUDCAP_STEREO;
83 return 0;
84}
85
86static int timbradio_vidioc_s_audio(struct file *file, void *priv,
87 struct v4l2_audio *a)
88{
89 return a->index ? -EINVAL : 0;
90}
91
92static int timbradio_vidioc_s_frequency(struct file *file, void *priv,
93 struct v4l2_frequency *f)
94{
95 struct timbradio *tr = video_drvdata(file);
96 return v4l2_subdev_call(tr->sd_tuner, tuner, s_frequency, f);
97}
98
99static int timbradio_vidioc_g_frequency(struct file *file, void *priv,
100 struct v4l2_frequency *f)
101{
102 struct timbradio *tr = video_drvdata(file);
103 return v4l2_subdev_call(tr->sd_tuner, tuner, g_frequency, f);
104}
105
106static int timbradio_vidioc_queryctrl(struct file *file, void *priv,
107 struct v4l2_queryctrl *qc)
108{
109 struct timbradio *tr = video_drvdata(file);
110 return v4l2_subdev_call(tr->sd_dsp, core, queryctrl, qc);
111}
112
113static int timbradio_vidioc_g_ctrl(struct file *file, void *priv,
114 struct v4l2_control *ctrl)
115{
116 struct timbradio *tr = video_drvdata(file);
117 return v4l2_subdev_call(tr->sd_dsp, core, g_ctrl, ctrl);
118}
119
120static int timbradio_vidioc_s_ctrl(struct file *file, void *priv,
121 struct v4l2_control *ctrl)
122{
123 struct timbradio *tr = video_drvdata(file);
124 return v4l2_subdev_call(tr->sd_dsp, core, s_ctrl, ctrl);
125}
126
127static const struct v4l2_ioctl_ops timbradio_ioctl_ops = {
128 .vidioc_querycap = timbradio_vidioc_querycap,
129 .vidioc_g_tuner = timbradio_vidioc_g_tuner,
130 .vidioc_s_tuner = timbradio_vidioc_s_tuner,
131 .vidioc_g_frequency = timbradio_vidioc_g_frequency,
132 .vidioc_s_frequency = timbradio_vidioc_s_frequency,
133 .vidioc_g_input = timbradio_vidioc_g_input,
134 .vidioc_s_input = timbradio_vidioc_s_input,
135 .vidioc_g_audio = timbradio_vidioc_g_audio,
136 .vidioc_s_audio = timbradio_vidioc_s_audio,
137 .vidioc_queryctrl = timbradio_vidioc_queryctrl,
138 .vidioc_g_ctrl = timbradio_vidioc_g_ctrl,
139 .vidioc_s_ctrl = timbradio_vidioc_s_ctrl
140};
141
142static const struct v4l2_file_operations timbradio_fops = {
143 .owner = THIS_MODULE,
144 .ioctl = video_ioctl2,
145};
146
147static int __devinit timbradio_probe(struct platform_device *pdev)
148{
149 struct timb_radio_platform_data *pdata = pdev->dev.platform_data;
150 struct timbradio *tr;
151 int err;
152
153 if (!pdata) {
154 dev_err(&pdev->dev, "Platform data missing\n");
155 err = -EINVAL;
156 goto err;
157 }
158
159 tr = kzalloc(sizeof(*tr), GFP_KERNEL);
160 if (!tr) {
161 err = -ENOMEM;
162 goto err;
163 }
164
165 tr->pdata = *pdata;
166
167 strlcpy(tr->video_dev.name, "Timberdale Radio",
168 sizeof(tr->video_dev.name));
169 tr->video_dev.fops = &timbradio_fops;
170 tr->video_dev.ioctl_ops = &timbradio_ioctl_ops;
171 tr->video_dev.release = video_device_release_empty;
172 tr->video_dev.minor = -1;
173
174 strlcpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name));
175 err = v4l2_device_register(NULL, &tr->v4l2_dev);
176 if (err)
177 goto err_v4l2_dev;
178
179 tr->video_dev.v4l2_dev = &tr->v4l2_dev;
180
181 err = video_register_device(&tr->video_dev, VFL_TYPE_RADIO, -1);
182 if (err) {
183 dev_err(&pdev->dev, "Error reg video\n");
184 goto err_video_req;
185 }
186
187 video_set_drvdata(&tr->video_dev, tr);
188
189 platform_set_drvdata(pdev, tr);
190 return 0;
191
192err_video_req:
193 video_device_release_empty(&tr->video_dev);
194 v4l2_device_unregister(&tr->v4l2_dev);
195err_v4l2_dev:
196 kfree(tr);
197err:
198 dev_err(&pdev->dev, "Failed to register: %d\n", err);
199
200 return err;
201}
202
203static int __devexit timbradio_remove(struct platform_device *pdev)
204{
205 struct timbradio *tr = platform_get_drvdata(pdev);
206
207 video_unregister_device(&tr->video_dev);
208 video_device_release_empty(&tr->video_dev);
209
210 v4l2_device_unregister(&tr->v4l2_dev);
211
212 kfree(tr);
213
214 return 0;
215}
216
217static struct platform_driver timbradio_platform_driver = {
218 .driver = {
219 .name = DRIVER_NAME,
220 .owner = THIS_MODULE,
221 },
222 .probe = timbradio_probe,
223 .remove = timbradio_remove,
224};
225
226/*--------------------------------------------------------------------------*/
227
228static int __init timbradio_init(void)
229{
230 return platform_driver_register(&timbradio_platform_driver);
231}
232
233static void __exit timbradio_exit(void)
234{
235 platform_driver_unregister(&timbradio_platform_driver);
236}
237
238module_init(timbradio_init);
239module_exit(timbradio_exit);
240
241MODULE_DESCRIPTION("Timberdale Radio driver");
242MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
243MODULE_LICENSE("GPL v2");
244MODULE_ALIAS("platform:"DRIVER_NAME);
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
new file mode 100644
index 000000000000..5db5528a8b25
--- /dev/null
+++ b/drivers/media/radio/saa7706h.c
@@ -0,0 +1,451 @@
1/*
2 * saa7706.c Philips SAA7706H Car Radio DSP driver
3 * Copyright (c) 2009 Intel Corporation
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/delay.h>
22#include <linux/errno.h>
23#include <linux/kernel.h>
24#include <linux/interrupt.h>
25#include <linux/i2c.h>
26#include <media/v4l2-device.h>
27#include <media/v4l2-chip-ident.h>
28
29#define DRIVER_NAME "saa7706h"
30
31/* the I2C memory map looks like this
32
33 $1C00 - $FFFF Not Used
34 $2200 - $3FFF Reserved YRAM (DSP2) space
35 $2000 - $21FF YRAM (DSP2)
36 $1FF0 - $1FFF Hardware Registers
37 $1280 - $1FEF Reserved XRAM (DSP2) space
38 $1000 - $127F XRAM (DSP2)
39 $0FFF DSP CONTROL
40 $0A00 - $0FFE Reserved
41 $0980 - $09FF Reserved YRAM (DSP1) space
42 $0800 - $097F YRAM (DSP1)
43 $0200 - $07FF Not Used
44 $0180 - $01FF Reserved XRAM (DSP1) space
45 $0000 - $017F XRAM (DSP1)
46*/
47
48#define SAA7706H_REG_CTRL 0x0fff
49#define SAA7706H_CTRL_BYP_PLL 0x0001
50#define SAA7706H_CTRL_PLL_DIV_MASK 0x003e
51#define SAA7706H_CTRL_PLL3_62975MHZ 0x003e
52#define SAA7706H_CTRL_DSP_TURBO 0x0040
53#define SAA7706H_CTRL_PC_RESET_DSP1 0x0080
54#define SAA7706H_CTRL_PC_RESET_DSP2 0x0100
55#define SAA7706H_CTRL_DSP1_ROM_EN_MASK 0x0600
56#define SAA7706H_CTRL_DSP1_FUNC_PROM 0x0000
57#define SAA7706H_CTRL_DSP2_ROM_EN_MASK 0x1800
58#define SAA7706H_CTRL_DSP2_FUNC_PROM 0x0000
59#define SAA7706H_CTRL_DIG_SIL_INTERPOL 0x8000
60
61#define SAA7706H_REG_EVALUATION 0x1ff0
62#define SAA7706H_EVAL_DISABLE_CHARGE_PUMP 0x000001
63#define SAA7706H_EVAL_DCS_CLOCK 0x000002
64#define SAA7706H_EVAL_GNDRC1_ENABLE 0x000004
65#define SAA7706H_EVAL_GNDRC2_ENABLE 0x000008
66
67#define SAA7706H_REG_CL_GEN1 0x1ff3
68#define SAA7706H_CL_GEN1_MIN_LOOPGAIN_MASK 0x00000f
69#define SAA7706H_CL_GEN1_LOOPGAIN_MASK 0x0000f0
70#define SAA7706H_CL_GEN1_COARSE_RATION 0xffff00
71
72#define SAA7706H_REG_CL_GEN2 0x1ff4
73#define SAA7706H_CL_GEN2_WSEDGE_FALLING 0x000001
74#define SAA7706H_CL_GEN2_STOP_VCO 0x000002
75#define SAA7706H_CL_GEN2_FRERUN 0x000004
76#define SAA7706H_CL_GEN2_ADAPTIVE 0x000008
77#define SAA7706H_CL_GEN2_FINE_RATIO_MASK 0x0ffff0
78
79#define SAA7706H_REG_CL_GEN4 0x1ff6
80#define SAA7706H_CL_GEN4_BYPASS_PLL1 0x001000
81#define SAA7706H_CL_GEN4_PLL1_DIV_MASK 0x03e000
82#define SAA7706H_CL_GEN4_DSP1_TURBO 0x040000
83
84#define SAA7706H_REG_SEL 0x1ff7
85#define SAA7706H_SEL_DSP2_SRCA_MASK 0x000007
86#define SAA7706H_SEL_DSP2_FMTA_MASK 0x000031
87#define SAA7706H_SEL_DSP2_SRCB_MASK 0x0001c0
88#define SAA7706H_SEL_DSP2_FMTB_MASK 0x000e00
89#define SAA7706H_SEL_DSP1_SRC_MASK 0x003000
90#define SAA7706H_SEL_DSP1_FMT_MASK 0x01c003
91#define SAA7706H_SEL_SPDIF2 0x020000
92#define SAA7706H_SEL_HOST_IO_FMT_MASK 0x1c0000
93#define SAA7706H_SEL_EN_HOST_IO 0x200000
94
95#define SAA7706H_REG_IAC 0x1ff8
96#define SAA7706H_REG_CLK_SET 0x1ff9
97#define SAA7706H_REG_CLK_COEFF 0x1ffa
98#define SAA7706H_REG_INPUT_SENS 0x1ffb
99#define SAA7706H_INPUT_SENS_RDS_VOL_MASK 0x0003f
100#define SAA7706H_INPUT_SENS_FM_VOL_MASK 0x00fc0
101#define SAA7706H_INPUT_SENS_FM_MPX 0x01000
102#define SAA7706H_INPUT_SENS_OFF_FILTER_A_EN 0x02000
103#define SAA7706H_INPUT_SENS_OFF_FILTER_B_EN 0x04000
104#define SAA7706H_REG_PHONE_NAV_AUDIO 0x1ffc
105#define SAA7706H_REG_IO_CONF_DSP2 0x1ffd
106#define SAA7706H_REG_STATUS_DSP2 0x1ffe
107#define SAA7706H_REG_PC_DSP2 0x1fff
108
109#define SAA7706H_DSP1_MOD0 0x0800
110#define SAA7706H_DSP1_ROM_VER 0x097f
111#define SAA7706H_DSP2_MPTR0 0x1000
112
113#define SAA7706H_DSP1_MODPNTR 0x0000
114
115#define SAA7706H_DSP2_XMEM_CONTLLCW 0x113e
116#define SAA7706H_DSP2_XMEM_BUSAMP 0x114a
117#define SAA7706H_DSP2_XMEM_FDACPNTR 0x11f9
118#define SAA7706H_DSP2_XMEM_IIS1PNTR 0x11fb
119
120#define SAA7706H_DSP2_YMEM_PVGA 0x212a
121#define SAA7706H_DSP2_YMEM_PVAT1 0x212b
122#define SAA7706H_DSP2_YMEM_PVAT 0x212c
123#define SAA7706H_DSP2_YMEM_ROM_VER 0x21ff
124
125#define SUPPORTED_DSP1_ROM_VER 0x667
126
127struct saa7706h_state {
128 struct v4l2_subdev sd;
129 unsigned muted;
130};
131
132static inline struct saa7706h_state *to_state(struct v4l2_subdev *sd)
133{
134 return container_of(sd, struct saa7706h_state, sd);
135}
136
137static int saa7706h_i2c_send(struct i2c_client *client, const u8 *data, int len)
138{
139 int err = i2c_master_send(client, data, len);
140 if (err == len)
141 return 0;
142 return err > 0 ? -EIO : err;
143}
144
145static int saa7706h_i2c_transfer(struct i2c_client *client,
146 struct i2c_msg *msgs, int num)
147{
148 int err = i2c_transfer(client->adapter, msgs, num);
149 if (err == num)
150 return 0;
151 return err > 0 ? -EIO : err;
152}
153
154static int saa7706h_set_reg24(struct v4l2_subdev *sd, u16 reg, u32 val)
155{
156 struct i2c_client *client = v4l2_get_subdevdata(sd);
157 u8 buf[5];
158 int pos = 0;
159
160 buf[pos++] = reg >> 8;
161 buf[pos++] = reg;
162 buf[pos++] = val >> 16;
163 buf[pos++] = val >> 8;
164 buf[pos++] = val;
165
166 return saa7706h_i2c_send(client, buf, pos);
167}
168
169static int saa7706h_set_reg24_err(struct v4l2_subdev *sd, u16 reg, u32 val,
170 int *err)
171{
172 return *err ? *err : saa7706h_set_reg24(sd, reg, val);
173}
174
175static int saa7706h_set_reg16(struct v4l2_subdev *sd, u16 reg, u16 val)
176{
177 struct i2c_client *client = v4l2_get_subdevdata(sd);
178 u8 buf[4];
179 int pos = 0;
180
181 buf[pos++] = reg >> 8;
182 buf[pos++] = reg;
183 buf[pos++] = val >> 8;
184 buf[pos++] = val;
185
186 return saa7706h_i2c_send(client, buf, pos);
187}
188
189static int saa7706h_set_reg16_err(struct v4l2_subdev *sd, u16 reg, u16 val,
190 int *err)
191{
192 return *err ? *err : saa7706h_set_reg16(sd, reg, val);
193}
194
195static int saa7706h_get_reg16(struct v4l2_subdev *sd, u16 reg)
196{
197 struct i2c_client *client = v4l2_get_subdevdata(sd);
198 u8 buf[2];
199 int err;
200 u8 regaddr[] = {reg >> 8, reg};
201 struct i2c_msg msg[] = { {client->addr, 0, sizeof(regaddr), regaddr},
202 {client->addr, I2C_M_RD, sizeof(buf), buf} };
203
204 err = saa7706h_i2c_transfer(client, msg, ARRAY_SIZE(msg));
205 if (err)
206 return err;
207
208 return buf[0] << 8 | buf[1];
209}
210
211static int saa7706h_unmute(struct v4l2_subdev *sd)
212{
213 struct saa7706h_state *state = to_state(sd);
214 int err = 0;
215
216 err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL,
217 SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 |
218 SAA7706H_CTRL_PC_RESET_DSP2, &err);
219
220 /* newer versions of the chip requires a small sleep after reset */
221 msleep(1);
222
223 err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL,
224 SAA7706H_CTRL_PLL3_62975MHZ, &err);
225
226 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_EVALUATION, 0, &err);
227
228 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN1, 0x040022, &err);
229
230 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN2,
231 SAA7706H_CL_GEN2_WSEDGE_FALLING, &err);
232
233 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN4, 0x024080, &err);
234
235 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_SEL, 0x200080, &err);
236
237 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IAC, 0xf4caed, &err);
238
239 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_SET, 0x124334, &err);
240
241 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_COEFF, 0x004a1a,
242 &err);
243
244 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_INPUT_SENS, 0x0071c7,
245 &err);
246
247 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PHONE_NAV_AUDIO,
248 0x0e22ff, &err);
249
250 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IO_CONF_DSP2, 0x001ff8,
251 &err);
252
253 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_STATUS_DSP2, 0x080003,
254 &err);
255
256 err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PC_DSP2, 0x000004, &err);
257
258 err = saa7706h_set_reg16_err(sd, SAA7706H_DSP1_MOD0, 0x0c6c, &err);
259
260 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_MPTR0, 0x000b4b, &err);
261
262 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x000600, &err);
263
264 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x0000c0, &err);
265
266 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000819,
267 &err);
268
269 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x00085a,
270 &err);
271
272 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_BUSAMP, 0x7fffff,
273 &err);
274
275 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_FDACPNTR, 0x2000cb,
276 &err);
277
278 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_IIS1PNTR, 0x2000cb,
279 &err);
280
281 err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVGA, 0x0f80, &err);
282
283 err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT1, 0x0800,
284 &err);
285
286 err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT, 0x0800, &err);
287
288 err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000905,
289 &err);
290 if (!err)
291 state->muted = 0;
292 return err;
293}
294
295static int saa7706h_mute(struct v4l2_subdev *sd)
296{
297 struct saa7706h_state *state = to_state(sd);
298 int err;
299
300 err = saa7706h_set_reg16(sd, SAA7706H_REG_CTRL,
301 SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 |
302 SAA7706H_CTRL_PC_RESET_DSP2);
303 if (!err)
304 state->muted = 1;
305 return err;
306}
307
308static int saa7706h_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
309{
310 switch (qc->id) {
311 case V4L2_CID_AUDIO_MUTE:
312 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
313 }
314 return -EINVAL;
315}
316
317static int saa7706h_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
318{
319 struct saa7706h_state *state = to_state(sd);
320
321 switch (ctrl->id) {
322 case V4L2_CID_AUDIO_MUTE:
323 ctrl->value = state->muted;
324 return 0;
325 }
326 return -EINVAL;
327}
328
329static int saa7706h_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
330{
331 switch (ctrl->id) {
332 case V4L2_CID_AUDIO_MUTE:
333 if (ctrl->value)
334 return saa7706h_mute(sd);
335 return saa7706h_unmute(sd);
336 }
337 return -EINVAL;
338}
339
340static int saa7706h_g_chip_ident(struct v4l2_subdev *sd,
341 struct v4l2_dbg_chip_ident *chip)
342{
343 struct i2c_client *client = v4l2_get_subdevdata(sd);
344
345 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7706H, 0);
346}
347
348static const struct v4l2_subdev_core_ops saa7706h_core_ops = {
349 .g_chip_ident = saa7706h_g_chip_ident,
350 .queryctrl = saa7706h_queryctrl,
351 .g_ctrl = saa7706h_g_ctrl,
352 .s_ctrl = saa7706h_s_ctrl,
353};
354
355static const struct v4l2_subdev_ops saa7706h_ops = {
356 .core = &saa7706h_core_ops,
357};
358
359/*
360 * Generic i2c probe
361 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
362 */
363
364static int __devinit saa7706h_probe(struct i2c_client *client,
365 const struct i2c_device_id *id)
366{
367 struct saa7706h_state *state;
368 struct v4l2_subdev *sd;
369 int err;
370
371 /* Check if the adapter supports the needed features */
372 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
373 return -EIO;
374
375 v4l_info(client, "chip found @ 0x%02x (%s)\n",
376 client->addr << 1, client->adapter->name);
377
378 state = kmalloc(sizeof(struct saa7706h_state), GFP_KERNEL);
379 if (state == NULL)
380 return -ENOMEM;
381 sd = &state->sd;
382 v4l2_i2c_subdev_init(sd, client, &saa7706h_ops);
383
384 /* check the rom versions */
385 err = saa7706h_get_reg16(sd, SAA7706H_DSP1_ROM_VER);
386 if (err < 0)
387 goto err;
388 if (err != SUPPORTED_DSP1_ROM_VER)
389 v4l2_warn(sd, "Unknown DSP1 ROM code version: 0x%x\n", err);
390
391 state->muted = 1;
392
393 /* startup in a muted state */
394 err = saa7706h_mute(sd);
395 if (err)
396 goto err;
397
398 return 0;
399
400err:
401 v4l2_device_unregister_subdev(sd);
402 kfree(to_state(sd));
403
404 printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", err);
405
406 return err;
407}
408
409static int __devexit saa7706h_remove(struct i2c_client *client)
410{
411 struct v4l2_subdev *sd = i2c_get_clientdata(client);
412
413 saa7706h_mute(sd);
414 v4l2_device_unregister_subdev(sd);
415 kfree(to_state(sd));
416 return 0;
417}
418
419static const struct i2c_device_id saa7706h_id[] = {
420 {DRIVER_NAME, 0},
421 {},
422};
423
424MODULE_DEVICE_TABLE(i2c, saa7706h_id);
425
426static struct i2c_driver saa7706h_driver = {
427 .driver = {
428 .owner = THIS_MODULE,
429 .name = DRIVER_NAME,
430 },
431 .probe = saa7706h_probe,
432 .remove = saa7706h_remove,
433 .id_table = saa7706h_id,
434};
435
436static __init int saa7706h_init(void)
437{
438 return i2c_add_driver(&saa7706h_driver);
439}
440
441static __exit void saa7706h_exit(void)
442{
443 i2c_del_driver(&saa7706h_driver);
444}
445
446module_init(saa7706h_init);
447module_exit(saa7706h_exit);
448
449MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver");
450MODULE_AUTHOR("Mocean Laboratories");
451MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 4da0f150c6e2..47075fc71f11 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -724,7 +724,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
724 tuner->audmode = V4L2_TUNER_MODE_MONO; 724 tuner->audmode = V4L2_TUNER_MODE_MONO;
725 725
726 /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ 726 /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
727 /* measured in units of dbV in 1 db increments (max at ~75 dbV) */ 727 /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
728 tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); 728 tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
729 /* the ideal factor is 0xffff/75 = 873,8 */ 729 /* the ideal factor is 0xffff/75 = 873,8 */
730 tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); 730 tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index a96e1b9dd646..6f60841828da 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -590,8 +590,9 @@ int si470x_fops_release(struct file *file)
590 video_unregister_device(radio->videodev); 590 video_unregister_device(radio->videodev);
591 kfree(radio->int_in_buffer); 591 kfree(radio->int_in_buffer);
592 kfree(radio->buffer); 592 kfree(radio->buffer);
593 mutex_unlock(&radio->disconnect_lock);
593 kfree(radio); 594 kfree(radio);
594 goto unlock; 595 goto done;
595 } 596 }
596 597
597 /* cancel read processes */ 598 /* cancel read processes */
@@ -601,7 +602,6 @@ int si470x_fops_release(struct file *file)
601 retval = si470x_stop(radio); 602 retval = si470x_stop(radio);
602 usb_autopm_put_interface(radio->intf); 603 usb_autopm_put_interface(radio->intf);
603 } 604 }
604unlock:
605 mutex_unlock(&radio->disconnect_lock); 605 mutex_unlock(&radio->disconnect_lock);
606done: 606done:
607 return retval; 607 return retval;
@@ -842,9 +842,11 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf)
842 kfree(radio->int_in_buffer); 842 kfree(radio->int_in_buffer);
843 video_unregister_device(radio->videodev); 843 video_unregister_device(radio->videodev);
844 kfree(radio->buffer); 844 kfree(radio->buffer);
845 mutex_unlock(&radio->disconnect_lock);
845 kfree(radio); 846 kfree(radio);
847 } else {
848 mutex_unlock(&radio->disconnect_lock);
846 } 849 }
847 mutex_unlock(&radio->disconnect_lock);
848} 850}
849 851
850 852