| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * drivers/media/radio/si470x/radio-si470x-i2c.c |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Copyright (c) 2009 Samsung Electronics Co.Ltd |
|---|
| 7 | 8 | * Author: Joonyoung Shim <jy0922.shim@samsung.com> |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 11 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 12 | | - * (at your option) any later version. |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 15 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 17 | | - * GNU General Public License for more details. |
|---|
| 18 | 9 | */ |
|---|
| 19 | 10 | |
|---|
| 20 | 11 | |
|---|
| .. | .. |
|---|
| 28 | 19 | #include <linux/i2c.h> |
|---|
| 29 | 20 | #include <linux/slab.h> |
|---|
| 30 | 21 | #include <linux/delay.h> |
|---|
| 22 | +#include <linux/gpio/consumer.h> |
|---|
| 31 | 23 | #include <linux/interrupt.h> |
|---|
| 32 | 24 | |
|---|
| 33 | 25 | #include "radio-si470x.h" |
|---|
| .. | .. |
|---|
| 229 | 221 | static int si470x_vidioc_querycap(struct file *file, void *priv, |
|---|
| 230 | 222 | struct v4l2_capability *capability) |
|---|
| 231 | 223 | { |
|---|
| 232 | | - strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); |
|---|
| 233 | | - strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); |
|---|
| 234 | | - capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | |
|---|
| 235 | | - V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; |
|---|
| 236 | | - capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; |
|---|
| 237 | | - |
|---|
| 224 | + strscpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); |
|---|
| 225 | + strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); |
|---|
| 238 | 226 | return 0; |
|---|
| 239 | 227 | } |
|---|
| 240 | 228 | |
|---|
| .. | .. |
|---|
| 342 | 330 | /* |
|---|
| 343 | 331 | * si470x_i2c_probe - probe for the device |
|---|
| 344 | 332 | */ |
|---|
| 345 | | -static int si470x_i2c_probe(struct i2c_client *client, |
|---|
| 346 | | - const struct i2c_device_id *id) |
|---|
| 333 | +static int si470x_i2c_probe(struct i2c_client *client) |
|---|
| 347 | 334 | { |
|---|
| 348 | 335 | struct si470x_device *radio; |
|---|
| 349 | 336 | int retval = 0; |
|---|
| 350 | 337 | unsigned char version_warning = 0; |
|---|
| 351 | 338 | |
|---|
| 352 | 339 | /* private data allocation and initialization */ |
|---|
| 353 | | - radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); |
|---|
| 340 | + radio = devm_kzalloc(&client->dev, sizeof(*radio), GFP_KERNEL); |
|---|
| 354 | 341 | if (!radio) { |
|---|
| 355 | 342 | retval = -ENOMEM; |
|---|
| 356 | 343 | goto err_initial; |
|---|
| .. | .. |
|---|
| 370 | 357 | retval = v4l2_device_register(&client->dev, &radio->v4l2_dev); |
|---|
| 371 | 358 | if (retval < 0) { |
|---|
| 372 | 359 | dev_err(&client->dev, "couldn't register v4l2_device\n"); |
|---|
| 373 | | - goto err_radio; |
|---|
| 360 | + goto err_initial; |
|---|
| 374 | 361 | } |
|---|
| 375 | 362 | |
|---|
| 376 | 363 | v4l2_ctrl_handler_init(&radio->hdl, 2); |
|---|
| .. | .. |
|---|
| 390 | 377 | radio->videodev.lock = &radio->lock; |
|---|
| 391 | 378 | radio->videodev.v4l2_dev = &radio->v4l2_dev; |
|---|
| 392 | 379 | radio->videodev.release = video_device_release_empty; |
|---|
| 380 | + radio->videodev.device_caps = |
|---|
| 381 | + V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | V4L2_CAP_TUNER | |
|---|
| 382 | + V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; |
|---|
| 393 | 383 | video_set_drvdata(&radio->videodev, radio); |
|---|
| 384 | + |
|---|
| 385 | + radio->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", |
|---|
| 386 | + GPIOD_OUT_LOW); |
|---|
| 387 | + if (IS_ERR(radio->gpio_reset)) { |
|---|
| 388 | + retval = PTR_ERR(radio->gpio_reset); |
|---|
| 389 | + dev_err(&client->dev, "Failed to request gpio: %d\n", retval); |
|---|
| 390 | + goto err_all; |
|---|
| 391 | + } |
|---|
| 392 | + |
|---|
| 393 | + if (radio->gpio_reset) |
|---|
| 394 | + gpiod_set_value(radio->gpio_reset, 1); |
|---|
| 394 | 395 | |
|---|
| 395 | 396 | /* power up : need 110ms */ |
|---|
| 396 | 397 | radio->registers[POWERCFG] = POWERCFG_ENABLE; |
|---|
| 397 | 398 | if (si470x_set_register(radio, POWERCFG) < 0) { |
|---|
| 398 | 399 | retval = -EIO; |
|---|
| 399 | | - goto err_ctrl; |
|---|
| 400 | + goto err_all; |
|---|
| 400 | 401 | } |
|---|
| 401 | 402 | msleep(110); |
|---|
| 402 | 403 | |
|---|
| 403 | 404 | /* get device and chip versions */ |
|---|
| 404 | 405 | if (si470x_get_all_registers(radio) < 0) { |
|---|
| 405 | 406 | retval = -EIO; |
|---|
| 406 | | - goto err_ctrl; |
|---|
| 407 | + goto err_all; |
|---|
| 407 | 408 | } |
|---|
| 408 | 409 | dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", |
|---|
| 409 | 410 | radio->registers[DEVICEID], radio->registers[SI_CHIPID]); |
|---|
| .. | .. |
|---|
| 430 | 431 | |
|---|
| 431 | 432 | /* rds buffer allocation */ |
|---|
| 432 | 433 | radio->buf_size = rds_buf * 3; |
|---|
| 433 | | - radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); |
|---|
| 434 | + radio->buffer = devm_kmalloc(&client->dev, radio->buf_size, GFP_KERNEL); |
|---|
| 434 | 435 | if (!radio->buffer) { |
|---|
| 435 | 436 | retval = -EIO; |
|---|
| 436 | | - goto err_ctrl; |
|---|
| 437 | + goto err_all; |
|---|
| 437 | 438 | } |
|---|
| 438 | 439 | |
|---|
| 439 | 440 | /* rds buffer configuration */ |
|---|
| .. | .. |
|---|
| 441 | 442 | radio->rd_index = 0; |
|---|
| 442 | 443 | init_waitqueue_head(&radio->read_queue); |
|---|
| 443 | 444 | |
|---|
| 444 | | - retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt, |
|---|
| 445 | | - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, DRIVER_NAME, |
|---|
| 446 | | - radio); |
|---|
| 445 | + retval = devm_request_threaded_irq(&client->dev, client->irq, NULL, |
|---|
| 446 | + si470x_i2c_interrupt, |
|---|
| 447 | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
|---|
| 448 | + DRIVER_NAME, radio); |
|---|
| 447 | 449 | if (retval) { |
|---|
| 448 | 450 | dev_err(&client->dev, "Failed to register interrupt\n"); |
|---|
| 449 | | - goto err_rds; |
|---|
| 451 | + goto err_all; |
|---|
| 450 | 452 | } |
|---|
| 451 | 453 | |
|---|
| 452 | 454 | /* register video device */ |
|---|
| .. | .. |
|---|
| 460 | 462 | |
|---|
| 461 | 463 | return 0; |
|---|
| 462 | 464 | err_all: |
|---|
| 463 | | - free_irq(client->irq, radio); |
|---|
| 464 | | -err_rds: |
|---|
| 465 | | - kfree(radio->buffer); |
|---|
| 466 | | -err_ctrl: |
|---|
| 467 | 465 | v4l2_ctrl_handler_free(&radio->hdl); |
|---|
| 468 | 466 | v4l2_device_unregister(&radio->v4l2_dev); |
|---|
| 469 | | -err_radio: |
|---|
| 470 | | - kfree(radio); |
|---|
| 471 | 467 | err_initial: |
|---|
| 472 | 468 | return retval; |
|---|
| 473 | 469 | } |
|---|
| .. | .. |
|---|
| 480 | 476 | { |
|---|
| 481 | 477 | struct si470x_device *radio = i2c_get_clientdata(client); |
|---|
| 482 | 478 | |
|---|
| 483 | | - free_irq(client->irq, radio); |
|---|
| 484 | 479 | video_unregister_device(&radio->videodev); |
|---|
| 480 | + |
|---|
| 481 | + if (radio->gpio_reset) |
|---|
| 482 | + gpiod_set_value(radio->gpio_reset, 0); |
|---|
| 485 | 483 | |
|---|
| 486 | 484 | v4l2_ctrl_handler_free(&radio->hdl); |
|---|
| 487 | 485 | v4l2_device_unregister(&radio->v4l2_dev); |
|---|
| 488 | | - kfree(radio); |
|---|
| 489 | 486 | return 0; |
|---|
| 490 | 487 | } |
|---|
| 491 | 488 | |
|---|
| .. | .. |
|---|
| 528 | 525 | static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume); |
|---|
| 529 | 526 | #endif |
|---|
| 530 | 527 | |
|---|
| 528 | +#if IS_ENABLED(CONFIG_OF) |
|---|
| 529 | +static const struct of_device_id si470x_of_match[] = { |
|---|
| 530 | + { .compatible = "silabs,si470x" }, |
|---|
| 531 | + { }, |
|---|
| 532 | +}; |
|---|
| 533 | +MODULE_DEVICE_TABLE(of, si470x_of_match); |
|---|
| 534 | +#endif |
|---|
| 531 | 535 | |
|---|
| 532 | 536 | /* |
|---|
| 533 | 537 | * si470x_i2c_driver - i2c driver interface |
|---|
| .. | .. |
|---|
| 535 | 539 | static struct i2c_driver si470x_i2c_driver = { |
|---|
| 536 | 540 | .driver = { |
|---|
| 537 | 541 | .name = "si470x", |
|---|
| 542 | + .of_match_table = of_match_ptr(si470x_of_match), |
|---|
| 538 | 543 | #ifdef CONFIG_PM_SLEEP |
|---|
| 539 | 544 | .pm = &si470x_i2c_pm, |
|---|
| 540 | 545 | #endif |
|---|
| 541 | 546 | }, |
|---|
| 542 | | - .probe = si470x_i2c_probe, |
|---|
| 547 | + .probe_new = si470x_i2c_probe, |
|---|
| 543 | 548 | .remove = si470x_i2c_remove, |
|---|
| 544 | 549 | .id_table = si470x_i2c_id, |
|---|
| 545 | 550 | }; |
|---|