| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe" |
|---|
| 3 | 4 | * multifunction chip. Currently works with the Omnivision OV7670 |
|---|
| 4 | 5 | * sensor. |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * The data sheet for this device can be found at: |
|---|
| 7 | | - * http://www.marvell.com/products/pc_connectivity/88alp01/ |
|---|
| 8 | + * http://wiki.laptop.org/images/5/5c/88ALP01_Datasheet_July_2007.pdf |
|---|
| 8 | 9 | * |
|---|
| 9 | 10 | * Copyright 2006-11 One Laptop Per Child Association, Inc. |
|---|
| 10 | 11 | * Copyright 2006-11 Jonathan Corbet <corbet@lwn.net> |
|---|
| 12 | + * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk> |
|---|
| 11 | 13 | * |
|---|
| 12 | 14 | * Written by Jonathan Corbet, corbet@lwn.net. |
|---|
| 13 | 15 | * |
|---|
| 14 | 16 | * v4l2_device/v4l2_subdev conversion by: |
|---|
| 15 | 17 | * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl> |
|---|
| 16 | | - * |
|---|
| 17 | | - * This file may be distributed under the terms of the GNU General |
|---|
| 18 | | - * Public License, version 2. |
|---|
| 19 | 18 | */ |
|---|
| 20 | 19 | #include <linux/kernel.h> |
|---|
| 21 | 20 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 27 | 26 | #include <linux/slab.h> |
|---|
| 28 | 27 | #include <linux/videodev2.h> |
|---|
| 29 | 28 | #include <media/v4l2-device.h> |
|---|
| 29 | +#include <media/i2c/ov7670.h> |
|---|
| 30 | 30 | #include <linux/device.h> |
|---|
| 31 | 31 | #include <linux/wait.h> |
|---|
| 32 | 32 | #include <linux/delay.h> |
|---|
| 33 | 33 | #include <linux/io.h> |
|---|
| 34 | +#include <linux/clkdev.h> |
|---|
| 34 | 35 | |
|---|
| 35 | 36 | #include "mcam-core.h" |
|---|
| 36 | 37 | |
|---|
| .. | .. |
|---|
| 52 | 53 | int registered; /* Fully initialized? */ |
|---|
| 53 | 54 | struct mcam_camera mcam; |
|---|
| 54 | 55 | struct pci_dev *pdev; |
|---|
| 56 | + struct i2c_adapter *i2c_adapter; |
|---|
| 55 | 57 | wait_queue_head_t smbus_wait; /* Waiting on i2c events */ |
|---|
| 56 | 58 | }; |
|---|
| 57 | 59 | |
|---|
| .. | .. |
|---|
| 341 | 343 | return -ENOMEM; |
|---|
| 342 | 344 | adap->owner = THIS_MODULE; |
|---|
| 343 | 345 | adap->algo = &cafe_smbus_algo; |
|---|
| 344 | | - strcpy(adap->name, "cafe_ccic"); |
|---|
| 346 | + strscpy(adap->name, "cafe_ccic", sizeof(adap->name)); |
|---|
| 345 | 347 | adap->dev.parent = &cam->pdev->dev; |
|---|
| 346 | 348 | i2c_set_adapdata(adap, cam); |
|---|
| 347 | 349 | ret = i2c_add_adapter(adap); |
|---|
| .. | .. |
|---|
| 351 | 353 | return ret; |
|---|
| 352 | 354 | } |
|---|
| 353 | 355 | |
|---|
| 354 | | - cam->mcam.i2c_adapter = adap; |
|---|
| 356 | + cam->i2c_adapter = adap; |
|---|
| 355 | 357 | cafe_smbus_enable_irq(cam); |
|---|
| 356 | 358 | return 0; |
|---|
| 357 | 359 | } |
|---|
| 358 | 360 | |
|---|
| 359 | 361 | static void cafe_smbus_shutdown(struct cafe_camera *cam) |
|---|
| 360 | 362 | { |
|---|
| 361 | | - i2c_del_adapter(cam->mcam.i2c_adapter); |
|---|
| 362 | | - kfree(cam->mcam.i2c_adapter); |
|---|
| 363 | + i2c_del_adapter(cam->i2c_adapter); |
|---|
| 364 | + kfree(cam->i2c_adapter); |
|---|
| 363 | 365 | } |
|---|
| 364 | 366 | |
|---|
| 365 | 367 | |
|---|
| .. | .. |
|---|
| 452 | 454 | return IRQ_RETVAL(handled); |
|---|
| 453 | 455 | } |
|---|
| 454 | 456 | |
|---|
| 457 | +/* -------------------------------------------------------------------------- */ |
|---|
| 458 | + |
|---|
| 459 | +static struct ov7670_config sensor_cfg = { |
|---|
| 460 | + /* |
|---|
| 461 | + * Exclude QCIF mode, because it only captures a tiny portion |
|---|
| 462 | + * of the sensor FOV |
|---|
| 463 | + */ |
|---|
| 464 | + .min_width = 320, |
|---|
| 465 | + .min_height = 240, |
|---|
| 466 | + |
|---|
| 467 | + /* |
|---|
| 468 | + * Set the clock speed for the XO 1; I don't believe this |
|---|
| 469 | + * driver has ever run anywhere else. |
|---|
| 470 | + */ |
|---|
| 471 | + .clock_speed = 45, |
|---|
| 472 | + .use_smbus = 1, |
|---|
| 473 | +}; |
|---|
| 474 | + |
|---|
| 475 | +static struct i2c_board_info ov7670_info = { |
|---|
| 476 | + .type = "ov7670", |
|---|
| 477 | + .addr = 0x42 >> 1, |
|---|
| 478 | + .platform_data = &sensor_cfg, |
|---|
| 479 | +}; |
|---|
| 455 | 480 | |
|---|
| 456 | 481 | /* -------------------------------------------------------------------------- */ |
|---|
| 457 | 482 | /* |
|---|
| .. | .. |
|---|
| 472 | 497 | cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); |
|---|
| 473 | 498 | if (cam == NULL) |
|---|
| 474 | 499 | goto out; |
|---|
| 500 | + pci_set_drvdata(pdev, cam); |
|---|
| 475 | 501 | cam->pdev = pdev; |
|---|
| 476 | 502 | mcam = &cam->mcam; |
|---|
| 477 | 503 | mcam->chip_id = MCAM_CAFE; |
|---|
| .. | .. |
|---|
| 481 | 507 | mcam->plat_power_down = cafe_ctlr_power_down; |
|---|
| 482 | 508 | mcam->dev = &pdev->dev; |
|---|
| 483 | 509 | snprintf(mcam->bus_info, sizeof(mcam->bus_info), "PCI:%s", pci_name(pdev)); |
|---|
| 484 | | - /* |
|---|
| 485 | | - * Set the clock speed for the XO 1; I don't believe this |
|---|
| 486 | | - * driver has ever run anywhere else. |
|---|
| 487 | | - */ |
|---|
| 488 | | - mcam->clock_speed = 45; |
|---|
| 489 | | - mcam->use_smbus = 1; |
|---|
| 490 | 510 | /* |
|---|
| 491 | 511 | * Vmalloc mode for buffers is traditional with this driver. |
|---|
| 492 | 512 | * We *might* be able to run DMA_contig, especially on a system |
|---|
| .. | .. |
|---|
| 513 | 533 | goto out_iounmap; |
|---|
| 514 | 534 | |
|---|
| 515 | 535 | /* |
|---|
| 516 | | - * Initialize the controller and leave it powered up. It will |
|---|
| 517 | | - * stay that way until the sensor driver shows up. |
|---|
| 536 | + * Initialize the controller. |
|---|
| 518 | 537 | */ |
|---|
| 519 | 538 | cafe_ctlr_init(mcam); |
|---|
| 520 | | - cafe_ctlr_power_up(mcam); |
|---|
| 539 | + |
|---|
| 521 | 540 | /* |
|---|
| 522 | 541 | * Set up I2C/SMBUS communications. We have to drop the mutex here |
|---|
| 523 | 542 | * because the sensor could attach in this call chain, leading to |
|---|
| .. | .. |
|---|
| 527 | 546 | if (ret) |
|---|
| 528 | 547 | goto out_pdown; |
|---|
| 529 | 548 | |
|---|
| 549 | + mcam->asd.match_type = V4L2_ASYNC_MATCH_I2C; |
|---|
| 550 | + mcam->asd.match.i2c.adapter_id = i2c_adapter_id(cam->i2c_adapter); |
|---|
| 551 | + mcam->asd.match.i2c.address = ov7670_info.addr; |
|---|
| 552 | + |
|---|
| 530 | 553 | ret = mccic_register(mcam); |
|---|
| 531 | | - if (ret == 0) { |
|---|
| 554 | + if (ret) |
|---|
| 555 | + goto out_smbus_shutdown; |
|---|
| 556 | + |
|---|
| 557 | + clkdev_create(mcam->mclk, "xclk", "%d-%04x", |
|---|
| 558 | + i2c_adapter_id(cam->i2c_adapter), ov7670_info.addr); |
|---|
| 559 | + |
|---|
| 560 | + if (!IS_ERR(i2c_new_client_device(cam->i2c_adapter, &ov7670_info))) { |
|---|
| 532 | 561 | cam->registered = 1; |
|---|
| 533 | 562 | return 0; |
|---|
| 534 | 563 | } |
|---|
| 535 | 564 | |
|---|
| 565 | + mccic_shutdown(mcam); |
|---|
| 566 | +out_smbus_shutdown: |
|---|
| 536 | 567 | cafe_smbus_shutdown(cam); |
|---|
| 537 | 568 | out_pdown: |
|---|
| 538 | 569 | cafe_ctlr_power_down(mcam); |
|---|
| .. | .. |
|---|
| 562 | 593 | |
|---|
| 563 | 594 | static void cafe_pci_remove(struct pci_dev *pdev) |
|---|
| 564 | 595 | { |
|---|
| 565 | | - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); |
|---|
| 566 | | - struct cafe_camera *cam = to_cam(v4l2_dev); |
|---|
| 596 | + struct cafe_camera *cam = pci_get_drvdata(pdev); |
|---|
| 567 | 597 | |
|---|
| 568 | 598 | if (cam == NULL) { |
|---|
| 569 | 599 | printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev); |
|---|
| .. | .. |
|---|
| 574 | 604 | } |
|---|
| 575 | 605 | |
|---|
| 576 | 606 | |
|---|
| 577 | | -#ifdef CONFIG_PM |
|---|
| 578 | 607 | /* |
|---|
| 579 | 608 | * Basic power management. |
|---|
| 580 | 609 | */ |
|---|
| 581 | | -static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) |
|---|
| 610 | +static int __maybe_unused cafe_pci_suspend(struct device *dev) |
|---|
| 582 | 611 | { |
|---|
| 583 | | - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); |
|---|
| 584 | | - struct cafe_camera *cam = to_cam(v4l2_dev); |
|---|
| 585 | | - int ret; |
|---|
| 612 | + struct cafe_camera *cam = dev_get_drvdata(dev); |
|---|
| 586 | 613 | |
|---|
| 587 | | - ret = pci_save_state(pdev); |
|---|
| 588 | | - if (ret) |
|---|
| 589 | | - return ret; |
|---|
| 590 | 614 | mccic_suspend(&cam->mcam); |
|---|
| 591 | | - pci_disable_device(pdev); |
|---|
| 592 | 615 | return 0; |
|---|
| 593 | 616 | } |
|---|
| 594 | 617 | |
|---|
| 595 | 618 | |
|---|
| 596 | | -static int cafe_pci_resume(struct pci_dev *pdev) |
|---|
| 619 | +static int __maybe_unused cafe_pci_resume(struct device *dev) |
|---|
| 597 | 620 | { |
|---|
| 598 | | - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); |
|---|
| 599 | | - struct cafe_camera *cam = to_cam(v4l2_dev); |
|---|
| 600 | | - int ret = 0; |
|---|
| 621 | + struct cafe_camera *cam = dev_get_drvdata(dev); |
|---|
| 601 | 622 | |
|---|
| 602 | | - pci_restore_state(pdev); |
|---|
| 603 | | - ret = pci_enable_device(pdev); |
|---|
| 604 | | - |
|---|
| 605 | | - if (ret) { |
|---|
| 606 | | - cam_warn(cam, "Unable to re-enable device on resume!\n"); |
|---|
| 607 | | - return ret; |
|---|
| 608 | | - } |
|---|
| 609 | 623 | cafe_ctlr_init(&cam->mcam); |
|---|
| 610 | 624 | return mccic_resume(&cam->mcam); |
|---|
| 611 | 625 | } |
|---|
| 612 | | - |
|---|
| 613 | | -#endif /* CONFIG_PM */ |
|---|
| 614 | 626 | |
|---|
| 615 | 627 | static const struct pci_device_id cafe_ids[] = { |
|---|
| 616 | 628 | { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, |
|---|
| .. | .. |
|---|
| 620 | 632 | |
|---|
| 621 | 633 | MODULE_DEVICE_TABLE(pci, cafe_ids); |
|---|
| 622 | 634 | |
|---|
| 635 | +static SIMPLE_DEV_PM_OPS(cafe_pci_pm_ops, cafe_pci_suspend, cafe_pci_resume); |
|---|
| 636 | + |
|---|
| 623 | 637 | static struct pci_driver cafe_pci_driver = { |
|---|
| 624 | 638 | .name = "cafe1000-ccic", |
|---|
| 625 | 639 | .id_table = cafe_ids, |
|---|
| 626 | 640 | .probe = cafe_pci_probe, |
|---|
| 627 | 641 | .remove = cafe_pci_remove, |
|---|
| 628 | | -#ifdef CONFIG_PM |
|---|
| 629 | | - .suspend = cafe_pci_suspend, |
|---|
| 630 | | - .resume = cafe_pci_resume, |
|---|
| 631 | | -#endif |
|---|
| 642 | + .driver.pm = &cafe_pci_pm_ops, |
|---|
| 632 | 643 | }; |
|---|
| 633 | 644 | |
|---|
| 634 | 645 | |
|---|