diff options
Diffstat (limited to 'drivers/media/video/tuner-3036.c')
-rw-r--r-- | drivers/media/video/tuner-3036.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/drivers/media/video/tuner-3036.c b/drivers/media/video/tuner-3036.c new file mode 100644 index 000000000000..6b20aa902a8f --- /dev/null +++ b/drivers/media/video/tuner-3036.c | |||
@@ -0,0 +1,220 @@ | |||
1 | /* | ||
2 | * Driver for Philips SAB3036 "CITAC" tuner control chip. | ||
3 | * | ||
4 | * Author: Phil Blundell <philb@gnu.org> | ||
5 | * | ||
6 | * The SAB3036 is just about different enough from the chips that | ||
7 | * tuner.c copes with to make it not worth the effort to crowbar | ||
8 | * the support into that file. So instead we have a separate driver. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/timer.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/errno.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/init.h> | ||
25 | |||
26 | #include <linux/i2c.h> | ||
27 | #include <linux/videodev.h> | ||
28 | |||
29 | #include <media/tuner.h> | ||
30 | |||
31 | static int debug; /* insmod parameter */ | ||
32 | static int this_adap; | ||
33 | |||
34 | static struct i2c_client client_template; | ||
35 | |||
36 | /* Addresses to scan */ | ||
37 | static unsigned short normal_i2c[] = {I2C_CLIENT_END}; | ||
38 | static unsigned short normal_i2c_range[] = {0x60, 0x61, I2C_CLIENT_END}; | ||
39 | static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; | ||
40 | static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; | ||
41 | static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; | ||
42 | static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; | ||
43 | static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; | ||
44 | |||
45 | static struct i2c_client_address_data addr_data = { | ||
46 | normal_i2c, normal_i2c_range, | ||
47 | probe, probe_range, | ||
48 | ignore, ignore_range, | ||
49 | force | ||
50 | }; | ||
51 | |||
52 | /* ---------------------------------------------------------------------- */ | ||
53 | |||
54 | static unsigned char | ||
55 | tuner_getstatus (struct i2c_client *c) | ||
56 | { | ||
57 | unsigned char byte; | ||
58 | if (i2c_master_recv(c, &byte, 1) != 1) | ||
59 | printk(KERN_ERR "tuner-3036: I/O error.\n"); | ||
60 | return byte; | ||
61 | } | ||
62 | |||
63 | #define TUNER_FL 0x80 | ||
64 | |||
65 | static int | ||
66 | tuner_islocked (struct i2c_client *c) | ||
67 | { | ||
68 | return (tuner_getstatus(c) & TUNER_FL); | ||
69 | } | ||
70 | |||
71 | /* ---------------------------------------------------------------------- */ | ||
72 | |||
73 | static void | ||
74 | set_tv_freq(struct i2c_client *c, int freq) | ||
75 | { | ||
76 | u16 div = ((freq * 20) / 16); | ||
77 | unsigned long give_up = jiffies + HZ; | ||
78 | unsigned char buffer[2]; | ||
79 | |||
80 | if (debug) | ||
81 | printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div); | ||
82 | |||
83 | /* Select high tuning current */ | ||
84 | buffer[0] = 0x29; | ||
85 | buffer[1] = 0x3e; | ||
86 | |||
87 | if (i2c_master_send(c, buffer, 2) != 2) | ||
88 | printk("tuner: i2c i/o error 1\n"); | ||
89 | |||
90 | buffer[0] = 0x80 | ((div>>8) & 0x7f); | ||
91 | buffer[1] = div & 0xff; | ||
92 | |||
93 | if (i2c_master_send(c, buffer, 2) != 2) | ||
94 | printk("tuner: i2c i/o error 2\n"); | ||
95 | |||
96 | while (!tuner_islocked(c) && time_before(jiffies, give_up)) | ||
97 | schedule(); | ||
98 | |||
99 | if (!tuner_islocked(c)) | ||
100 | printk(KERN_WARNING "tuner: failed to achieve PLL lock\n"); | ||
101 | |||
102 | /* Select low tuning current and engage AFC */ | ||
103 | buffer[0] = 0x29; | ||
104 | buffer[1] = 0xb2; | ||
105 | |||
106 | if (i2c_master_send(c, buffer, 2) != 2) | ||
107 | printk("tuner: i2c i/o error 3\n"); | ||
108 | |||
109 | if (debug) | ||
110 | printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c)); | ||
111 | } | ||
112 | |||
113 | /* ---------------------------------------------------------------------- */ | ||
114 | |||
115 | static int | ||
116 | tuner_attach(struct i2c_adapter *adap, int addr, int kind) | ||
117 | { | ||
118 | static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 }; | ||
119 | |||
120 | struct i2c_client *client; | ||
121 | |||
122 | if (this_adap > 0) | ||
123 | return -1; | ||
124 | this_adap++; | ||
125 | |||
126 | client_template.adapter = adap; | ||
127 | client_template.addr = addr; | ||
128 | |||
129 | client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); | ||
130 | if (client == NULL) | ||
131 | return -ENOMEM; | ||
132 | memcpy(client, &client_template, sizeof(struct i2c_client)); | ||
133 | |||
134 | printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client)); | ||
135 | |||
136 | i2c_attach_client(client); | ||
137 | |||
138 | if (i2c_master_send(client, buffer, 2) != 2) | ||
139 | printk("tuner: i2c i/o error 1\n"); | ||
140 | if (i2c_master_send(client, buffer+2, 2) != 2) | ||
141 | printk("tuner: i2c i/o error 2\n"); | ||
142 | if (i2c_master_send(client, buffer+4, 2) != 2) | ||
143 | printk("tuner: i2c i/o error 3\n"); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static int | ||
148 | tuner_detach(struct i2c_client *c) | ||
149 | { | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int | ||
154 | tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) | ||
155 | { | ||
156 | int *iarg = (int*)arg; | ||
157 | |||
158 | switch (cmd) | ||
159 | { | ||
160 | case TUNER_SET_TVFREQ: | ||
161 | set_tv_freq(client, *iarg); | ||
162 | break; | ||
163 | |||
164 | default: | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static int | ||
171 | tuner_probe(struct i2c_adapter *adap) | ||
172 | { | ||
173 | this_adap = 0; | ||
174 | if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_LP)) | ||
175 | return i2c_probe(adap, &addr_data, tuner_attach); | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | /* ----------------------------------------------------------------------- */ | ||
180 | |||
181 | static struct i2c_driver | ||
182 | i2c_driver_tuner = | ||
183 | { | ||
184 | .owner = THIS_MODULE, | ||
185 | .name = "sab3036", | ||
186 | .id = I2C_DRIVERID_SAB3036, | ||
187 | .flags = I2C_DF_NOTIFY, | ||
188 | .attach_adapter = tuner_probe, | ||
189 | .detach_client = tuner_detach, | ||
190 | .command = tuner_command | ||
191 | }; | ||
192 | |||
193 | static struct i2c_client client_template = | ||
194 | { | ||
195 | .driver = &i2c_driver_tuner, | ||
196 | .name = "SAB3036", | ||
197 | }; | ||
198 | |||
199 | static int __init | ||
200 | tuner3036_init(void) | ||
201 | { | ||
202 | i2c_add_driver(&i2c_driver_tuner); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static void __exit | ||
207 | tuner3036_exit(void) | ||
208 | { | ||
209 | i2c_del_driver(&i2c_driver_tuner); | ||
210 | } | ||
211 | |||
212 | MODULE_DESCRIPTION("SAB3036 tuner driver"); | ||
213 | MODULE_AUTHOR("Philip Blundell <philb@gnu.org>"); | ||
214 | MODULE_LICENSE("GPL"); | ||
215 | |||
216 | module_param(debug, int, 0); | ||
217 | MODULE_PARM_DESC(debug,"Enable debugging output"); | ||
218 | |||
219 | module_init(tuner3036_init); | ||
220 | module_exit(tuner3036_exit); | ||