xref: /openbmc/linux/drivers/media/radio/tef6862.c (revision c0decac1)
1eea85b0aSRichard Röjfors /*
2eea85b0aSRichard Röjfors  * tef6862.c Philips TEF6862 Car Radio Enhanced Selectivity Tuner
3eea85b0aSRichard Röjfors  * Copyright (c) 2009 Intel Corporation
4eea85b0aSRichard Röjfors  *
5eea85b0aSRichard Röjfors  * This program is free software; you can redistribute it and/or modify
6eea85b0aSRichard Röjfors  * it under the terms of the GNU General Public License version 2 as
7eea85b0aSRichard Röjfors  * published by the Free Software Foundation.
8eea85b0aSRichard Röjfors  *
9eea85b0aSRichard Röjfors  * This program is distributed in the hope that it will be useful,
10eea85b0aSRichard Röjfors  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11eea85b0aSRichard Röjfors  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12eea85b0aSRichard Röjfors  * GNU General Public License for more details.
13eea85b0aSRichard Röjfors  */
14eea85b0aSRichard Röjfors 
15eea85b0aSRichard Röjfors #include <linux/module.h>
16eea85b0aSRichard Röjfors #include <linux/init.h>
17eea85b0aSRichard Röjfors #include <linux/errno.h>
18eea85b0aSRichard Röjfors #include <linux/kernel.h>
19eea85b0aSRichard Röjfors #include <linux/interrupt.h>
20eea85b0aSRichard Röjfors #include <linux/i2c.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
22eea85b0aSRichard Röjfors #include <media/v4l2-ioctl.h>
23eea85b0aSRichard Röjfors #include <media/v4l2-device.h>
24eea85b0aSRichard Röjfors 
25eea85b0aSRichard Röjfors #define DRIVER_NAME "tef6862"
26eea85b0aSRichard Röjfors 
27eea85b0aSRichard Röjfors #define FREQ_MUL 16000
28eea85b0aSRichard Röjfors 
29fa915996SHans Verkuil #define TEF6862_LO_FREQ (875U * FREQ_MUL / 10)
30fa915996SHans Verkuil #define TEF6862_HI_FREQ (108U * FREQ_MUL)
31eea85b0aSRichard Röjfors 
32eea85b0aSRichard Röjfors /* Write mode sub addresses */
33eea85b0aSRichard Röjfors #define WM_SUB_BANDWIDTH	0x0
34eea85b0aSRichard Röjfors #define WM_SUB_PLLM		0x1
35eea85b0aSRichard Röjfors #define WM_SUB_PLLL		0x2
36eea85b0aSRichard Röjfors #define WM_SUB_DAA		0x3
37eea85b0aSRichard Röjfors #define WM_SUB_AGC		0x4
38eea85b0aSRichard Röjfors #define WM_SUB_BAND		0x5
39eea85b0aSRichard Röjfors #define WM_SUB_CONTROL		0x6
40eea85b0aSRichard Röjfors #define WM_SUB_LEVEL		0x7
41eea85b0aSRichard Röjfors #define WM_SUB_IFCF		0x8
42eea85b0aSRichard Röjfors #define WM_SUB_IFCAP		0x9
43eea85b0aSRichard Röjfors #define WM_SUB_ACD		0xA
44eea85b0aSRichard Röjfors #define WM_SUB_TEST		0xF
45eea85b0aSRichard Röjfors 
46eea85b0aSRichard Röjfors /* Different modes of the MSA register */
475f27ca41SMauro Carvalho Chehab #define MSA_MODE_BUFFER		0x0
485f27ca41SMauro Carvalho Chehab #define MSA_MODE_PRESET		0x1
495f27ca41SMauro Carvalho Chehab #define MSA_MODE_SEARCH		0x2
505f27ca41SMauro Carvalho Chehab #define MSA_MODE_AF_UPDATE	0x3
515f27ca41SMauro Carvalho Chehab #define MSA_MODE_JUMP		0x4
525f27ca41SMauro Carvalho Chehab #define MSA_MODE_CHECK		0x5
535f27ca41SMauro Carvalho Chehab #define MSA_MODE_LOAD		0x6
545f27ca41SMauro Carvalho Chehab #define MSA_MODE_END		0x7
555f27ca41SMauro Carvalho Chehab #define MSA_MODE_SHIFT		5
56eea85b0aSRichard Röjfors 
57eea85b0aSRichard Röjfors struct tef6862_state {
58eea85b0aSRichard Röjfors 	struct v4l2_subdev sd;
59eea85b0aSRichard Röjfors 	unsigned long freq;
60eea85b0aSRichard Röjfors };
61eea85b0aSRichard Röjfors 
62eea85b0aSRichard Röjfors static inline struct tef6862_state *to_state(struct v4l2_subdev *sd)
63eea85b0aSRichard Röjfors {
64eea85b0aSRichard Röjfors 	return container_of(sd, struct tef6862_state, sd);
65eea85b0aSRichard Röjfors }
66eea85b0aSRichard Röjfors 
67eea85b0aSRichard Röjfors static u16 tef6862_sigstr(struct i2c_client *client)
68eea85b0aSRichard Röjfors {
69eea85b0aSRichard Röjfors 	u8 buf[4];
70eea85b0aSRichard Röjfors 	int err = i2c_master_recv(client, buf, sizeof(buf));
71eea85b0aSRichard Röjfors 	if (err == sizeof(buf))
72eea85b0aSRichard Röjfors 		return buf[3] << 8;
73eea85b0aSRichard Röjfors 	return 0;
74eea85b0aSRichard Röjfors }
75eea85b0aSRichard Röjfors 
76eea85b0aSRichard Röjfors static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
77eea85b0aSRichard Röjfors {
78eea85b0aSRichard Röjfors 	if (v->index > 0)
79eea85b0aSRichard Röjfors 		return -EINVAL;
80eea85b0aSRichard Röjfors 
81eea85b0aSRichard Röjfors 	/* only support FM for now */
82c0decac1SMauro Carvalho Chehab 	strscpy(v->name, "FM", sizeof(v->name));
83eea85b0aSRichard Röjfors 	v->type = V4L2_TUNER_RADIO;
84eea85b0aSRichard Röjfors 	v->rangelow = TEF6862_LO_FREQ;
85eea85b0aSRichard Röjfors 	v->rangehigh = TEF6862_HI_FREQ;
86eea85b0aSRichard Röjfors 	v->rxsubchans = V4L2_TUNER_SUB_MONO;
87eea85b0aSRichard Röjfors 	v->capability = V4L2_TUNER_CAP_LOW;
88eea85b0aSRichard Röjfors 	v->audmode = V4L2_TUNER_MODE_STEREO;
89eea85b0aSRichard Röjfors 	v->signal = tef6862_sigstr(v4l2_get_subdevdata(sd));
90eea85b0aSRichard Röjfors 
91eea85b0aSRichard Röjfors 	return 0;
92eea85b0aSRichard Röjfors }
93eea85b0aSRichard Röjfors 
942f73c7c5SHans Verkuil static int tef6862_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v)
95eea85b0aSRichard Röjfors {
96eea85b0aSRichard Röjfors 	return v->index ? -EINVAL : 0;
97eea85b0aSRichard Röjfors }
98eea85b0aSRichard Röjfors 
99b530a447SHans Verkuil static int tef6862_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f)
100eea85b0aSRichard Röjfors {
101eea85b0aSRichard Röjfors 	struct tef6862_state *state = to_state(sd);
102eea85b0aSRichard Röjfors 	struct i2c_client *client = v4l2_get_subdevdata(sd);
103fa915996SHans Verkuil 	unsigned freq = f->frequency;
104eea85b0aSRichard Röjfors 	u16 pll;
105eea85b0aSRichard Röjfors 	u8 i2cmsg[3];
106eea85b0aSRichard Röjfors 	int err;
107eea85b0aSRichard Röjfors 
108eea85b0aSRichard Röjfors 	if (f->tuner != 0)
109eea85b0aSRichard Röjfors 		return -EINVAL;
110eea85b0aSRichard Röjfors 
1119ba6a91fSHans Verkuil 	freq = clamp(freq, TEF6862_LO_FREQ, TEF6862_HI_FREQ);
112fa915996SHans Verkuil 	pll = 1964 + ((freq - TEF6862_LO_FREQ) * 20) / FREQ_MUL;
1135f27ca41SMauro Carvalho Chehab 	i2cmsg[0] = (MSA_MODE_PRESET << MSA_MODE_SHIFT) | WM_SUB_PLLM;
114eea85b0aSRichard Röjfors 	i2cmsg[1] = (pll >> 8) & 0xff;
115eea85b0aSRichard Röjfors 	i2cmsg[2] = pll & 0xff;
116eea85b0aSRichard Röjfors 
117eea85b0aSRichard Röjfors 	err = i2c_master_send(client, i2cmsg, sizeof(i2cmsg));
118bd93b3adSAxel Lin 	if (err != sizeof(i2cmsg))
119bd93b3adSAxel Lin 		return err < 0 ? err : -EIO;
120bd93b3adSAxel Lin 
121fa915996SHans Verkuil 	state->freq = freq;
122bd93b3adSAxel Lin 	return 0;
123eea85b0aSRichard Röjfors }
124eea85b0aSRichard Röjfors 
125eea85b0aSRichard Röjfors static int tef6862_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
126eea85b0aSRichard Röjfors {
127eea85b0aSRichard Röjfors 	struct tef6862_state *state = to_state(sd);
128eea85b0aSRichard Röjfors 
129eea85b0aSRichard Röjfors 	if (f->tuner != 0)
130eea85b0aSRichard Röjfors 		return -EINVAL;
131eea85b0aSRichard Röjfors 	f->type = V4L2_TUNER_RADIO;
132eea85b0aSRichard Röjfors 	f->frequency = state->freq;
133eea85b0aSRichard Röjfors 	return 0;
134eea85b0aSRichard Röjfors }
135eea85b0aSRichard Röjfors 
136eea85b0aSRichard Röjfors static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = {
137eea85b0aSRichard Röjfors 	.g_tuner = tef6862_g_tuner,
138eea85b0aSRichard Röjfors 	.s_tuner = tef6862_s_tuner,
139eea85b0aSRichard Röjfors 	.s_frequency = tef6862_s_frequency,
140eea85b0aSRichard Röjfors 	.g_frequency = tef6862_g_frequency,
141eea85b0aSRichard Röjfors };
142eea85b0aSRichard Röjfors 
143eea85b0aSRichard Röjfors static const struct v4l2_subdev_ops tef6862_ops = {
144eea85b0aSRichard Röjfors 	.tuner = &tef6862_tuner_ops,
145eea85b0aSRichard Röjfors };
146eea85b0aSRichard Röjfors 
147eea85b0aSRichard Röjfors /*
148eea85b0aSRichard Röjfors  * Generic i2c probe
149eea85b0aSRichard Röjfors  * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
150eea85b0aSRichard Röjfors  */
151eea85b0aSRichard Röjfors 
1524c62e976SGreg Kroah-Hartman static int tef6862_probe(struct i2c_client *client,
153eea85b0aSRichard Röjfors 			 const struct i2c_device_id *id)
154eea85b0aSRichard Röjfors {
155eea85b0aSRichard Röjfors 	struct tef6862_state *state;
156eea85b0aSRichard Röjfors 	struct v4l2_subdev *sd;
157eea85b0aSRichard Röjfors 
158eea85b0aSRichard Röjfors 	/* Check if the adapter supports the needed features */
159eea85b0aSRichard Röjfors 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
160eea85b0aSRichard Röjfors 		return -EIO;
161eea85b0aSRichard Röjfors 
162eea85b0aSRichard Röjfors 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
163eea85b0aSRichard Röjfors 			client->addr << 1, client->adapter->name);
164eea85b0aSRichard Röjfors 
16580845a33SHerton Ronaldo Krzesinski 	state = kzalloc(sizeof(struct tef6862_state), GFP_KERNEL);
166eea85b0aSRichard Röjfors 	if (state == NULL)
167eea85b0aSRichard Röjfors 		return -ENOMEM;
168eea85b0aSRichard Röjfors 	state->freq = TEF6862_LO_FREQ;
169eea85b0aSRichard Röjfors 
170eea85b0aSRichard Röjfors 	sd = &state->sd;
171eea85b0aSRichard Röjfors 	v4l2_i2c_subdev_init(sd, client, &tef6862_ops);
172eea85b0aSRichard Röjfors 
173eea85b0aSRichard Röjfors 	return 0;
174eea85b0aSRichard Röjfors }
175eea85b0aSRichard Röjfors 
1764c62e976SGreg Kroah-Hartman static int tef6862_remove(struct i2c_client *client)
177eea85b0aSRichard Röjfors {
178eea85b0aSRichard Röjfors 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
179eea85b0aSRichard Röjfors 
180eea85b0aSRichard Röjfors 	v4l2_device_unregister_subdev(sd);
181eea85b0aSRichard Röjfors 	kfree(to_state(sd));
182eea85b0aSRichard Röjfors 	return 0;
183eea85b0aSRichard Röjfors }
184eea85b0aSRichard Röjfors 
185eea85b0aSRichard Röjfors static const struct i2c_device_id tef6862_id[] = {
186eea85b0aSRichard Röjfors 	{DRIVER_NAME, 0},
187eea85b0aSRichard Röjfors 	{},
188eea85b0aSRichard Röjfors };
189eea85b0aSRichard Röjfors 
190eea85b0aSRichard Röjfors MODULE_DEVICE_TABLE(i2c, tef6862_id);
191eea85b0aSRichard Röjfors 
192eea85b0aSRichard Röjfors static struct i2c_driver tef6862_driver = {
193eea85b0aSRichard Röjfors 	.driver = {
194eea85b0aSRichard Röjfors 		.name	= DRIVER_NAME,
195eea85b0aSRichard Röjfors 	},
196eea85b0aSRichard Röjfors 	.probe		= tef6862_probe,
1974c62e976SGreg Kroah-Hartman 	.remove		= tef6862_remove,
198eea85b0aSRichard Röjfors 	.id_table	= tef6862_id,
199eea85b0aSRichard Röjfors };
200eea85b0aSRichard Röjfors 
201c6e8d86fSAxel Lin module_i2c_driver(tef6862_driver);
202eea85b0aSRichard Röjfors 
203eea85b0aSRichard Röjfors MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner");
204eea85b0aSRichard Röjfors MODULE_AUTHOR("Mocean Laboratories");
205eea85b0aSRichard Röjfors MODULE_LICENSE("GPL v2");
206