diff options
Diffstat (limited to 'sound/i2c/other')
-rw-r--r-- | sound/i2c/other/tea575x-tuner.c | 302 |
1 files changed, 207 insertions, 95 deletions
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 9d98a6658ac..d31c373e076 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
27 | #include <linux/version.h> | ||
27 | #include <sound/core.h> | 28 | #include <sound/core.h> |
28 | #include <sound/tea575x-tuner.h> | 29 | #include <sound/tea575x-tuner.h> |
29 | 30 | ||
@@ -31,6 +32,13 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | |||
31 | MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); | 32 | MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); |
32 | MODULE_LICENSE("GPL"); | 33 | MODULE_LICENSE("GPL"); |
33 | 34 | ||
35 | static int radio_nr = -1; | ||
36 | module_param(radio_nr, int, 0); | ||
37 | |||
38 | #define RADIO_VERSION KERNEL_VERSION(0, 0, 2) | ||
39 | #define FREQ_LO (87 * 16000) | ||
40 | #define FREQ_HI (108 * 16000) | ||
41 | |||
34 | /* | 42 | /* |
35 | * definitions | 43 | * definitions |
36 | */ | 44 | */ |
@@ -53,6 +61,17 @@ MODULE_LICENSE("GPL"); | |||
53 | #define TEA575X_BIT_DUMMY (1<<15) /* buffer */ | 61 | #define TEA575X_BIT_DUMMY (1<<15) /* buffer */ |
54 | #define TEA575X_BIT_FREQ_MASK 0x7fff | 62 | #define TEA575X_BIT_FREQ_MASK 0x7fff |
55 | 63 | ||
64 | static struct v4l2_queryctrl radio_qctrl[] = { | ||
65 | { | ||
66 | .id = V4L2_CID_AUDIO_MUTE, | ||
67 | .name = "Mute", | ||
68 | .minimum = 0, | ||
69 | .maximum = 1, | ||
70 | .default_value = 1, | ||
71 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
72 | } | ||
73 | }; | ||
74 | |||
56 | /* | 75 | /* |
57 | * lowlevel part | 76 | * lowlevel part |
58 | */ | 77 | */ |
@@ -84,94 +103,146 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea) | |||
84 | * Linux Video interface | 103 | * Linux Video interface |
85 | */ | 104 | */ |
86 | 105 | ||
87 | static long snd_tea575x_ioctl(struct file *file, | 106 | static int vidioc_querycap(struct file *file, void *priv, |
88 | unsigned int cmd, unsigned long data) | 107 | struct v4l2_capability *v) |
89 | { | 108 | { |
90 | struct snd_tea575x *tea = video_drvdata(file); | 109 | struct snd_tea575x *tea = video_drvdata(file); |
91 | void __user *arg = (void __user *)data; | 110 | |
92 | 111 | strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757"); | |
93 | switch(cmd) { | 112 | strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); |
94 | case VIDIOCGCAP: | 113 | strlcpy(v->card, "Maestro Radio", sizeof(v->card)); |
95 | { | 114 | sprintf(v->bus_info, "PCI"); |
96 | struct video_capability v; | 115 | v->version = RADIO_VERSION; |
97 | v.type = VID_TYPE_TUNER; | 116 | v->capabilities = V4L2_CAP_TUNER; |
98 | v.channels = 1; | 117 | return 0; |
99 | v.audios = 1; | 118 | } |
100 | /* No we don't do pictures */ | 119 | |
101 | v.maxwidth = 0; | 120 | static int vidioc_g_tuner(struct file *file, void *priv, |
102 | v.maxheight = 0; | 121 | struct v4l2_tuner *v) |
103 | v.minwidth = 0; | 122 | { |
104 | v.minheight = 0; | 123 | if (v->index > 0) |
105 | strcpy(v.name, tea->tea5759 ? "TEA5759" : "TEA5757"); | 124 | return -EINVAL; |
106 | if (copy_to_user(arg,&v,sizeof(v))) | 125 | |
107 | return -EFAULT; | 126 | strcpy(v->name, "FM"); |
108 | return 0; | 127 | v->type = V4L2_TUNER_RADIO; |
109 | } | 128 | v->rangelow = FREQ_LO; |
110 | case VIDIOCGTUNER: | 129 | v->rangehigh = FREQ_HI; |
111 | { | 130 | v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; |
112 | struct video_tuner v; | 131 | v->capability = V4L2_TUNER_CAP_LOW; |
113 | if (copy_from_user(&v, arg,sizeof(v))!=0) | 132 | v->audmode = V4L2_TUNER_MODE_MONO; |
114 | return -EFAULT; | 133 | v->signal = 0xffff; |
115 | if (v.tuner) /* Only 1 tuner */ | 134 | return 0; |
116 | return -EINVAL; | 135 | } |
117 | v.rangelow = (87*16000); | 136 | |
118 | v.rangehigh = (108*16000); | 137 | static int vidioc_s_tuner(struct file *file, void *priv, |
119 | v.flags = VIDEO_TUNER_LOW; | 138 | struct v4l2_tuner *v) |
120 | v.mode = VIDEO_MODE_AUTO; | 139 | { |
121 | strcpy(v.name, "FM"); | 140 | if (v->index > 0) |
122 | v.signal = 0xFFFF; | 141 | return -EINVAL; |
123 | if (copy_to_user(arg, &v, sizeof(v))) | 142 | return 0; |
124 | return -EFAULT; | 143 | } |
125 | return 0; | 144 | |
126 | } | 145 | static int vidioc_g_frequency(struct file *file, void *priv, |
127 | case VIDIOCSTUNER: | 146 | struct v4l2_frequency *f) |
128 | { | 147 | { |
129 | struct video_tuner v; | 148 | struct snd_tea575x *tea = video_drvdata(file); |
130 | if(copy_from_user(&v, arg, sizeof(v))) | 149 | |
131 | return -EFAULT; | 150 | f->type = V4L2_TUNER_RADIO; |
132 | if(v.tuner!=0) | 151 | f->frequency = tea->freq; |
133 | return -EINVAL; | 152 | return 0; |
134 | /* Only 1 tuner so no setting needed ! */ | 153 | } |
154 | |||
155 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
156 | struct v4l2_frequency *f) | ||
157 | { | ||
158 | struct snd_tea575x *tea = video_drvdata(file); | ||
159 | |||
160 | if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) | ||
161 | return -EINVAL; | ||
162 | |||
163 | tea->freq = f->frequency; | ||
164 | |||
165 | snd_tea575x_set_freq(tea); | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static int vidioc_g_audio(struct file *file, void *priv, | ||
171 | struct v4l2_audio *a) | ||
172 | { | ||
173 | if (a->index > 1) | ||
174 | return -EINVAL; | ||
175 | |||
176 | strcpy(a->name, "Radio"); | ||
177 | a->capability = V4L2_AUDCAP_STEREO; | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static int vidioc_s_audio(struct file *file, void *priv, | ||
182 | struct v4l2_audio *a) | ||
183 | { | ||
184 | if (a->index != 0) | ||
185 | return -EINVAL; | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int vidioc_queryctrl(struct file *file, void *priv, | ||
190 | struct v4l2_queryctrl *qc) | ||
191 | { | ||
192 | int i; | ||
193 | |||
194 | for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { | ||
195 | if (qc->id && qc->id == radio_qctrl[i].id) { | ||
196 | memcpy(qc, &(radio_qctrl[i]), | ||
197 | sizeof(*qc)); | ||
135 | return 0; | 198 | return 0; |
136 | } | 199 | } |
137 | case VIDIOCGFREQ: | 200 | } |
138 | if(copy_to_user(arg, &tea->freq, sizeof(tea->freq))) | 201 | return -EINVAL; |
139 | return -EFAULT; | 202 | } |
140 | return 0; | 203 | |
141 | case VIDIOCSFREQ: | 204 | static int vidioc_g_ctrl(struct file *file, void *priv, |
142 | if(copy_from_user(&tea->freq, arg, sizeof(tea->freq))) | 205 | struct v4l2_control *ctrl) |
143 | return -EFAULT; | 206 | { |
144 | snd_tea575x_set_freq(tea); | 207 | struct snd_tea575x *tea = video_drvdata(file); |
145 | return 0; | 208 | |
146 | case VIDIOCGAUDIO: | 209 | switch (ctrl->id) { |
147 | { | 210 | case V4L2_CID_AUDIO_MUTE: |
148 | struct video_audio v; | 211 | if (tea->ops->mute) { |
149 | memset(&v, 0, sizeof(v)); | 212 | ctrl->value = tea->mute; |
150 | strcpy(v.name, "Radio"); | ||
151 | if(copy_to_user(arg,&v, sizeof(v))) | ||
152 | return -EFAULT; | ||
153 | return 0; | 213 | return 0; |
154 | } | 214 | } |
155 | case VIDIOCSAUDIO: | 215 | } |
156 | { | 216 | return -EINVAL; |
157 | struct video_audio v; | 217 | } |
158 | if(copy_from_user(&v, arg, sizeof(v))) | 218 | |
159 | return -EFAULT; | 219 | static int vidioc_s_ctrl(struct file *file, void *priv, |
160 | if (tea->ops->mute) | 220 | struct v4l2_control *ctrl) |
161 | tea->ops->mute(tea, | 221 | { |
162 | (v.flags & | 222 | struct snd_tea575x *tea = video_drvdata(file); |
163 | VIDEO_AUDIO_MUTE) ? 1 : 0); | 223 | |
164 | if(v.audio) | 224 | switch (ctrl->id) { |
165 | return -EINVAL; | 225 | case V4L2_CID_AUDIO_MUTE: |
226 | if (tea->ops->mute) { | ||
227 | tea->ops->mute(tea, ctrl->value); | ||
228 | tea->mute = 1; | ||
166 | return 0; | 229 | return 0; |
167 | } | 230 | } |
168 | default: | ||
169 | return -ENOIOCTLCMD; | ||
170 | } | 231 | } |
232 | return -EINVAL; | ||
233 | } | ||
234 | |||
235 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
236 | { | ||
237 | *i = 0; | ||
238 | return 0; | ||
171 | } | 239 | } |
172 | 240 | ||
173 | static void snd_tea575x_release(struct video_device *vfd) | 241 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) |
174 | { | 242 | { |
243 | if (i != 0) | ||
244 | return -EINVAL; | ||
245 | return 0; | ||
175 | } | 246 | } |
176 | 247 | ||
177 | static int snd_tea575x_exclusive_open(struct file *file) | 248 | static int snd_tea575x_exclusive_open(struct file *file) |
@@ -189,50 +260,91 @@ static int snd_tea575x_exclusive_release(struct file *file) | |||
189 | return 0; | 260 | return 0; |
190 | } | 261 | } |
191 | 262 | ||
263 | static const struct v4l2_file_operations tea575x_fops = { | ||
264 | .owner = THIS_MODULE, | ||
265 | .open = snd_tea575x_exclusive_open, | ||
266 | .release = snd_tea575x_exclusive_release, | ||
267 | .ioctl = video_ioctl2, | ||
268 | }; | ||
269 | |||
270 | static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { | ||
271 | .vidioc_querycap = vidioc_querycap, | ||
272 | .vidioc_g_tuner = vidioc_g_tuner, | ||
273 | .vidioc_s_tuner = vidioc_s_tuner, | ||
274 | .vidioc_g_audio = vidioc_g_audio, | ||
275 | .vidioc_s_audio = vidioc_s_audio, | ||
276 | .vidioc_g_input = vidioc_g_input, | ||
277 | .vidioc_s_input = vidioc_s_input, | ||
278 | .vidioc_g_frequency = vidioc_g_frequency, | ||
279 | .vidioc_s_frequency = vidioc_s_frequency, | ||
280 | .vidioc_queryctrl = vidioc_queryctrl, | ||
281 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
282 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
283 | }; | ||
284 | |||
285 | static struct video_device tea575x_radio = { | ||
286 | .name = "tea575x-tuner", | ||
287 | .fops = &tea575x_fops, | ||
288 | .ioctl_ops = &tea575x_ioctl_ops, | ||
289 | .release = video_device_release, | ||
290 | }; | ||
291 | |||
192 | /* | 292 | /* |
193 | * initialize all the tea575x chips | 293 | * initialize all the tea575x chips |
194 | */ | 294 | */ |
195 | void snd_tea575x_init(struct snd_tea575x *tea) | 295 | void snd_tea575x_init(struct snd_tea575x *tea) |
196 | { | 296 | { |
297 | int retval; | ||
197 | unsigned int val; | 298 | unsigned int val; |
299 | struct video_device *tea575x_radio_inst; | ||
198 | 300 | ||
199 | val = tea->ops->read(tea); | 301 | val = tea->ops->read(tea); |
200 | if (val == 0x1ffffff || val == 0) { | 302 | if (val == 0x1ffffff || val == 0) { |
201 | snd_printk(KERN_ERR "Cannot find TEA575x chip\n"); | 303 | snd_printk(KERN_ERR |
304 | "tea575x-tuner: Cannot find TEA575x chip\n"); | ||
202 | return; | 305 | return; |
203 | } | 306 | } |
204 | 307 | ||
205 | memset(&tea->vd, 0, sizeof(tea->vd)); | ||
206 | strcpy(tea->vd.name, tea->tea5759 ? "TEA5759 radio" : "TEA5757 radio"); | ||
207 | tea->vd.release = snd_tea575x_release; | ||
208 | video_set_drvdata(&tea->vd, tea); | ||
209 | tea->vd.fops = &tea->fops; | ||
210 | tea->in_use = 0; | 308 | tea->in_use = 0; |
211 | tea->fops.owner = tea->card->module; | 309 | tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; |
212 | tea->fops.open = snd_tea575x_exclusive_open; | 310 | tea->freq = 90500 * 16; /* 90.5Mhz default */ |
213 | tea->fops.release = snd_tea575x_exclusive_release; | 311 | |
214 | tea->fops.ioctl = snd_tea575x_ioctl; | 312 | tea575x_radio_inst = video_device_alloc(); |
215 | if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) { | 313 | if (tea575x_radio_inst == NULL) { |
216 | snd_printk(KERN_ERR "unable to register tea575x tuner\n"); | 314 | printk(KERN_ERR "tea575x-tuner: not enough memory\n"); |
217 | return; | 315 | return; |
218 | } | 316 | } |
219 | tea->vd_registered = 1; | ||
220 | 317 | ||
221 | tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; | 318 | memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio)); |
222 | tea->freq = 90500 * 16; /* 90.5Mhz default */ | 319 | |
320 | strcpy(tea575x_radio.name, tea->tea5759 ? | ||
321 | "TEA5759 radio" : "TEA5757 radio"); | ||
322 | |||
323 | video_set_drvdata(tea575x_radio_inst, tea); | ||
324 | |||
325 | retval = video_register_device(tea575x_radio_inst, | ||
326 | VFL_TYPE_RADIO, radio_nr); | ||
327 | if (retval) { | ||
328 | printk(KERN_ERR "tea575x-tuner: can't register video device!\n"); | ||
329 | kfree(tea575x_radio_inst); | ||
330 | return; | ||
331 | } | ||
223 | 332 | ||
224 | snd_tea575x_set_freq(tea); | 333 | snd_tea575x_set_freq(tea); |
225 | 334 | ||
226 | /* mute on init */ | 335 | /* mute on init */ |
227 | if (tea->ops->mute) | 336 | if (tea->ops->mute) { |
228 | tea->ops->mute(tea, 1); | 337 | tea->ops->mute(tea, 1); |
338 | tea->mute = 1; | ||
339 | } | ||
340 | tea->vd = tea575x_radio_inst; | ||
229 | } | 341 | } |
230 | 342 | ||
231 | void snd_tea575x_exit(struct snd_tea575x *tea) | 343 | void snd_tea575x_exit(struct snd_tea575x *tea) |
232 | { | 344 | { |
233 | if (tea->vd_registered) { | 345 | if (tea->vd) { |
234 | video_unregister_device(&tea->vd); | 346 | video_unregister_device(tea->vd); |
235 | tea->vd_registered = 0; | 347 | tea->vd = NULL; |
236 | } | 348 | } |
237 | } | 349 | } |
238 | 350 | ||