0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/module.h>
0012
0013 #include <linux/slab.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/delay.h>
0016 #include <linux/gpio.h>
0017 #include <linux/regulator/consumer.h>
0018 #include <linux/i2c.h>
0019 #include <linux/err.h>
0020
0021 #include <linux/mfd/si476x-core.h>
0022
0023 #define SI476X_MAX_IO_ERRORS 10
0024 #define SI476X_DRIVER_RDS_FIFO_DEPTH 128
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 static int si476x_core_config_pinmux(struct si476x_core *core)
0037 {
0038 int err;
0039 dev_dbg(&core->client->dev, "Configuring pinmux\n");
0040 err = si476x_core_cmd_dig_audio_pin_cfg(core,
0041 core->pinmux.dclk,
0042 core->pinmux.dfs,
0043 core->pinmux.dout,
0044 core->pinmux.xout);
0045 if (err < 0) {
0046 dev_err(&core->client->dev,
0047 "Failed to configure digital audio pins(err = %d)\n",
0048 err);
0049 return err;
0050 }
0051
0052 err = si476x_core_cmd_zif_pin_cfg(core,
0053 core->pinmux.iqclk,
0054 core->pinmux.iqfs,
0055 core->pinmux.iout,
0056 core->pinmux.qout);
0057 if (err < 0) {
0058 dev_err(&core->client->dev,
0059 "Failed to configure ZIF pins(err = %d)\n",
0060 err);
0061 return err;
0062 }
0063
0064 err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core,
0065 core->pinmux.icin,
0066 core->pinmux.icip,
0067 core->pinmux.icon,
0068 core->pinmux.icop);
0069 if (err < 0) {
0070 dev_err(&core->client->dev,
0071 "Failed to configure IC-Link/GPO pins(err = %d)\n",
0072 err);
0073 return err;
0074 }
0075
0076 err = si476x_core_cmd_ana_audio_pin_cfg(core,
0077 core->pinmux.lrout);
0078 if (err < 0) {
0079 dev_err(&core->client->dev,
0080 "Failed to configure analog audio pins(err = %d)\n",
0081 err);
0082 return err;
0083 }
0084
0085 err = si476x_core_cmd_intb_pin_cfg(core,
0086 core->pinmux.intb,
0087 core->pinmux.a1);
0088 if (err < 0) {
0089 dev_err(&core->client->dev,
0090 "Failed to configure interrupt pins(err = %d)\n",
0091 err);
0092 return err;
0093 }
0094
0095 return 0;
0096 }
0097
0098 static inline void si476x_core_schedule_polling_work(struct si476x_core *core)
0099 {
0100 schedule_delayed_work(&core->status_monitor,
0101 usecs_to_jiffies(SI476X_STATUS_POLL_US));
0102 }
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127 int si476x_core_start(struct si476x_core *core, bool soft)
0128 {
0129 struct i2c_client *client = core->client;
0130 int err;
0131
0132 if (!soft) {
0133 if (gpio_is_valid(core->gpio_reset))
0134 gpio_set_value_cansleep(core->gpio_reset, 1);
0135
0136 if (client->irq)
0137 enable_irq(client->irq);
0138
0139 udelay(100);
0140
0141 if (!client->irq) {
0142 atomic_set(&core->is_alive, 1);
0143 si476x_core_schedule_polling_work(core);
0144 }
0145 } else {
0146 if (client->irq)
0147 enable_irq(client->irq);
0148 else {
0149 atomic_set(&core->is_alive, 1);
0150 si476x_core_schedule_polling_work(core);
0151 }
0152 }
0153
0154 err = si476x_core_cmd_power_up(core,
0155 &core->power_up_parameters);
0156
0157 if (err < 0) {
0158 dev_err(&core->client->dev,
0159 "Power up failure(err = %d)\n",
0160 err);
0161 goto disable_irq;
0162 }
0163
0164 if (client->irq)
0165 atomic_set(&core->is_alive, 1);
0166
0167 err = si476x_core_config_pinmux(core);
0168 if (err < 0) {
0169 dev_err(&core->client->dev,
0170 "Failed to configure pinmux(err = %d)\n",
0171 err);
0172 goto disable_irq;
0173 }
0174
0175 if (client->irq) {
0176 err = regmap_write(core->regmap,
0177 SI476X_PROP_INT_CTL_ENABLE,
0178 SI476X_RDSIEN |
0179 SI476X_STCIEN |
0180 SI476X_CTSIEN);
0181 if (err < 0) {
0182 dev_err(&core->client->dev,
0183 "Failed to configure interrupt sources"
0184 "(err = %d)\n", err);
0185 goto disable_irq;
0186 }
0187 }
0188
0189 return 0;
0190
0191 disable_irq:
0192 if (err == -ENODEV)
0193 atomic_set(&core->is_alive, 0);
0194
0195 if (client->irq)
0196 disable_irq(client->irq);
0197 else
0198 cancel_delayed_work_sync(&core->status_monitor);
0199
0200 if (gpio_is_valid(core->gpio_reset))
0201 gpio_set_value_cansleep(core->gpio_reset, 0);
0202
0203 return err;
0204 }
0205 EXPORT_SYMBOL_GPL(si476x_core_start);
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221 int si476x_core_stop(struct si476x_core *core, bool soft)
0222 {
0223 int err = 0;
0224 atomic_set(&core->is_alive, 0);
0225
0226 if (soft) {
0227
0228
0229
0230
0231 struct si476x_power_down_args args = {
0232 .xosc = false,
0233 };
0234 err = si476x_core_cmd_power_down(core, &args);
0235 }
0236
0237
0238
0239
0240 if (core->client->irq)
0241 disable_irq(core->client->irq);
0242 else
0243 cancel_delayed_work_sync(&core->status_monitor);
0244
0245 if (!soft) {
0246 if (gpio_is_valid(core->gpio_reset))
0247 gpio_set_value_cansleep(core->gpio_reset, 0);
0248 }
0249 return err;
0250 }
0251 EXPORT_SYMBOL_GPL(si476x_core_stop);
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265 int si476x_core_set_power_state(struct si476x_core *core,
0266 enum si476x_power_state next_state)
0267 {
0268
0269
0270
0271
0272
0273 int err = 0;
0274
0275 if (core->power_state == SI476X_POWER_INCONSISTENT) {
0276 dev_err(&core->client->dev,
0277 "The device in inconsistent power state\n");
0278 return -EINVAL;
0279 }
0280
0281 if (next_state != core->power_state) {
0282 switch (next_state) {
0283 case SI476X_POWER_UP_FULL:
0284 err = regulator_bulk_enable(ARRAY_SIZE(core->supplies),
0285 core->supplies);
0286 if (err < 0) {
0287 core->power_state = SI476X_POWER_INCONSISTENT;
0288 break;
0289 }
0290
0291
0292
0293
0294
0295 udelay(100);
0296
0297 err = si476x_core_start(core, false);
0298 if (err < 0)
0299 goto disable_regulators;
0300
0301 core->power_state = next_state;
0302 break;
0303
0304 case SI476X_POWER_DOWN:
0305 core->power_state = next_state;
0306 err = si476x_core_stop(core, false);
0307 if (err < 0)
0308 core->power_state = SI476X_POWER_INCONSISTENT;
0309 disable_regulators:
0310 err = regulator_bulk_disable(ARRAY_SIZE(core->supplies),
0311 core->supplies);
0312 if (err < 0)
0313 core->power_state = SI476X_POWER_INCONSISTENT;
0314 break;
0315 default:
0316 BUG();
0317 }
0318 }
0319
0320 return err;
0321 }
0322 EXPORT_SYMBOL_GPL(si476x_core_set_power_state);
0323
0324
0325
0326
0327
0328
0329
0330 static inline void si476x_core_report_drainer_stop(struct si476x_core *core)
0331 {
0332 mutex_lock(&core->rds_drainer_status_lock);
0333 core->rds_drainer_is_working = false;
0334 mutex_unlock(&core->rds_drainer_status_lock);
0335 }
0336
0337
0338
0339
0340
0341
0342
0343 static inline void si476x_core_start_rds_drainer_once(struct si476x_core *core)
0344 {
0345 mutex_lock(&core->rds_drainer_status_lock);
0346 if (!core->rds_drainer_is_working) {
0347 core->rds_drainer_is_working = true;
0348 schedule_work(&core->rds_fifo_drainer);
0349 }
0350 mutex_unlock(&core->rds_drainer_status_lock);
0351 }
0352
0353
0354
0355
0356
0357
0358
0359 static void si476x_core_drain_rds_fifo(struct work_struct *work)
0360 {
0361 int err;
0362
0363 struct si476x_core *core = container_of(work, struct si476x_core,
0364 rds_fifo_drainer);
0365
0366 struct si476x_rds_status_report report;
0367
0368 si476x_core_lock(core);
0369 err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report);
0370 if (!err) {
0371 int i = report.rdsfifoused;
0372 dev_dbg(&core->client->dev,
0373 "%d elements in RDS FIFO. Draining.\n", i);
0374 for (; i > 0; --i) {
0375 err = si476x_core_cmd_fm_rds_status(core, false, false,
0376 (i == 1), &report);
0377 if (err < 0)
0378 goto unlock;
0379
0380 kfifo_in(&core->rds_fifo, report.rds,
0381 sizeof(report.rds));
0382 dev_dbg(&core->client->dev, "RDS data:\n %*ph\n",
0383 (int)sizeof(report.rds), report.rds);
0384 }
0385 dev_dbg(&core->client->dev, "Drrrrained!\n");
0386 wake_up_interruptible(&core->rds_read_queue);
0387 }
0388
0389 unlock:
0390 si476x_core_unlock(core);
0391 si476x_core_report_drainer_stop(core);
0392 }
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403 static void si476x_core_pronounce_dead(struct si476x_core *core)
0404 {
0405 dev_info(&core->client->dev, "Core device is dead.\n");
0406
0407 atomic_set(&core->is_alive, 0);
0408
0409
0410 wake_up_interruptible(&core->rds_read_queue);
0411
0412 atomic_set(&core->cts, 1);
0413 wake_up(&core->command);
0414
0415 atomic_set(&core->stc, 1);
0416 wake_up(&core->tuning);
0417 }
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428
0429
0430
0431
0432
0433
0434 int si476x_core_i2c_xfer(struct si476x_core *core,
0435 enum si476x_i2c_type type,
0436 char *buf, int count)
0437 {
0438 static int io_errors_count;
0439 int err;
0440 if (type == SI476X_I2C_SEND)
0441 err = i2c_master_send(core->client, buf, count);
0442 else
0443 err = i2c_master_recv(core->client, buf, count);
0444
0445 if (err < 0) {
0446 if (io_errors_count++ > SI476X_MAX_IO_ERRORS)
0447 si476x_core_pronounce_dead(core);
0448 } else {
0449 io_errors_count = 0;
0450 }
0451
0452 return err;
0453 }
0454 EXPORT_SYMBOL_GPL(si476x_core_i2c_xfer);
0455
0456
0457
0458
0459
0460
0461
0462
0463
0464
0465
0466 static int si476x_core_get_status(struct si476x_core *core)
0467 {
0468 u8 response;
0469 int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
0470 &response, sizeof(response));
0471
0472 return (err < 0) ? err : response;
0473 }
0474
0475
0476
0477
0478
0479
0480
0481
0482
0483 static void si476x_core_get_and_signal_status(struct si476x_core *core)
0484 {
0485 int status = si476x_core_get_status(core);
0486 if (status < 0) {
0487 dev_err(&core->client->dev, "Failed to get status\n");
0488 return;
0489 }
0490
0491 if (status & SI476X_CTS) {
0492
0493
0494
0495
0496
0497
0498 dev_dbg(&core->client->dev, "[interrupt] CTSINT\n");
0499 atomic_set(&core->cts, 1);
0500 wake_up(&core->command);
0501 }
0502
0503 if (status & SI476X_FM_RDS_INT) {
0504 dev_dbg(&core->client->dev, "[interrupt] RDSINT\n");
0505 si476x_core_start_rds_drainer_once(core);
0506 }
0507
0508 if (status & SI476X_STC_INT) {
0509 dev_dbg(&core->client->dev, "[interrupt] STCINT\n");
0510 atomic_set(&core->stc, 1);
0511 wake_up(&core->tuning);
0512 }
0513 }
0514
0515 static void si476x_core_poll_loop(struct work_struct *work)
0516 {
0517 struct si476x_core *core = SI476X_WORK_TO_CORE(work);
0518
0519 si476x_core_get_and_signal_status(core);
0520
0521 if (atomic_read(&core->is_alive))
0522 si476x_core_schedule_polling_work(core);
0523 }
0524
0525 static irqreturn_t si476x_core_interrupt(int irq, void *dev)
0526 {
0527 struct si476x_core *core = dev;
0528
0529 si476x_core_get_and_signal_status(core);
0530
0531 return IRQ_HANDLED;
0532 }
0533
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552 static int si476x_core_fwver_to_revision(struct si476x_core *core,
0553 int func, int major,
0554 int minor1, int minor2)
0555 {
0556 switch (func) {
0557 case SI476X_FUNC_FM_RECEIVER:
0558 switch (major) {
0559 case 5:
0560 return SI476X_REVISION_A10;
0561 case 8:
0562 return SI476X_REVISION_A20;
0563 case 10:
0564 return SI476X_REVISION_A30;
0565 default:
0566 goto unknown_revision;
0567 }
0568 case SI476X_FUNC_AM_RECEIVER:
0569 switch (major) {
0570 case 5:
0571 return SI476X_REVISION_A10;
0572 case 7:
0573 return SI476X_REVISION_A20;
0574 case 9:
0575 return SI476X_REVISION_A30;
0576 default:
0577 goto unknown_revision;
0578 }
0579 case SI476X_FUNC_WB_RECEIVER:
0580 switch (major) {
0581 case 3:
0582 return SI476X_REVISION_A10;
0583 case 5:
0584 return SI476X_REVISION_A20;
0585 case 7:
0586 return SI476X_REVISION_A30;
0587 default:
0588 goto unknown_revision;
0589 }
0590 case SI476X_FUNC_BOOTLOADER:
0591 default:
0592 BUG();
0593 return -1;
0594 }
0595
0596 unknown_revision:
0597 dev_err(&core->client->dev,
0598 "Unsupported version of the firmware: %d.%d.%d, "
0599 "reverting to A10 compatible functions\n",
0600 major, minor1, minor2);
0601
0602 return SI476X_REVISION_A10;
0603 }
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617
0618 static int si476x_core_get_revision_info(struct si476x_core *core)
0619 {
0620 int rval;
0621 struct si476x_func_info info;
0622
0623 si476x_core_lock(core);
0624 rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL);
0625 if (rval < 0)
0626 goto exit;
0627
0628 rval = si476x_core_cmd_func_info(core, &info);
0629 if (rval < 0)
0630 goto power_down;
0631
0632 core->revision = si476x_core_fwver_to_revision(core, info.func,
0633 info.firmware.major,
0634 info.firmware.minor[0],
0635 info.firmware.minor[1]);
0636 power_down:
0637 si476x_core_set_power_state(core, SI476X_POWER_DOWN);
0638 exit:
0639 si476x_core_unlock(core);
0640
0641 return rval;
0642 }
0643
0644 bool si476x_core_has_am(struct si476x_core *core)
0645 {
0646 return core->chip_id == SI476X_CHIP_SI4761 ||
0647 core->chip_id == SI476X_CHIP_SI4764;
0648 }
0649 EXPORT_SYMBOL_GPL(si476x_core_has_am);
0650
0651 bool si476x_core_has_diversity(struct si476x_core *core)
0652 {
0653 return core->chip_id == SI476X_CHIP_SI4764;
0654 }
0655 EXPORT_SYMBOL_GPL(si476x_core_has_diversity);
0656
0657 bool si476x_core_is_a_secondary_tuner(struct si476x_core *core)
0658 {
0659 return si476x_core_has_diversity(core) &&
0660 (core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA ||
0661 core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING);
0662 }
0663 EXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner);
0664
0665 bool si476x_core_is_a_primary_tuner(struct si476x_core *core)
0666 {
0667 return si476x_core_has_diversity(core) &&
0668 (core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA ||
0669 core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING);
0670 }
0671 EXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner);
0672
0673 bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core)
0674 {
0675 return si476x_core_has_am(core) &&
0676 (core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER);
0677 }
0678 EXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode);
0679
0680 bool si476x_core_is_powered_up(struct si476x_core *core)
0681 {
0682 return core->power_state == SI476X_POWER_UP_FULL;
0683 }
0684 EXPORT_SYMBOL_GPL(si476x_core_is_powered_up);
0685
0686 static int si476x_core_probe(struct i2c_client *client,
0687 const struct i2c_device_id *id)
0688 {
0689 int rval;
0690 struct si476x_core *core;
0691 struct si476x_platform_data *pdata;
0692 struct mfd_cell *cell;
0693 int cell_num;
0694
0695 core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);
0696 if (!core)
0697 return -ENOMEM;
0698
0699 core->client = client;
0700
0701 core->regmap = devm_regmap_init_si476x(core);
0702 if (IS_ERR(core->regmap)) {
0703 rval = PTR_ERR(core->regmap);
0704 dev_err(&client->dev,
0705 "Failed to allocate register map: %d\n",
0706 rval);
0707 return rval;
0708 }
0709
0710 i2c_set_clientdata(client, core);
0711
0712 atomic_set(&core->is_alive, 0);
0713 core->power_state = SI476X_POWER_DOWN;
0714
0715 pdata = dev_get_platdata(&client->dev);
0716 if (pdata) {
0717 memcpy(&core->power_up_parameters,
0718 &pdata->power_up_parameters,
0719 sizeof(core->power_up_parameters));
0720
0721 core->gpio_reset = -1;
0722 if (gpio_is_valid(pdata->gpio_reset)) {
0723 rval = gpio_request(pdata->gpio_reset, "si476x reset");
0724 if (rval) {
0725 dev_err(&client->dev,
0726 "Failed to request gpio: %d\n", rval);
0727 return rval;
0728 }
0729 core->gpio_reset = pdata->gpio_reset;
0730 gpio_direction_output(core->gpio_reset, 0);
0731 }
0732
0733 core->diversity_mode = pdata->diversity_mode;
0734 memcpy(&core->pinmux, &pdata->pinmux,
0735 sizeof(struct si476x_pinmux));
0736 } else {
0737 dev_err(&client->dev, "No platform data provided\n");
0738 return -EINVAL;
0739 }
0740
0741 core->supplies[0].supply = "vd";
0742 core->supplies[1].supply = "va";
0743 core->supplies[2].supply = "vio1";
0744 core->supplies[3].supply = "vio2";
0745
0746 rval = devm_regulator_bulk_get(&client->dev,
0747 ARRAY_SIZE(core->supplies),
0748 core->supplies);
0749 if (rval) {
0750 dev_err(&client->dev, "Failed to get all of the regulators\n");
0751 goto free_gpio;
0752 }
0753
0754 mutex_init(&core->cmd_lock);
0755 init_waitqueue_head(&core->command);
0756 init_waitqueue_head(&core->tuning);
0757
0758 rval = kfifo_alloc(&core->rds_fifo,
0759 SI476X_DRIVER_RDS_FIFO_DEPTH *
0760 sizeof(struct v4l2_rds_data),
0761 GFP_KERNEL);
0762 if (rval) {
0763 dev_err(&client->dev, "Could not allocate the FIFO\n");
0764 goto free_gpio;
0765 }
0766 mutex_init(&core->rds_drainer_status_lock);
0767 init_waitqueue_head(&core->rds_read_queue);
0768 INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo);
0769
0770 if (client->irq) {
0771 rval = devm_request_threaded_irq(&client->dev,
0772 client->irq, NULL,
0773 si476x_core_interrupt,
0774 IRQF_TRIGGER_FALLING |
0775 IRQF_ONESHOT,
0776 client->name, core);
0777 if (rval < 0) {
0778 dev_err(&client->dev, "Could not request IRQ %d\n",
0779 client->irq);
0780 goto free_kfifo;
0781 }
0782 disable_irq(client->irq);
0783 dev_dbg(&client->dev, "IRQ requested.\n");
0784
0785 core->rds_fifo_depth = 20;
0786 } else {
0787 INIT_DELAYED_WORK(&core->status_monitor,
0788 si476x_core_poll_loop);
0789 dev_info(&client->dev,
0790 "No IRQ number specified, will use polling\n");
0791
0792 core->rds_fifo_depth = 5;
0793 }
0794
0795 core->chip_id = id->driver_data;
0796
0797 rval = si476x_core_get_revision_info(core);
0798 if (rval < 0) {
0799 rval = -ENODEV;
0800 goto free_kfifo;
0801 }
0802
0803 cell_num = 0;
0804
0805 cell = &core->cells[SI476X_RADIO_CELL];
0806 cell->name = "si476x-radio";
0807 cell_num++;
0808
0809 #ifdef CONFIG_SND_SOC_SI476X
0810 if ((core->chip_id == SI476X_CHIP_SI4761 ||
0811 core->chip_id == SI476X_CHIP_SI4764) &&
0812 core->pinmux.dclk == SI476X_DCLK_DAUDIO &&
0813 core->pinmux.dfs == SI476X_DFS_DAUDIO &&
0814 core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT &&
0815 core->pinmux.xout == SI476X_XOUT_TRISTATE) {
0816 cell = &core->cells[SI476X_CODEC_CELL];
0817 cell->name = "si476x-codec";
0818 cell_num++;
0819 }
0820 #endif
0821 rval = mfd_add_devices(&client->dev,
0822 (client->adapter->nr << 8) + client->addr,
0823 core->cells, cell_num,
0824 NULL, 0, NULL);
0825 if (!rval)
0826 return 0;
0827
0828 free_kfifo:
0829 kfifo_free(&core->rds_fifo);
0830
0831 free_gpio:
0832 if (gpio_is_valid(core->gpio_reset))
0833 gpio_free(core->gpio_reset);
0834
0835 return rval;
0836 }
0837
0838 static int si476x_core_remove(struct i2c_client *client)
0839 {
0840 struct si476x_core *core = i2c_get_clientdata(client);
0841
0842 si476x_core_pronounce_dead(core);
0843 mfd_remove_devices(&client->dev);
0844
0845 if (client->irq)
0846 disable_irq(client->irq);
0847 else
0848 cancel_delayed_work_sync(&core->status_monitor);
0849
0850 kfifo_free(&core->rds_fifo);
0851
0852 if (gpio_is_valid(core->gpio_reset))
0853 gpio_free(core->gpio_reset);
0854
0855 return 0;
0856 }
0857
0858
0859 static const struct i2c_device_id si476x_id[] = {
0860 { "si4761", SI476X_CHIP_SI4761 },
0861 { "si4764", SI476X_CHIP_SI4764 },
0862 { "si4768", SI476X_CHIP_SI4768 },
0863 { },
0864 };
0865 MODULE_DEVICE_TABLE(i2c, si476x_id);
0866
0867 static struct i2c_driver si476x_core_driver = {
0868 .driver = {
0869 .name = "si476x-core",
0870 },
0871 .probe = si476x_core_probe,
0872 .remove = si476x_core_remove,
0873 .id_table = si476x_id,
0874 };
0875 module_i2c_driver(si476x_core_driver);
0876
0877
0878 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
0879 MODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver");
0880 MODULE_LICENSE("GPL");