root/drivers/media/radio/tea575x.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_tea575x_write
  2. snd_tea575x_read
  3. snd_tea575x_val_to_freq
  4. snd_tea575x_get_freq
  5. snd_tea575x_set_freq
  6. vidioc_querycap
  7. snd_tea575x_enum_freq_bands
  8. vidioc_enum_freq_bands
  9. snd_tea575x_g_tuner
  10. vidioc_g_tuner
  11. vidioc_s_tuner
  12. vidioc_g_frequency
  13. vidioc_s_frequency
  14. snd_tea575x_s_hw_freq_seek
  15. vidioc_s_hw_freq_seek
  16. tea575x_s_ctrl
  17. snd_tea575x_hw_init
  18. snd_tea575x_init
  19. snd_tea575x_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *   ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips
   4  *
   5  *      Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
   6  */
   7 
   8 #include <linux/delay.h>
   9 #include <linux/module.h>
  10 #include <linux/init.h>
  11 #include <linux/slab.h>
  12 #include <linux/sched.h>
  13 #include <asm/io.h>
  14 #include <media/v4l2-device.h>
  15 #include <media/v4l2-dev.h>
  16 #include <media/v4l2-fh.h>
  17 #include <media/v4l2-ioctl.h>
  18 #include <media/v4l2-event.h>
  19 #include <media/drv-intf/tea575x.h>
  20 
  21 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  22 MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
  23 MODULE_LICENSE("GPL");
  24 
  25 /*
  26  * definitions
  27  */
  28 
  29 #define TEA575X_BIT_SEARCH      (1<<24)         /* 1 = search action, 0 = tuned */
  30 #define TEA575X_BIT_UPDOWN      (1<<23)         /* 0 = search down, 1 = search up */
  31 #define TEA575X_BIT_MONO        (1<<22)         /* 0 = stereo, 1 = mono */
  32 #define TEA575X_BIT_BAND_MASK   (3<<20)
  33 #define TEA575X_BIT_BAND_FM     (0<<20)
  34 #define TEA575X_BIT_BAND_MW     (1<<20)
  35 #define TEA575X_BIT_BAND_LW     (2<<20)
  36 #define TEA575X_BIT_BAND_SW     (3<<20)
  37 #define TEA575X_BIT_PORT_0      (1<<19)         /* user bit */
  38 #define TEA575X_BIT_PORT_1      (1<<18)         /* user bit */
  39 #define TEA575X_BIT_SEARCH_MASK (3<<16)         /* search level */
  40 #define TEA575X_BIT_SEARCH_5_28      (0<<16)    /* FM >5uV, AM >28uV */
  41 #define TEA575X_BIT_SEARCH_10_40     (1<<16)    /* FM >10uV, AM > 40uV */
  42 #define TEA575X_BIT_SEARCH_30_63     (2<<16)    /* FM >30uV, AM > 63uV */
  43 #define TEA575X_BIT_SEARCH_150_1000  (3<<16)    /* FM > 150uV, AM > 1000uV */
  44 #define TEA575X_BIT_DUMMY       (1<<15)         /* buffer */
  45 #define TEA575X_BIT_FREQ_MASK   0x7fff
  46 
  47 enum { BAND_FM, BAND_FM_JAPAN, BAND_AM };
  48 
  49 static const struct v4l2_frequency_band bands[] = {
  50         {
  51                 .type = V4L2_TUNER_RADIO,
  52                 .index = 0,
  53                 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
  54                               V4L2_TUNER_CAP_FREQ_BANDS,
  55                 .rangelow   =  87500 * 16,
  56                 .rangehigh  = 108000 * 16,
  57                 .modulation = V4L2_BAND_MODULATION_FM,
  58         },
  59         {
  60                 .type = V4L2_TUNER_RADIO,
  61                 .index = 0,
  62                 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
  63                               V4L2_TUNER_CAP_FREQ_BANDS,
  64                 .rangelow   = 76000 * 16,
  65                 .rangehigh  = 91000 * 16,
  66                 .modulation = V4L2_BAND_MODULATION_FM,
  67         },
  68         {
  69                 .type = V4L2_TUNER_RADIO,
  70                 .index = 1,
  71                 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
  72                 .rangelow   =  530 * 16,
  73                 .rangehigh  = 1710 * 16,
  74                 .modulation = V4L2_BAND_MODULATION_AM,
  75         },
  76 };
  77 
  78 /*
  79  * lowlevel part
  80  */
  81 
  82 static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
  83 {
  84         u16 l;
  85         u8 data;
  86 
  87         if (tea->ops->write_val)
  88                 return tea->ops->write_val(tea, val);
  89 
  90         tea->ops->set_direction(tea, 1);
  91         udelay(16);
  92 
  93         for (l = 25; l > 0; l--) {
  94                 data = (val >> 24) & TEA575X_DATA;
  95                 val <<= 1;                      /* shift data */
  96                 tea->ops->set_pins(tea, data | TEA575X_WREN);
  97                 udelay(2);
  98                 tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
  99                 udelay(2);
 100                 tea->ops->set_pins(tea, data | TEA575X_WREN);
 101                 udelay(2);
 102         }
 103 
 104         if (!tea->mute)
 105                 tea->ops->set_pins(tea, 0);
 106 }
 107 
 108 static u32 snd_tea575x_read(struct snd_tea575x *tea)
 109 {
 110         u16 l, rdata;
 111         u32 data = 0;
 112 
 113         if (tea->ops->read_val)
 114                 return tea->ops->read_val(tea);
 115 
 116         tea->ops->set_direction(tea, 0);
 117         tea->ops->set_pins(tea, 0);
 118         udelay(16);
 119 
 120         for (l = 24; l--;) {
 121                 tea->ops->set_pins(tea, TEA575X_CLK);
 122                 udelay(2);
 123                 if (!l)
 124                         tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
 125                 tea->ops->set_pins(tea, 0);
 126                 udelay(2);
 127                 data <<= 1;                     /* shift data */
 128                 rdata = tea->ops->get_pins(tea);
 129                 if (!l)
 130                         tea->stereo = (rdata & TEA575X_MOST) ?  0 : 1;
 131                 if (rdata & TEA575X_DATA)
 132                         data++;
 133                 udelay(2);
 134         }
 135 
 136         if (tea->mute)
 137                 tea->ops->set_pins(tea, TEA575X_WREN);
 138 
 139         return data;
 140 }
 141 
 142 static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val)
 143 {
 144         u32 freq = val & TEA575X_BIT_FREQ_MASK;
 145 
 146         if (freq == 0)
 147                 return freq;
 148 
 149         switch (tea->band) {
 150         case BAND_FM:
 151                 /* freq *= 12.5 */
 152                 freq *= 125;
 153                 freq /= 10;
 154                 /* crystal fixup */
 155                 freq -= TEA575X_FMIF;
 156                 break;
 157         case BAND_FM_JAPAN:
 158                 /* freq *= 12.5 */
 159                 freq *= 125;
 160                 freq /= 10;
 161                 /* crystal fixup */
 162                 freq += TEA575X_FMIF;
 163                 break;
 164         case BAND_AM:
 165                 /* crystal fixup */
 166                 freq -= TEA575X_AMIF;
 167                 break;
 168         }
 169 
 170         return clamp(freq * 16, bands[tea->band].rangelow,
 171                                 bands[tea->band].rangehigh); /* from kHz */
 172 }
 173 
 174 static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
 175 {
 176         return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea));
 177 }
 178 
 179 void snd_tea575x_set_freq(struct snd_tea575x *tea)
 180 {
 181         u32 freq = tea->freq / 16;      /* to kHz */
 182         u32 band = 0;
 183 
 184         switch (tea->band) {
 185         case BAND_FM:
 186                 band = TEA575X_BIT_BAND_FM;
 187                 /* crystal fixup */
 188                 freq += TEA575X_FMIF;
 189                 /* freq /= 12.5 */
 190                 freq *= 10;
 191                 freq /= 125;
 192                 break;
 193         case BAND_FM_JAPAN:
 194                 band = TEA575X_BIT_BAND_FM;
 195                 /* crystal fixup */
 196                 freq -= TEA575X_FMIF;
 197                 /* freq /= 12.5 */
 198                 freq *= 10;
 199                 freq /= 125;
 200                 break;
 201         case BAND_AM:
 202                 band = TEA575X_BIT_BAND_MW;
 203                 /* crystal fixup */
 204                 freq += TEA575X_AMIF;
 205                 break;
 206         }
 207 
 208         tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK);
 209         tea->val |= band;
 210         tea->val |= freq & TEA575X_BIT_FREQ_MASK;
 211         snd_tea575x_write(tea, tea->val);
 212         tea->freq = snd_tea575x_val_to_freq(tea, tea->val);
 213 }
 214 EXPORT_SYMBOL(snd_tea575x_set_freq);
 215 
 216 /*
 217  * Linux Video interface
 218  */
 219 
 220 static int vidioc_querycap(struct file *file, void  *priv,
 221                                         struct v4l2_capability *v)
 222 {
 223         struct snd_tea575x *tea = video_drvdata(file);
 224 
 225         strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
 226         strscpy(v->card, tea->card, sizeof(v->card));
 227         strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
 228         strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 229         return 0;
 230 }
 231 
 232 int snd_tea575x_enum_freq_bands(struct snd_tea575x *tea,
 233                                         struct v4l2_frequency_band *band)
 234 {
 235         int index;
 236 
 237         if (band->tuner != 0)
 238                 return -EINVAL;
 239 
 240         switch (band->index) {
 241         case 0:
 242                 if (tea->tea5759)
 243                         index = BAND_FM_JAPAN;
 244                 else
 245                         index = BAND_FM;
 246                 break;
 247         case 1:
 248                 if (tea->has_am) {
 249                         index = BAND_AM;
 250                         break;
 251                 }
 252                 /* Fall through */
 253         default:
 254                 return -EINVAL;
 255         }
 256 
 257         *band = bands[index];
 258         if (!tea->cannot_read_data)
 259                 band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
 260 
 261         return 0;
 262 }
 263 EXPORT_SYMBOL(snd_tea575x_enum_freq_bands);
 264 
 265 static int vidioc_enum_freq_bands(struct file *file, void *priv,
 266                                          struct v4l2_frequency_band *band)
 267 {
 268         struct snd_tea575x *tea = video_drvdata(file);
 269 
 270         return snd_tea575x_enum_freq_bands(tea, band);
 271 }
 272 
 273 int snd_tea575x_g_tuner(struct snd_tea575x *tea, struct v4l2_tuner *v)
 274 {
 275         struct v4l2_frequency_band band_fm = { 0, };
 276 
 277         if (v->index > 0)
 278                 return -EINVAL;
 279 
 280         snd_tea575x_read(tea);
 281         snd_tea575x_enum_freq_bands(tea, &band_fm);
 282 
 283         memset(v, 0, sizeof(*v));
 284         strscpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name));
 285         v->type = V4L2_TUNER_RADIO;
 286         v->capability = band_fm.capability;
 287         v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow;
 288         v->rangehigh = band_fm.rangehigh;
 289         v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
 290         v->audmode = (tea->val & TEA575X_BIT_MONO) ?
 291                 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
 292         v->signal = tea->tuned ? 0xffff : 0;
 293         return 0;
 294 }
 295 EXPORT_SYMBOL(snd_tea575x_g_tuner);
 296 
 297 static int vidioc_g_tuner(struct file *file, void *priv,
 298                                         struct v4l2_tuner *v)
 299 {
 300         struct snd_tea575x *tea = video_drvdata(file);
 301 
 302         return snd_tea575x_g_tuner(tea, v);
 303 }
 304 
 305 static int vidioc_s_tuner(struct file *file, void *priv,
 306                                         const struct v4l2_tuner *v)
 307 {
 308         struct snd_tea575x *tea = video_drvdata(file);
 309         u32 orig_val = tea->val;
 310 
 311         if (v->index)
 312                 return -EINVAL;
 313         tea->val &= ~TEA575X_BIT_MONO;
 314         if (v->audmode == V4L2_TUNER_MODE_MONO)
 315                 tea->val |= TEA575X_BIT_MONO;
 316         /* Only apply changes if currently tuning FM */
 317         if (tea->band != BAND_AM && tea->val != orig_val)
 318                 snd_tea575x_set_freq(tea);
 319 
 320         return 0;
 321 }
 322 
 323 static int vidioc_g_frequency(struct file *file, void *priv,
 324                                         struct v4l2_frequency *f)
 325 {
 326         struct snd_tea575x *tea = video_drvdata(file);
 327 
 328         if (f->tuner != 0)
 329                 return -EINVAL;
 330         f->type = V4L2_TUNER_RADIO;
 331         f->frequency = tea->freq;
 332         return 0;
 333 }
 334 
 335 static int vidioc_s_frequency(struct file *file, void *priv,
 336                                         const struct v4l2_frequency *f)
 337 {
 338         struct snd_tea575x *tea = video_drvdata(file);
 339 
 340         if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
 341                 return -EINVAL;
 342 
 343         if (tea->has_am && f->frequency < (20000 * 16))
 344                 tea->band = BAND_AM;
 345         else if (tea->tea5759)
 346                 tea->band = BAND_FM_JAPAN;
 347         else
 348                 tea->band = BAND_FM;
 349 
 350         tea->freq = clamp_t(u32, f->frequency, bands[tea->band].rangelow,
 351                                         bands[tea->band].rangehigh);
 352         snd_tea575x_set_freq(tea);
 353         return 0;
 354 }
 355 
 356 int snd_tea575x_s_hw_freq_seek(struct file *file, struct snd_tea575x *tea,
 357                                 const struct v4l2_hw_freq_seek *a)
 358 {
 359         unsigned long timeout;
 360         int i, spacing;
 361 
 362         if (tea->cannot_read_data)
 363                 return -ENOTTY;
 364         if (a->tuner || a->wrap_around)
 365                 return -EINVAL;
 366 
 367         if (file->f_flags & O_NONBLOCK)
 368                 return -EWOULDBLOCK;
 369 
 370         if (a->rangelow || a->rangehigh) {
 371                 for (i = 0; i < ARRAY_SIZE(bands); i++) {
 372                         if ((i == BAND_FM && tea->tea5759) ||
 373                             (i == BAND_FM_JAPAN && !tea->tea5759) ||
 374                             (i == BAND_AM && !tea->has_am))
 375                                 continue;
 376                         if (bands[i].rangelow  == a->rangelow &&
 377                             bands[i].rangehigh == a->rangehigh)
 378                                 break;
 379                 }
 380                 if (i == ARRAY_SIZE(bands))
 381                         return -EINVAL; /* No matching band found */
 382                 if (i != tea->band) {
 383                         tea->band = i;
 384                         tea->freq = clamp(tea->freq, bands[i].rangelow,
 385                                                      bands[i].rangehigh);
 386                         snd_tea575x_set_freq(tea);
 387                 }
 388         }
 389 
 390         spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */
 391 
 392         /* clear the frequency, HW will fill it in */
 393         tea->val &= ~TEA575X_BIT_FREQ_MASK;
 394         tea->val |= TEA575X_BIT_SEARCH;
 395         if (a->seek_upward)
 396                 tea->val |= TEA575X_BIT_UPDOWN;
 397         else
 398                 tea->val &= ~TEA575X_BIT_UPDOWN;
 399         snd_tea575x_write(tea, tea->val);
 400         timeout = jiffies + msecs_to_jiffies(10000);
 401         for (;;) {
 402                 if (time_after(jiffies, timeout))
 403                         break;
 404                 if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
 405                         /* some signal arrived, stop search */
 406                         tea->val &= ~TEA575X_BIT_SEARCH;
 407                         snd_tea575x_set_freq(tea);
 408                         return -ERESTARTSYS;
 409                 }
 410                 if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) {
 411                         u32 freq;
 412 
 413                         /* Found a frequency, wait until it can be read */
 414                         for (i = 0; i < 100; i++) {
 415                                 msleep(10);
 416                                 freq = snd_tea575x_get_freq(tea);
 417                                 if (freq) /* available */
 418                                         break;
 419                         }
 420                         if (freq == 0) /* shouldn't happen */
 421                                 break;
 422                         /*
 423                          * if we moved by less than the spacing, or in the
 424                          * wrong direction, continue seeking
 425                          */
 426                         if (abs(tea->freq - freq) < 16 * spacing ||
 427                                         (a->seek_upward && freq < tea->freq) ||
 428                                         (!a->seek_upward && freq > tea->freq)) {
 429                                 snd_tea575x_write(tea, tea->val);
 430                                 continue;
 431                         }
 432                         tea->freq = freq;
 433                         tea->val &= ~TEA575X_BIT_SEARCH;
 434                         return 0;
 435                 }
 436         }
 437         tea->val &= ~TEA575X_BIT_SEARCH;
 438         snd_tea575x_set_freq(tea);
 439         return -ENODATA;
 440 }
 441 EXPORT_SYMBOL(snd_tea575x_s_hw_freq_seek);
 442 
 443 static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
 444                                         const struct v4l2_hw_freq_seek *a)
 445 {
 446         struct snd_tea575x *tea = video_drvdata(file);
 447 
 448         return snd_tea575x_s_hw_freq_seek(file, tea, a);
 449 }
 450 
 451 static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 452 {
 453         struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 454 
 455         switch (ctrl->id) {
 456         case V4L2_CID_AUDIO_MUTE:
 457                 tea->mute = ctrl->val;
 458                 snd_tea575x_set_freq(tea);
 459                 return 0;
 460         }
 461 
 462         return -EINVAL;
 463 }
 464 
 465 static const struct v4l2_file_operations tea575x_fops = {
 466         .unlocked_ioctl = video_ioctl2,
 467         .open           = v4l2_fh_open,
 468         .release        = v4l2_fh_release,
 469         .poll           = v4l2_ctrl_poll,
 470 };
 471 
 472 static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
 473         .vidioc_querycap    = vidioc_querycap,
 474         .vidioc_g_tuner     = vidioc_g_tuner,
 475         .vidioc_s_tuner     = vidioc_s_tuner,
 476         .vidioc_g_frequency = vidioc_g_frequency,
 477         .vidioc_s_frequency = vidioc_s_frequency,
 478         .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
 479         .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
 480         .vidioc_log_status  = v4l2_ctrl_log_status,
 481         .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 482         .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 483 };
 484 
 485 static const struct video_device tea575x_radio = {
 486         .ioctl_ops      = &tea575x_ioctl_ops,
 487         .release        = video_device_release_empty,
 488 };
 489 
 490 static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
 491         .s_ctrl = tea575x_s_ctrl,
 492 };
 493 
 494 
 495 int snd_tea575x_hw_init(struct snd_tea575x *tea)
 496 {
 497         tea->mute = true;
 498 
 499         /* Not all devices can or know how to read the data back.
 500            Such devices can set cannot_read_data to true. */
 501         if (!tea->cannot_read_data) {
 502                 snd_tea575x_write(tea, 0x55AA);
 503                 if (snd_tea575x_read(tea) != 0x55AA)
 504                         return -ENODEV;
 505         }
 506 
 507         tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28;
 508         tea->freq = 90500 * 16;         /* 90.5Mhz default */
 509         snd_tea575x_set_freq(tea);
 510 
 511         return 0;
 512 }
 513 EXPORT_SYMBOL(snd_tea575x_hw_init);
 514 
 515 int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner)
 516 {
 517         int retval = snd_tea575x_hw_init(tea);
 518 
 519         if (retval)
 520                 return retval;
 521 
 522         tea->vd = tea575x_radio;
 523         video_set_drvdata(&tea->vd, tea);
 524         mutex_init(&tea->mutex);
 525         strscpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
 526         tea->vd.lock = &tea->mutex;
 527         tea->vd.v4l2_dev = tea->v4l2_dev;
 528         tea->vd.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 529         if (!tea->cannot_read_data)
 530                 tea->vd.device_caps |= V4L2_CAP_HW_FREQ_SEEK;
 531         tea->fops = tea575x_fops;
 532         tea->fops.owner = owner;
 533         tea->vd.fops = &tea->fops;
 534         /* disable hw_freq_seek if we can't use it */
 535         if (tea->cannot_read_data)
 536                 v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK);
 537 
 538         if (!tea->cannot_mute) {
 539                 tea->vd.ctrl_handler = &tea->ctrl_handler;
 540                 v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
 541                 v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
 542                                   V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
 543                 retval = tea->ctrl_handler.error;
 544                 if (retval) {
 545                         v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
 546                         v4l2_ctrl_handler_free(&tea->ctrl_handler);
 547                         return retval;
 548                 }
 549 
 550                 if (tea->ext_init) {
 551                         retval = tea->ext_init(tea);
 552                         if (retval) {
 553                                 v4l2_ctrl_handler_free(&tea->ctrl_handler);
 554                                 return retval;
 555                         }
 556                 }
 557 
 558                 v4l2_ctrl_handler_setup(&tea->ctrl_handler);
 559         }
 560 
 561         retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr);
 562         if (retval) {
 563                 v4l2_err(tea->v4l2_dev, "can't register video device!\n");
 564                 v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
 565                 return retval;
 566         }
 567 
 568         return 0;
 569 }
 570 EXPORT_SYMBOL(snd_tea575x_init);
 571 
 572 void snd_tea575x_exit(struct snd_tea575x *tea)
 573 {
 574         video_unregister_device(&tea->vd);
 575         v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
 576 }
 577 EXPORT_SYMBOL(snd_tea575x_exit);

/* [<][>][^][v][top][bottom][index][help] */