.. | .. |
---|
| 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 | |
---|