aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/radio/Kconfig17
-rw-r--r--drivers/media/radio/Makefile3
-rw-r--r--drivers/media/radio/radio-shark2.c348
-rw-r--r--drivers/media/radio/radio-tea5777.c489
-rw-r--r--drivers/media/radio/radio-tea5777.h87
5 files changed, 944 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index e3c117122351..8090b87b3066 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -74,6 +74,23 @@ config RADIO_SHARK
74 To compile this driver as a module, choose M here: the 74 To compile this driver as a module, choose M here: the
75 module will be called radio-shark. 75 module will be called radio-shark.
76 76
77config RADIO_SHARK2
78 tristate "Griffin radioSHARK2 USB radio receiver"
79 depends on USB
80 ---help---
81 Choose Y here if you have this radio receiver.
82
83 There are 2 versions of this device, this driver is for version 2,
84 which is black.
85
86 In order to control your radio card, you will need to use programs
87 that are compatible with the Video For Linux API. Information on
88 this API and pointers to "v4l" programs may be found at
89 <file:Documentation/video4linux/API.html>.
90
91 To compile this driver as a module, choose M here: the
92 module will be called radio-shark2.
93
77config I2C_SI4713 94config I2C_SI4713
78 tristate "I2C driver for Silicon Labs Si4713 device" 95 tristate "I2C driver for Silicon Labs Si4713 device"
79 depends on I2C && VIDEO_V4L2 96 depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index e03b258e18d4..c03ce4fe74e9 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
12obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o 12obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
13obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o 13obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
14obj-$(CONFIG_RADIO_SHARK) += radio-shark.o 14obj-$(CONFIG_RADIO_SHARK) += radio-shark.o
15obj-$(CONFIG_RADIO_SHARK2) += shark2.o
15obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o 16obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
16obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o 17obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
17obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o 18obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
@@ -30,4 +31,6 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
30obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o 31obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
31obj-$(CONFIG_RADIO_WL128X) += wl128x/ 32obj-$(CONFIG_RADIO_WL128X) += wl128x/
32 33
34shark2-objs := radio-shark2.o radio-tea5777.o
35
33ccflags-y += -Isound 36ccflags-y += -Isound
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
new file mode 100644
index 000000000000..b9575de3e7e8
--- /dev/null
+++ b/drivers/media/radio/radio-shark2.c
@@ -0,0 +1,348 @@
1/*
2 * Linux V4L2 radio driver for the Griffin radioSHARK2 USB radio receiver
3 *
4 * Note the radioSHARK2 offers the audio through a regular USB audio device,
5 * this driver only handles the tuning.
6 *
7 * The info necessary to drive the shark2 was taken from the small userspace
8 * shark2.c program by Hisaaki Shibata, which he kindly placed in the Public
9 * Domain.
10 *
11 * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28#include <linux/init.h>
29#include <linux/kernel.h>
30#include <linux/leds.h>
31#include <linux/module.h>
32#include <linux/slab.h>
33#include <linux/usb.h>
34#include <linux/workqueue.h>
35#include <media/v4l2-device.h>
36#include "radio-tea5777.h"
37
38MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
39MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver");
40MODULE_LICENSE("GPL");
41
42static int debug;
43module_param(debug, int, 0);
44MODULE_PARM_DESC(debug, "Debug level (0-1)");
45
46
47#define SHARK_IN_EP 0x83
48#define SHARK_OUT_EP 0x05
49
50#define TB_LEN 7
51#define DRV_NAME "radioshark2"
52
53#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
54
55enum { BLUE_LED, RED_LED, NO_LEDS };
56
57static void shark_led_set_blue(struct led_classdev *led_cdev,
58 enum led_brightness value);
59static void shark_led_set_red(struct led_classdev *led_cdev,
60 enum led_brightness value);
61
62static const struct led_classdev shark_led_templates[NO_LEDS] = {
63 [BLUE_LED] = {
64 .name = "%s:blue:",
65 .brightness = LED_OFF,
66 .max_brightness = 127,
67 .brightness_set = shark_led_set_blue,
68 },
69 [RED_LED] = {
70 .name = "%s:red:",
71 .brightness = LED_OFF,
72 .max_brightness = 1,
73 .brightness_set = shark_led_set_red,
74 },
75};
76
77struct shark_device {
78 struct usb_device *usbdev;
79 struct v4l2_device v4l2_dev;
80 struct radio_tea5777 tea;
81
82 struct work_struct led_work;
83 struct led_classdev leds[NO_LEDS];
84 char led_names[NO_LEDS][32];
85 atomic_t brightness[NO_LEDS];
86 unsigned long brightness_new;
87
88 u8 *transfer_buffer;
89};
90
91static atomic_t shark_instance = ATOMIC_INIT(0);
92
93static int shark_write_reg(struct radio_tea5777 *tea, u64 reg)
94{
95 struct shark_device *shark = tea->private_data;
96 int i, res, actual_len;
97
98 memset(shark->transfer_buffer, 0, TB_LEN);
99 shark->transfer_buffer[0] = 0x81; /* Write register command */
100 for (i = 0; i < 6; i++)
101 shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff;
102
103 v4l2_dbg(1, debug, tea->v4l2_dev,
104 "shark2-write: %02x %02x %02x %02x %02x %02x %02x\n",
105 shark->transfer_buffer[0], shark->transfer_buffer[1],
106 shark->transfer_buffer[2], shark->transfer_buffer[3],
107 shark->transfer_buffer[4], shark->transfer_buffer[5],
108 shark->transfer_buffer[6]);
109
110 res = usb_interrupt_msg(shark->usbdev,
111 usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
112 shark->transfer_buffer, TB_LEN,
113 &actual_len, 1000);
114 if (res < 0) {
115 v4l2_err(tea->v4l2_dev, "write error: %d\n", res);
116 return res;
117 }
118
119 return 0;
120}
121
122static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
123{
124 struct shark_device *shark = tea->private_data;
125 int i, res, actual_len;
126 u32 reg = 0;
127
128 memset(shark->transfer_buffer, 0, TB_LEN);
129 shark->transfer_buffer[0] = 0x82;
130 res = usb_interrupt_msg(shark->usbdev,
131 usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
132 shark->transfer_buffer, TB_LEN,
133 &actual_len, 1000);
134 if (res < 0) {
135 v4l2_err(tea->v4l2_dev, "request-read error: %d\n", res);
136 return res;
137 }
138
139 res = usb_interrupt_msg(shark->usbdev,
140 usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
141 shark->transfer_buffer, TB_LEN,
142 &actual_len, 1000);
143 if (res < 0) {
144 v4l2_err(tea->v4l2_dev, "read error: %d\n", res);
145 return res;
146 }
147
148 for (i = 0; i < 3; i++)
149 reg |= shark->transfer_buffer[i] << (16 - i * 8);
150
151 v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %02x %02x %02x\n",
152 shark->transfer_buffer[0], shark->transfer_buffer[1],
153 shark->transfer_buffer[2]);
154
155 *reg_ret = reg;
156 return 0;
157}
158
159static struct radio_tea5777_ops shark_tea_ops = {
160 .write_reg = shark_write_reg,
161 .read_reg = shark_read_reg,
162};
163
164static void shark_led_work(struct work_struct *work)
165{
166 struct shark_device *shark =
167 container_of(work, struct shark_device, led_work);
168 int i, res, brightness, actual_len;
169 /*
170 * We use the v4l2_dev lock and registered bit to ensure the device
171 * does not get unplugged and unreffed while we're running.
172 */
173 mutex_lock(&shark->tea.mutex);
174 if (!video_is_registered(&shark->tea.vd))
175 goto leave;
176
177 for (i = 0; i < 2; i++) {
178 if (!test_and_clear_bit(i, &shark->brightness_new))
179 continue;
180
181 brightness = atomic_read(&shark->brightness[i]);
182 memset(shark->transfer_buffer, 0, TB_LEN);
183 shark->transfer_buffer[0] = 0x83 + i;
184 shark->transfer_buffer[1] = brightness;
185 res = usb_interrupt_msg(shark->usbdev,
186 usb_sndintpipe(shark->usbdev,
187 SHARK_OUT_EP),
188 shark->transfer_buffer, TB_LEN,
189 &actual_len, 1000);
190 if (res < 0)
191 v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
192 shark->led_names[i], res);
193 }
194leave:
195 mutex_unlock(&shark->tea.mutex);
196}
197
198static void shark_led_set_blue(struct led_classdev *led_cdev,
199 enum led_brightness value)
200{
201 struct shark_device *shark =
202 container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
203
204 atomic_set(&shark->brightness[BLUE_LED], value);
205 set_bit(BLUE_LED, &shark->brightness_new);
206 schedule_work(&shark->led_work);
207}
208
209static void shark_led_set_red(struct led_classdev *led_cdev,
210 enum led_brightness value)
211{
212 struct shark_device *shark =
213 container_of(led_cdev, struct shark_device, leds[RED_LED]);
214
215 atomic_set(&shark->brightness[RED_LED], value);
216 set_bit(RED_LED, &shark->brightness_new);
217 schedule_work(&shark->led_work);
218}
219
220static void usb_shark_disconnect(struct usb_interface *intf)
221{
222 struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
223 struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
224 int i;
225
226 mutex_lock(&shark->tea.mutex);
227 v4l2_device_disconnect(&shark->v4l2_dev);
228 radio_tea5777_exit(&shark->tea);
229 mutex_unlock(&shark->tea.mutex);
230
231 for (i = 0; i < NO_LEDS; i++)
232 led_classdev_unregister(&shark->leds[i]);
233
234 v4l2_device_put(&shark->v4l2_dev);
235}
236
237static void usb_shark_release(struct v4l2_device *v4l2_dev)
238{
239 struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
240
241 cancel_work_sync(&shark->led_work);
242 v4l2_device_unregister(&shark->v4l2_dev);
243 kfree(shark->transfer_buffer);
244 kfree(shark);
245}
246
247static int usb_shark_probe(struct usb_interface *intf,
248 const struct usb_device_id *id)
249{
250 struct shark_device *shark;
251 int i, retval = -ENOMEM;
252
253 shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
254 if (!shark)
255 return retval;
256
257 shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
258 if (!shark->transfer_buffer)
259 goto err_alloc_buffer;
260
261 /*
262 * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
263 * pointer in intfdata causing v4l2-device.c to not set it. Which
264 * results in usb_shark_disconnect() referencing the dangling pointer
265 *
266 * REMOVE (as soon as the above bug is fixed, patch submitted)
267 */
268 usb_set_intfdata(intf, NULL);
269
270 shark->v4l2_dev.release = usb_shark_release;
271 v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
272 retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
273 if (retval) {
274 v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
275 goto err_reg_dev;
276 }
277
278 shark->usbdev = interface_to_usbdev(intf);
279 shark->tea.v4l2_dev = &shark->v4l2_dev;
280 shark->tea.private_data = shark;
281 shark->tea.ops = &shark_tea_ops;
282 shark->tea.has_am = true;
283 shark->tea.write_before_read = true;
284 strlcpy(shark->tea.card, "Griffin radioSHARK2",
285 sizeof(shark->tea.card));
286 usb_make_path(shark->usbdev, shark->tea.bus_info,
287 sizeof(shark->tea.bus_info));
288
289 retval = radio_tea5777_init(&shark->tea, THIS_MODULE);
290 if (retval) {
291 v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n");
292 goto err_init_tea;
293 }
294
295 INIT_WORK(&shark->led_work, shark_led_work);
296 for (i = 0; i < NO_LEDS; i++) {
297 shark->leds[i] = shark_led_templates[i];
298 snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
299 shark->leds[i].name, shark->v4l2_dev.name);
300 shark->leds[i].name = shark->led_names[i];
301 /*
302 * We don't fail the probe if we fail to register the leds,
303 * because once we've called radio_tea5777_init, the /dev/radio0
304 * node may be opened from userspace holding a reference to us!
305 *
306 * Note we cannot register the leds first instead as
307 * shark_led_work depends on the v4l2 mutex and registered bit.
308 */
309 retval = led_classdev_register(&intf->dev, &shark->leds[i]);
310 if (retval)
311 v4l2_err(&shark->v4l2_dev,
312 "couldn't register led: %s\n",
313 shark->led_names[i]);
314 }
315
316 return 0;
317
318err_init_tea:
319 v4l2_device_unregister(&shark->v4l2_dev);
320err_reg_dev:
321 kfree(shark->transfer_buffer);
322err_alloc_buffer:
323 kfree(shark);
324
325 return retval;
326}
327
328/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
329static struct usb_device_id usb_shark_device_table[] = {
330 { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
331 USB_DEVICE_ID_MATCH_INT_CLASS,
332 .idVendor = 0x077d,
333 .idProduct = 0x627a,
334 .bcdDevice_lo = 0x0010,
335 .bcdDevice_hi = 0x0010,
336 .bInterfaceClass = 3,
337 },
338 { }
339};
340MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
341
342static struct usb_driver usb_shark_driver = {
343 .name = DRV_NAME,
344 .probe = usb_shark_probe,
345 .disconnect = usb_shark_disconnect,
346 .id_table = usb_shark_device_table,
347};
348module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c
new file mode 100644
index 000000000000..3e12179364f8
--- /dev/null
+++ b/drivers/media/radio/radio-tea5777.c
@@ -0,0 +1,489 @@
1/*
2 * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
3 *
4 * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
5 *
6 * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
7 *
8 * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26#include <linux/delay.h>
27#include <linux/init.h>
28#include <linux/module.h>
29#include <linux/sched.h>
30#include <linux/slab.h>
31#include <media/v4l2-device.h>
32#include <media/v4l2-dev.h>
33#include <media/v4l2-fh.h>
34#include <media/v4l2-ioctl.h>
35#include <media/v4l2-event.h>
36#include "radio-tea5777.h"
37
38MODULE_AUTHOR("Hans de Goede <perex@perex.cz>");
39MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
40MODULE_LICENSE("GPL");
41
42/* Fixed FM only band for now, will implement multi-band support when the
43 VIDIOC_ENUM_FREQ_BANDS API is upstream */
44#define TEA5777_FM_RANGELOW (76000 * 16)
45#define TEA5777_FM_RANGEHIGH (108000 * 16)
46
47#define TEA5777_FM_IF 150 /* kHz */
48#define TEA5777_FM_FREQ_STEP 50 /* kHz */
49
50/* Write reg, common bits */
51#define TEA5777_W_MUTE_MASK (1LL << 47)
52#define TEA5777_W_MUTE_SHIFT 47
53#define TEA5777_W_AM_FM_MASK (1LL << 46)
54#define TEA5777_W_AM_FM_SHIFT 46
55#define TEA5777_W_STB_MASK (1LL << 45)
56#define TEA5777_W_STB_SHIFT 45
57
58#define TEA5777_W_IFCE_MASK (1LL << 29)
59#define TEA5777_W_IFCE_SHIFT 29
60#define TEA5777_W_IFW_MASK (1LL << 28)
61#define TEA5777_W_IFW_SHIFT 28
62#define TEA5777_W_HILO_MASK (1LL << 27)
63#define TEA5777_W_HILO_SHIFT 27
64#define TEA5777_W_DBUS_MASK (1LL << 26)
65#define TEA5777_W_DBUS_SHIFT 26
66
67#define TEA5777_W_INTEXT_MASK (1LL << 24)
68#define TEA5777_W_INTEXT_SHIFT 24
69#define TEA5777_W_P1_MASK (1LL << 23)
70#define TEA5777_W_P1_SHIFT 23
71#define TEA5777_W_P0_MASK (1LL << 22)
72#define TEA5777_W_P0_SHIFT 22
73#define TEA5777_W_PEN1_MASK (1LL << 21)
74#define TEA5777_W_PEN1_SHIFT 21
75#define TEA5777_W_PEN0_MASK (1LL << 20)
76#define TEA5777_W_PEN0_SHIFT 20
77
78#define TEA5777_W_CHP0_MASK (1LL << 18)
79#define TEA5777_W_CHP0_SHIFT 18
80#define TEA5777_W_DEEM_MASK (1LL << 17)
81#define TEA5777_W_DEEM_SHIFT 17
82
83#define TEA5777_W_SEARCH_MASK (1LL << 7)
84#define TEA5777_W_SEARCH_SHIFT 7
85#define TEA5777_W_PROGBLIM_MASK (1LL << 6)
86#define TEA5777_W_PROGBLIM_SHIFT 6
87#define TEA5777_W_UPDWN_MASK (1LL << 5)
88#define TEA5777_W_UPDWN_SHIFT 5
89#define TEA5777_W_SLEV_MASK (3LL << 3)
90#define TEA5777_W_SLEV_SHIFT 3
91
92/* Write reg, FM specific bits */
93#define TEA5777_W_FM_PLL_MASK (0x1fffLL << 32)
94#define TEA5777_W_FM_PLL_SHIFT 32
95#define TEA5777_W_FM_FREF_MASK (0x03LL << 30)
96#define TEA5777_W_FM_FREF_SHIFT 30
97#define TEA5777_W_FM_FREF_VALUE 0 /* 50 kHz tune steps, 150 kHz IF */
98
99#define TEA5777_W_FM_FORCEMONO_MASK (1LL << 15)
100#define TEA5777_W_FM_FORCEMONO_SHIFT 15
101#define TEA5777_W_FM_SDSOFF_MASK (1LL << 14)
102#define TEA5777_W_FM_SDSOFF_SHIFT 14
103#define TEA5777_W_FM_DOFF_MASK (1LL << 13)
104#define TEA5777_W_FM_DOFF_SHIFT 13
105
106#define TEA5777_W_FM_STEP_MASK (3LL << 1)
107#define TEA5777_W_FM_STEP_SHIFT 1
108
109/* Write reg, AM specific bits */
110#define TEA5777_W_AM_PLL_MASK (0x7ffLL << 34)
111#define TEA5777_W_AM_PLL_SHIFT 34
112#define TEA5777_W_AM_AGCRF_MASK (1LL << 33)
113#define TEA5777_W_AM_AGCRF_SHIFT 33
114#define TEA5777_W_AM_AGCIF_MASK (1LL << 32)
115#define TEA5777_W_AM_AGCIF_SHIFT 32
116#define TEA5777_W_AM_MWLW_MASK (1LL << 31)
117#define TEA5777_W_AM_MWLW_SHIFT 31
118#define TEA5777_W_AM_LNA_MASK (1LL << 30)
119#define TEA5777_W_AM_LNA_SHIFT 30
120
121#define TEA5777_W_AM_PEAK_MASK (1LL << 25)
122#define TEA5777_W_AM_PEAK_SHIFT 25
123
124#define TEA5777_W_AM_RFB_MASK (1LL << 16)
125#define TEA5777_W_AM_RFB_SHIFT 16
126#define TEA5777_W_AM_CALLIGN_MASK (1LL << 15)
127#define TEA5777_W_AM_CALLIGN_SHIFT 15
128#define TEA5777_W_AM_CBANK_MASK (0x7fLL << 8)
129#define TEA5777_W_AM_CBANK_SHIFT 8
130
131#define TEA5777_W_AM_DELAY_MASK (1LL << 2)
132#define TEA5777_W_AM_DELAY_SHIFT 2
133#define TEA5777_W_AM_STEP_MASK (1LL << 1)
134#define TEA5777_W_AM_STEP_SHIFT 1
135
136/* Read reg, common bits */
137#define TEA5777_R_LEVEL_MASK (0x0f << 17)
138#define TEA5777_R_LEVEL_SHIFT 17
139#define TEA5777_R_SFOUND_MASK (0x01 << 16)
140#define TEA5777_R_SFOUND_SHIFT 16
141#define TEA5777_R_BLIM_MASK (0x01 << 15)
142#define TEA5777_R_BLIM_SHIFT 15
143
144/* Read reg, FM specific bits */
145#define TEA5777_R_FM_STEREO_MASK (0x01 << 21)
146#define TEA5777_R_FM_STEREO_SHIFT 21
147#define TEA5777_R_FM_PLL_MASK 0x1fff
148#define TEA5777_R_FM_PLL_SHIFT 0
149
150static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
151{
152 return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
153}
154
155static int radio_tea5777_set_freq(struct radio_tea5777 *tea)
156{
157 u64 freq;
158 int res;
159
160 freq = clamp_t(u32, tea->freq,
161 TEA5777_FM_RANGELOW, TEA5777_FM_RANGEHIGH);
162 freq = (freq + 8) / 16; /* to kHz */
163
164 freq = (freq - TEA5777_FM_IF) / TEA5777_FM_FREQ_STEP;
165
166 tea->write_reg &= ~(TEA5777_W_FM_PLL_MASK | TEA5777_W_FM_FREF_MASK);
167 tea->write_reg |= freq << TEA5777_W_FM_PLL_SHIFT;
168 tea->write_reg |= TEA5777_W_FM_FREF_VALUE << TEA5777_W_FM_FREF_SHIFT;
169
170 res = tea->ops->write_reg(tea, tea->write_reg);
171 if (res)
172 return res;
173
174 tea->needs_write = false;
175 tea->read_reg = -1;
176 tea->freq = tea5777_freq_to_v4l2_freq(tea, freq);
177
178 return 0;
179}
180
181static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait)
182{
183 int res;
184
185 if (tea->read_reg != -1)
186 return 0;
187
188 if (tea->write_before_read && tea->needs_write) {
189 res = radio_tea5777_set_freq(tea);
190 if (res)
191 return res;
192 }
193
194 if (wait) {
195 if (schedule_timeout_interruptible(msecs_to_jiffies(wait)))
196 return -ERESTARTSYS;
197 }
198
199 res = tea->ops->read_reg(tea, &tea->read_reg);
200 if (res)
201 return res;
202
203 tea->needs_write = true;
204 return 0;
205}
206
207/*
208 * Linux Video interface
209 */
210
211static int vidioc_querycap(struct file *file, void *priv,
212 struct v4l2_capability *v)
213{
214 struct radio_tea5777 *tea = video_drvdata(file);
215
216 strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
217 strlcpy(v->card, tea->card, sizeof(v->card));
218 strlcat(v->card, " TEA5777", sizeof(v->card));
219 strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
220 v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
221 v->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
222 v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
223 return 0;
224}
225
226static int vidioc_g_tuner(struct file *file, void *priv,
227 struct v4l2_tuner *v)
228{
229 struct radio_tea5777 *tea = video_drvdata(file);
230 int res;
231
232 if (v->index > 0)
233 return -EINVAL;
234
235 res = radio_tea5777_update_read_reg(tea, 0);
236 if (res)
237 return res;
238
239 memset(v, 0, sizeof(*v));
240 if (tea->has_am)
241 strlcpy(v->name, "AM/FM", sizeof(v->name));
242 else
243 strlcpy(v->name, "FM", sizeof(v->name));
244 v->type = V4L2_TUNER_RADIO;
245 v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
246 V4L2_TUNER_CAP_HWSEEK_BOUNDED;
247 v->rangelow = TEA5777_FM_RANGELOW;
248 v->rangehigh = TEA5777_FM_RANGEHIGH;
249 v->rxsubchans = (tea->read_reg & TEA5777_R_FM_STEREO_MASK) ?
250 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
251 v->audmode = (tea->write_reg & TEA5777_W_FM_FORCEMONO_MASK) ?
252 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
253 /* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
254 v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
255 (TEA5777_R_LEVEL_SHIFT - 12);
256
257 /* Invalidate read_reg, so that next call we return up2date signal */
258 tea->read_reg = -1;
259
260 return 0;
261}
262
263static int vidioc_s_tuner(struct file *file, void *priv,
264 struct v4l2_tuner *v)
265{
266 struct radio_tea5777 *tea = video_drvdata(file);
267
268 if (v->index)
269 return -EINVAL;
270
271 if (v->audmode == V4L2_TUNER_MODE_MONO)
272 tea->write_reg |= TEA5777_W_FM_FORCEMONO_MASK;
273 else
274 tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
275
276 return radio_tea5777_set_freq(tea);
277}
278
279static int vidioc_g_frequency(struct file *file, void *priv,
280 struct v4l2_frequency *f)
281{
282 struct radio_tea5777 *tea = video_drvdata(file);
283
284 if (f->tuner != 0)
285 return -EINVAL;
286 f->type = V4L2_TUNER_RADIO;
287 f->frequency = tea->freq;
288 return 0;
289}
290
291static int vidioc_s_frequency(struct file *file, void *priv,
292 struct v4l2_frequency *f)
293{
294 struct radio_tea5777 *tea = video_drvdata(file);
295
296 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
297 return -EINVAL;
298
299 tea->freq = f->frequency;
300 return radio_tea5777_set_freq(tea);
301}
302
303static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
304 struct v4l2_hw_freq_seek *a)
305{
306 struct radio_tea5777 *tea = video_drvdata(file);
307 u32 orig_freq = tea->freq;
308 unsigned long timeout;
309 int res, spacing = 200 * 16; /* 200 kHz */
310 /* These are fixed *for now* */
311 const u32 seek_rangelow = TEA5777_FM_RANGELOW;
312 const u32 seek_rangehigh = TEA5777_FM_RANGEHIGH;
313
314 if (a->tuner || a->wrap_around)
315 return -EINVAL;
316
317 tea->write_reg |= TEA5777_W_PROGBLIM_MASK;
318 if (seek_rangelow != tea->seek_rangelow) {
319 tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
320 tea->freq = seek_rangelow;
321 res = radio_tea5777_set_freq(tea);
322 if (res)
323 goto leave;
324 tea->seek_rangelow = tea->freq;
325 }
326 if (seek_rangehigh != tea->seek_rangehigh) {
327 tea->write_reg |= TEA5777_W_UPDWN_MASK;
328 tea->freq = seek_rangehigh;
329 res = radio_tea5777_set_freq(tea);
330 if (res)
331 goto leave;
332 tea->seek_rangehigh = tea->freq;
333 }
334 tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
335
336 tea->write_reg |= TEA5777_W_SEARCH_MASK;
337 if (a->seek_upward) {
338 tea->write_reg |= TEA5777_W_UPDWN_MASK;
339 tea->freq = orig_freq + spacing;
340 } else {
341 tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
342 tea->freq = orig_freq - spacing;
343 }
344 res = radio_tea5777_set_freq(tea);
345 if (res)
346 goto leave;
347
348 timeout = jiffies + msecs_to_jiffies(5000);
349 for (;;) {
350 if (time_after(jiffies, timeout)) {
351 res = -ENODATA;
352 break;
353 }
354
355 res = radio_tea5777_update_read_reg(tea, 100);
356 if (res)
357 break;
358
359 /*
360 * Note we use tea->freq to track how far we've searched sofar
361 * this is necessary to ensure we continue seeking at the right
362 * point, in the write_before_read case.
363 */
364 tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK);
365 tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq);
366
367 if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) {
368 tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
369 return 0;
370 }
371
372 if (tea->read_reg & TEA5777_R_BLIM_MASK) {
373 res = -ENODATA;
374 break;
375 }
376
377 /* Force read_reg update */
378 tea->read_reg = -1;
379 }
380leave:
381 tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
382 tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
383 tea->freq = orig_freq;
384 radio_tea5777_set_freq(tea);
385 return res;
386}
387
388static int tea575x_s_ctrl(struct v4l2_ctrl *c)
389{
390 struct radio_tea5777 *tea =
391 container_of(c->handler, struct radio_tea5777, ctrl_handler);
392
393 switch (c->id) {
394 case V4L2_CID_AUDIO_MUTE:
395 if (c->val)
396 tea->write_reg |= TEA5777_W_MUTE_MASK;
397 else
398 tea->write_reg &= ~TEA5777_W_MUTE_MASK;
399
400 return radio_tea5777_set_freq(tea);
401 }
402
403 return -EINVAL;
404}
405
406static const struct v4l2_file_operations tea575x_fops = {
407 .unlocked_ioctl = video_ioctl2,
408 .open = v4l2_fh_open,
409 .release = v4l2_fh_release,
410 .poll = v4l2_ctrl_poll,
411};
412
413static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
414 .vidioc_querycap = vidioc_querycap,
415 .vidioc_g_tuner = vidioc_g_tuner,
416 .vidioc_s_tuner = vidioc_s_tuner,
417 .vidioc_g_frequency = vidioc_g_frequency,
418 .vidioc_s_frequency = vidioc_s_frequency,
419 .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
420 .vidioc_log_status = v4l2_ctrl_log_status,
421 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
422 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
423};
424
425static const struct video_device tea575x_radio = {
426 .ioctl_ops = &tea575x_ioctl_ops,
427 .release = video_device_release_empty,
428};
429
430static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
431 .s_ctrl = tea575x_s_ctrl,
432};
433
434int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
435{
436 int res;
437
438 tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) |
439 (1LL << TEA5777_W_IFW_SHIFT) |
440 (1LL << TEA5777_W_INTEXT_SHIFT) |
441 (1LL << TEA5777_W_CHP0_SHIFT) |
442 (2LL << TEA5777_W_SLEV_SHIFT);
443 tea->freq = 90500 * 16; /* 90.5Mhz default */
444 res = radio_tea5777_set_freq(tea);
445 if (res) {
446 v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
447 return res;
448 }
449
450 tea->vd = tea575x_radio;
451 video_set_drvdata(&tea->vd, tea);
452 mutex_init(&tea->mutex);
453 strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
454 tea->vd.lock = &tea->mutex;
455 tea->vd.v4l2_dev = tea->v4l2_dev;
456 tea->fops = tea575x_fops;
457 tea->fops.owner = owner;
458 tea->vd.fops = &tea->fops;
459 set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
460
461 tea->vd.ctrl_handler = &tea->ctrl_handler;
462 v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
463 v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
464 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
465 res = tea->ctrl_handler.error;
466 if (res) {
467 v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
468 v4l2_ctrl_handler_free(&tea->ctrl_handler);
469 return res;
470 }
471 v4l2_ctrl_handler_setup(&tea->ctrl_handler);
472
473 res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1);
474 if (res) {
475 v4l2_err(tea->v4l2_dev, "can't register video device!\n");
476 v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
477 return res;
478 }
479
480 return 0;
481}
482EXPORT_SYMBOL_GPL(radio_tea5777_init);
483
484void radio_tea5777_exit(struct radio_tea5777 *tea)
485{
486 video_unregister_device(&tea->vd);
487 v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
488}
489EXPORT_SYMBOL_GPL(radio_tea5777_exit);
diff --git a/drivers/media/radio/radio-tea5777.h b/drivers/media/radio/radio-tea5777.h
new file mode 100644
index 000000000000..55cbd78df5ed
--- /dev/null
+++ b/drivers/media/radio/radio-tea5777.h
@@ -0,0 +1,87 @@
1#ifndef __RADIO_TEA5777_H
2#define __RADIO_TEA5777_H
3
4/*
5 * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
6 *
7 * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
8 *
9 * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
10 *
11 * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
12 * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 *
28 */
29
30#include <linux/videodev2.h>
31#include <media/v4l2-ctrls.h>
32#include <media/v4l2-dev.h>
33#include <media/v4l2-device.h>
34
35#define TEA575X_FMIF 10700
36#define TEA575X_AMIF 450
37
38struct radio_tea5777;
39
40struct radio_tea5777_ops {
41 /*
42 * Write the 6 bytes large write register of the tea5777
43 *
44 * val represents the 6 write registers, with byte 1 from the
45 * datasheet being the most significant byte (so byte 5 of the u64),
46 * and byte 6 from the datasheet being the least significant byte.
47 *
48 * returns 0 on success.
49 */
50 int (*write_reg)(struct radio_tea5777 *tea, u64 val);
51 /*
52 * Read the 3 bytes large read register of the tea5777
53 *
54 * The read value gets returned in val, akin to write_reg, byte 1 from
55 * the datasheet is stored as the most significant byte (so byte 2 of
56 * the u32), and byte 3 from the datasheet gets stored as the least
57 * significant byte (iow byte 0 of the u32).
58 *
59 * returns 0 on success.
60 */
61 int (*read_reg)(struct radio_tea5777 *tea, u32 *val);
62};
63
64struct radio_tea5777 {
65 struct v4l2_device *v4l2_dev;
66 struct v4l2_file_operations fops;
67 struct video_device vd; /* video device */
68 bool has_am; /* Device can tune to AM freqs */
69 bool write_before_read; /* must write before read quirk */
70 bool needs_write; /* for write before read quirk */
71 u32 freq; /* current frequency */
72 u32 seek_rangelow; /* current hwseek limits */
73 u32 seek_rangehigh;
74 u32 read_reg;
75 u64 write_reg;
76 struct mutex mutex;
77 struct radio_tea5777_ops *ops;
78 void *private_data;
79 u8 card[32];
80 u8 bus_info[32];
81 struct v4l2_ctrl_handler ctrl_handler;
82};
83
84int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner);
85void radio_tea5777_exit(struct radio_tea5777 *tea);
86
87#endif /* __RADIO_TEA5777_H */