Skip to content

Commit

Permalink
media: allegro: add pm_runtime support
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Hans Verkuil <[email protected]>
Signed-off-by: Mauro Carvalho Chehab <[email protected]>
  • Loading branch information
tretter authored and mchehab committed Oct 20, 2021
1 parent b6707e7 commit 83cc5fd
Showing 1 changed file with 84 additions and 1 deletion.
85 changes: 84 additions & 1 deletion drivers/media/platform/allegro-dvt/allegro-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/gcd.h>
#include <linux/interrupt.h>
Expand All @@ -18,6 +19,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
#include <linux/slab.h>
Expand Down Expand Up @@ -140,6 +142,9 @@ struct allegro_dev {
struct regmap *sram;
struct regmap *settings;

struct clk *clk_core;
struct clk *clk_mcu;

const struct fw_info *fw_info;
struct allegro_buffer firmware;
struct allegro_buffer suballocator;
Expand Down Expand Up @@ -3604,11 +3609,16 @@ static void allegro_fw_callback(const struct firmware *fw, void *context)
v4l2_info(&dev->v4l2_dev,
"using mcu firmware version '%s'\n", dev->fw_info->version);

pm_runtime_enable(&dev->plat_dev->dev);
err = pm_runtime_resume_and_get(&dev->plat_dev->dev);
if (err)
goto err_release_firmware_codec;

/* Ensure that the mcu is sleeping at the reset vector */
err = allegro_mcu_reset(dev);
if (err) {
v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n");
goto err_release_firmware_codec;
goto err_suspend;
}

allegro_copy_firmware(dev, fw->data, fw->size);
Expand Down Expand Up @@ -3650,6 +3660,9 @@ static void allegro_fw_callback(const struct firmware *fw, void *context)
allegro_mcu_hw_deinit(dev);
err_free_fw_codec:
allegro_free_fw_codec(dev);
err_suspend:
pm_runtime_put(&dev->plat_dev->dev);
pm_runtime_disable(&dev->plat_dev->dev);
err_release_firmware_codec:
release_firmware(fw_codec);
err_release_firmware:
Expand Down Expand Up @@ -3728,6 +3741,14 @@ static int allegro_probe(struct platform_device *pdev)
if (IS_ERR(dev->settings))
dev_warn(&pdev->dev, "failed to open settings\n");

dev->clk_core = devm_clk_get(&pdev->dev, "core_clk");
if (IS_ERR(dev->clk_core))
return PTR_ERR(dev->clk_core);

dev->clk_mcu = devm_clk_get(&pdev->dev, "mcu_clk");
if (IS_ERR(dev->clk_mcu))
return PTR_ERR(dev->clk_mcu);

irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
Expand Down Expand Up @@ -3768,24 +3789,86 @@ static int allegro_remove(struct platform_device *pdev)
allegro_free_fw_codec(dev);
}

pm_runtime_put(&dev->plat_dev->dev);
pm_runtime_disable(&dev->plat_dev->dev);

v4l2_device_unregister(&dev->v4l2_dev);

return 0;
}

static int allegro_runtime_resume(struct device *device)
{
struct allegro_dev *dev = dev_get_drvdata(device);
struct regmap *settings = dev->settings;
unsigned int clk_mcu;
unsigned int clk_core;
int err;

if (!settings)
return -EINVAL;

#define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000)

err = regmap_read(settings, VCU_CORE_CLK, &clk_core);
if (err < 0)
return err;
err = clk_set_rate(dev->clk_core, MHZ_TO_HZ(clk_core));
if (err < 0)
return err;
err = clk_prepare_enable(dev->clk_core);
if (err)
return err;

err = regmap_read(settings, VCU_MCU_CLK, &clk_mcu);
if (err < 0)
goto disable_clk_core;
err = clk_set_rate(dev->clk_mcu, MHZ_TO_HZ(clk_mcu));
if (err < 0)
goto disable_clk_core;
err = clk_prepare_enable(dev->clk_mcu);
if (err)
goto disable_clk_core;

#undef MHZ_TO_HZ

return 0;

disable_clk_core:
clk_disable_unprepare(dev->clk_core);

return err;
}

static int allegro_runtime_suspend(struct device *device)
{
struct allegro_dev *dev = dev_get_drvdata(device);

clk_disable_unprepare(dev->clk_mcu);
clk_disable_unprepare(dev->clk_core);

return 0;
}

static const struct of_device_id allegro_dt_ids[] = {
{ .compatible = "allegro,al5e-1.1" },
{ /* sentinel */ }
};

MODULE_DEVICE_TABLE(of, allegro_dt_ids);

static const struct dev_pm_ops allegro_pm_ops = {
.runtime_resume = allegro_runtime_resume,
.runtime_suspend = allegro_runtime_suspend,
};

static struct platform_driver allegro_driver = {
.probe = allegro_probe,
.remove = allegro_remove,
.driver = {
.name = "allegro",
.of_match_table = of_match_ptr(allegro_dt_ids),
.pm = &allegro_pm_ops,
},
};

Expand Down

0 comments on commit 83cc5fd

Please sign in to comment.