Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: allegro: add pm_runtime support

The allegro driver must ensure that the mcu and core clocks are enabled
and set to the expected clock rate before trying to load the firmware
and to reset the MCU.

Up until now, the driver assumed that the clocks are always enabled in
the PL (programming logic), because the xlnx_vcu driver did not export
the clocks to other drivers. This has changed and by explicitly enabling
the clocks in the driver, this assumption can be dropped.

It might even be possible to disable the clocks for the encoder if the
encoder is not used. However, the behavior is not documented and it
might be necessary to reinitialize the encoder after deactivating the
clocks. Play it safe by sticking to the current behavior.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

authored by

Michael Tretter and committed by
Mauro Carvalho Chehab
83cc5fd9 b6707e77

+84 -1
+84 -1
drivers/media/platform/allegro-dvt/allegro-core.c
··· 6 6 */ 7 7 8 8 #include <linux/bits.h> 9 + #include <linux/clk.h> 9 10 #include <linux/firmware.h> 10 11 #include <linux/gcd.h> 11 12 #include <linux/interrupt.h> ··· 19 18 #include <linux/of.h> 20 19 #include <linux/of_device.h> 21 20 #include <linux/platform_device.h> 21 + #include <linux/pm_runtime.h> 22 22 #include <linux/regmap.h> 23 23 #include <linux/sizes.h> 24 24 #include <linux/slab.h> ··· 141 139 struct regmap *regmap; 142 140 struct regmap *sram; 143 141 struct regmap *settings; 142 + 143 + struct clk *clk_core; 144 + struct clk *clk_mcu; 144 145 145 146 const struct fw_info *fw_info; 146 147 struct allegro_buffer firmware; ··· 3609 3604 v4l2_info(&dev->v4l2_dev, 3610 3605 "using mcu firmware version '%s'\n", dev->fw_info->version); 3611 3606 3607 + pm_runtime_enable(&dev->plat_dev->dev); 3608 + err = pm_runtime_resume_and_get(&dev->plat_dev->dev); 3609 + if (err) 3610 + goto err_release_firmware_codec; 3611 + 3612 3612 /* Ensure that the mcu is sleeping at the reset vector */ 3613 3613 err = allegro_mcu_reset(dev); 3614 3614 if (err) { 3615 3615 v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); 3616 - goto err_release_firmware_codec; 3616 + goto err_suspend; 3617 3617 } 3618 3618 3619 3619 allegro_copy_firmware(dev, fw->data, fw->size); ··· 3660 3650 allegro_mcu_hw_deinit(dev); 3661 3651 err_free_fw_codec: 3662 3652 allegro_free_fw_codec(dev); 3653 + err_suspend: 3654 + pm_runtime_put(&dev->plat_dev->dev); 3655 + pm_runtime_disable(&dev->plat_dev->dev); 3663 3656 err_release_firmware_codec: 3664 3657 release_firmware(fw_codec); 3665 3658 err_release_firmware: ··· 3741 3728 if (IS_ERR(dev->settings)) 3742 3729 dev_warn(&pdev->dev, "failed to open settings\n"); 3743 3730 3731 + dev->clk_core = devm_clk_get(&pdev->dev, "core_clk"); 3732 + if (IS_ERR(dev->clk_core)) 3733 + return PTR_ERR(dev->clk_core); 3734 + 3735 + dev->clk_mcu = devm_clk_get(&pdev->dev, "mcu_clk"); 3736 + if (IS_ERR(dev->clk_mcu)) 3737 + return PTR_ERR(dev->clk_mcu); 3738 + 3744 3739 irq = platform_get_irq(pdev, 0); 3745 3740 if (irq < 0) 3746 3741 return irq; ··· 3789 3768 allegro_free_fw_codec(dev); 3790 3769 } 3791 3770 3771 + pm_runtime_put(&dev->plat_dev->dev); 3772 + pm_runtime_disable(&dev->plat_dev->dev); 3773 + 3792 3774 v4l2_device_unregister(&dev->v4l2_dev); 3775 + 3776 + return 0; 3777 + } 3778 + 3779 + static int allegro_runtime_resume(struct device *device) 3780 + { 3781 + struct allegro_dev *dev = dev_get_drvdata(device); 3782 + struct regmap *settings = dev->settings; 3783 + unsigned int clk_mcu; 3784 + unsigned int clk_core; 3785 + int err; 3786 + 3787 + if (!settings) 3788 + return -EINVAL; 3789 + 3790 + #define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000) 3791 + 3792 + err = regmap_read(settings, VCU_CORE_CLK, &clk_core); 3793 + if (err < 0) 3794 + return err; 3795 + err = clk_set_rate(dev->clk_core, MHZ_TO_HZ(clk_core)); 3796 + if (err < 0) 3797 + return err; 3798 + err = clk_prepare_enable(dev->clk_core); 3799 + if (err) 3800 + return err; 3801 + 3802 + err = regmap_read(settings, VCU_MCU_CLK, &clk_mcu); 3803 + if (err < 0) 3804 + goto disable_clk_core; 3805 + err = clk_set_rate(dev->clk_mcu, MHZ_TO_HZ(clk_mcu)); 3806 + if (err < 0) 3807 + goto disable_clk_core; 3808 + err = clk_prepare_enable(dev->clk_mcu); 3809 + if (err) 3810 + goto disable_clk_core; 3811 + 3812 + #undef MHZ_TO_HZ 3813 + 3814 + return 0; 3815 + 3816 + disable_clk_core: 3817 + clk_disable_unprepare(dev->clk_core); 3818 + 3819 + return err; 3820 + } 3821 + 3822 + static int allegro_runtime_suspend(struct device *device) 3823 + { 3824 + struct allegro_dev *dev = dev_get_drvdata(device); 3825 + 3826 + clk_disable_unprepare(dev->clk_mcu); 3827 + clk_disable_unprepare(dev->clk_core); 3793 3828 3794 3829 return 0; 3795 3830 } ··· 3857 3780 3858 3781 MODULE_DEVICE_TABLE(of, allegro_dt_ids); 3859 3782 3783 + static const struct dev_pm_ops allegro_pm_ops = { 3784 + .runtime_resume = allegro_runtime_resume, 3785 + .runtime_suspend = allegro_runtime_suspend, 3786 + }; 3787 + 3860 3788 static struct platform_driver allegro_driver = { 3861 3789 .probe = allegro_probe, 3862 3790 .remove = allegro_remove, 3863 3791 .driver = { 3864 3792 .name = "allegro", 3865 3793 .of_match_table = of_match_ptr(allegro_dt_ids), 3794 + .pm = &allegro_pm_ops, 3866 3795 }, 3867 3796 }; 3868 3797