Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 68 additions & 4 deletions drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>

#include <media/v4l2-device.h>
Expand Down Expand Up @@ -46,7 +47,7 @@ static const struct v4l2_ctrl_config hevc_d_ctrls[] = {
.id = V4L2_CID_STATELESS_HEVC_PPS,
.ops = &hevc_d_hevc_pps_ctrl_ops,
}, {
.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
}, {
.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
}, {
Expand All @@ -66,7 +67,17 @@ static const struct v4l2_ctrl_config hevc_d_ctrls[] = {
.min = V4L2_STATELESS_HEVC_START_CODE_NONE,
.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
.def = V4L2_STATELESS_HEVC_START_CODE_NONE,
},
}, {
.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
Comment thread
6by9 marked this conversation as resolved.
.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
}, {
.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1,
.def = V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1,
}
};

void *hevc_d_find_control_data(struct hevc_d_ctx *ctx, u32 id)
Expand Down Expand Up @@ -211,6 +222,7 @@ static int hevc_d_probe(struct platform_device *pdev)
dev->vfd = hevc_d_video_device;
dev->dev = &pdev->dev;
dev->pdev = pdev;
platform_set_drvdata(pdev, dev);

ret = hevc_d_hw_probe(dev);
if (ret) {
Expand Down Expand Up @@ -281,8 +293,6 @@ static int hevc_d_probe(struct platform_device *pdev)
goto err_m2m_mc;
}

platform_set_drvdata(pdev, dev);

return 0;

err_m2m_mc:
Expand Down Expand Up @@ -317,6 +327,59 @@ static void hevc_d_remove(struct platform_device *pdev)
hevc_d_hw_remove(dev);
}

static int hevc_d_runtime_suspend(struct device *d)
{
struct hevc_d_dev *dev = dev_get_drvdata(d);

hevc_d_hw_power_off(dev);
return 0;
}

static int hevc_d_runtime_resume(struct device *d)
{
struct hevc_d_dev *dev = dev_get_drvdata(d);

return hevc_d_hw_power_on(dev);
}

static int hevc_d_suspend(struct device *d)
{
struct hevc_d_dev *dev = dev_get_drvdata(d);
int ret;

/* Stop new jobs being started */
v4l2_m2m_suspend(dev->m2m_dev);
/*
* v4l2_m2m_suspend only waits for the current job to finish but we
* finish jobs before the h/w is done with them so also wait for any
* phase 1 / phase 2 work still in flight
*/
hevc_d_h265_sync(dev);

ret = pm_runtime_force_suspend(d);
if (ret)
v4l2_m2m_resume(dev->m2m_dev);
return ret;
}

static int hevc_d_resume(struct device *d)
{
struct hevc_d_dev *dev = dev_get_drvdata(d);
int ret;

ret = pm_runtime_force_resume(d);
if (ret)
return ret;

v4l2_m2m_resume(dev->m2m_dev);
return 0;
}

static const struct dev_pm_ops hevc_d_pm_ops = {
SYSTEM_SLEEP_PM_OPS(hevc_d_suspend, hevc_d_resume)
RUNTIME_PM_OPS(hevc_d_runtime_suspend, hevc_d_runtime_resume, NULL)
};

static const struct of_device_id hevc_d_dt_match[] = {
{ .compatible = "brcm,bcm2711-hevc-dec", },
{ .compatible = "brcm,bcm2712-hevc-dec", },
Expand All @@ -330,6 +393,7 @@ static struct platform_driver hevc_d_driver = {
.driver = {
.name = HEVC_D_NAME,
.of_match_table = hevc_d_dt_match,
.pm = pm_ptr(&hevc_d_pm_ops),
},
};
module_platform_driver(hevc_d_driver);
Expand Down
4 changes: 2 additions & 2 deletions drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c
Original file line number Diff line number Diff line change
Expand Up @@ -2286,7 +2286,7 @@ static void phase1_sync_claimed(struct hevc_d_dev *const dev, void *v)
* phase1 has counted enables so must reenable once claimed
* phase2 has unlimited enables
*/
static void irq_sync(struct hevc_d_dev *const dev)
void hevc_d_h265_sync(struct hevc_d_dev *dev)
{
struct irq_sync sync;

Expand Down Expand Up @@ -2322,7 +2322,7 @@ void hevc_d_h265_stop(struct hevc_d_ctx *ctx)
{
struct hevc_d_dev *const dev = ctx->dev;

irq_sync(dev);
hevc_d_h265_sync(dev);
h265_ctx_uninit(dev, ctx);
}

Expand Down
1 change: 1 addition & 0 deletions drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extern const struct v4l2_ctrl_ops hevc_d_hevc_slice_params_ctrl_ops;
int hevc_d_h265_start(struct hevc_d_ctx *ctx);
void hevc_d_h265_stop(struct hevc_d_ctx *ctx);
void hevc_d_h265_trigger(struct hevc_d_ctx *ctx);
void hevc_d_h265_sync(struct hevc_d_dev *dev);

void hevc_d_device_run(void *priv);

Expand Down
120 changes: 88 additions & 32 deletions drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>

#include <media/videobuf2-core.h>
#include <media/v4l2-mem2mem.h>
Expand Down Expand Up @@ -300,18 +301,22 @@ void hevc_d_hw_irq_active2_irq(struct hevc_d_dev *dev,
}

/*
* Stop the clock for this context
* clk_disable_unprepare does ref counting so this will not actually
* disable the clock if there are other running contexts
* Power down the h/w
* Called from runtime suspend (or directly if runtime PM is unavailable)
*/
void hevc_d_hw_stop_clock(struct hevc_d_dev *dev)
void hevc_d_hw_power_off(struct hevc_d_dev *dev)
{
clk_set_min_rate(dev->clock, 0);
clk_disable_unprepare(dev->clock);
}

/* Always starts the clock if it isn't already on this ctx */
int hevc_d_hw_start_clock(struct hevc_d_dev *dev)
/*
* Power up the h/w and (re)initialise any state that is not set per-job
* Called from runtime resume (or directly if runtime PM is unavailable)
*/
int hevc_d_hw_power_on(struct hevc_d_dev *dev)
{
u32 irq_stat;
int rv;

rv = clk_set_min_rate(dev->clock, dev->max_clock_rate);
Expand All @@ -325,22 +330,47 @@ int hevc_d_hw_start_clock(struct hevc_d_dev *dev)
dev_err(dev->dev, "Failed to enable clock\n");
return rv;
}

/*
* Enable IRQs & reset anything pending
* Whilst this seems the wrong way round the h/w doesn't actually
* set the IRQ status bits till the IRQs are enabled. On probe we
* haven't got the IRQ yet and on resume nothing is in flight so
* this should still be safe.
*/
irq_write(dev, ARG_IC_ICTRL,
ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
irq_stat = irq_read(dev, ARG_IC_ICTRL);
irq_write(dev, ARG_IC_ICTRL, irq_stat);

return 0;
}

static int hw_setup(struct hevc_d_dev *dev)
/*
* Take a power reference for this context
* Runtime PM does ref counting so the h/w stays powered whilst any
* context holds a reference
*/
int hevc_d_hw_pm_get(struct hevc_d_dev *dev)
{
int rv;

rv = pm_runtime_resume_and_get(dev->dev);
if (rv)
dev_err(dev->dev, "Failed to power up: %d\n", rv);
return rv;
}

void hevc_d_hw_pm_put(struct hevc_d_dev *dev)
{
pm_runtime_put(dev->dev);
}

static int get_max_clock_rate(struct hevc_d_dev *dev)
{
struct device_node *node;
u32 ver;
u32 irq_stat;
struct rpi_firmware *firmware;

ver = apb_read(dev, RPI_VERSION);
if (ver != 0x202) {
dev_err(dev->dev, "Unexpected version %#x only 0x202 supported\n", ver);
return -ENODEV;
}

node = rpi_firmware_find_node();
if (!node)
return -EINVAL;
Expand All @@ -354,17 +384,19 @@ static int hw_setup(struct hevc_d_dev *dev)
RPI_FIRMWARE_HEVC_CLK_ID);
rpi_firmware_put(firmware);

/*
* Enable IRQs & reset anything pending
* Whilst this seems the wrong way round the h/w doesn't actually
* set the IRQ status bits till the IRQs are enabled. As we haven't
* got the IRQ yet this should still be safe.
*/
irq_write(dev, ARG_IC_ICTRL,
ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
irq_stat = irq_read(dev, ARG_IC_ICTRL);
irq_write(dev, ARG_IC_ICTRL, irq_stat);
return 0;
}

/* H/w must be powered */
static int check_version(struct hevc_d_dev *dev)
{
u32 ver;

ver = apb_read(dev, RPI_VERSION);
if (ver != 0x202) {
dev_err(dev->dev, "Unexpected version %#x only 0x202 supported\n", ver);
return -ENODEV;
}
return 0;
}

Expand All @@ -388,34 +420,58 @@ int hevc_d_hw_probe(struct hevc_d_dev *dev)
if (IS_ERR(dev->clock))
return PTR_ERR(dev->clock);

ret = clk_prepare_enable(dev->clock);
ret = get_max_clock_rate(dev);
if (ret)
return ret;
ret = hw_setup(dev);
clk_disable_unprepare(dev->clock);

/*
* Power up manually so this works without runtime PM. Runtime PM is
* told we are active once enabled and the driver core idles us after
* probe completes.
*/
ret = hevc_d_hw_power_on(dev);
if (ret)
return ret;

ret = check_version(dev);
if (ret)
goto fail_power_off;

irq_dec = platform_get_irq(dev->pdev, 0);
if (irq_dec < 0)
return irq_dec;
if (irq_dec < 0) {
ret = irq_dec;
goto fail_power_off;
}
ret = devm_request_threaded_irq(dev->dev, irq_dec,
hevc_d_irq_irq,
hevc_d_irq_thread,
0, dev_name(dev->dev), dev);
if (ret)
if (ret) {
dev_err(dev->dev, "Failed to request IRQ - %d\n", ret);
goto fail_power_off;
}

pm_runtime_set_active(dev->dev);
pm_runtime_enable(dev->dev);

return 0;

fail_power_off:
hevc_d_hw_power_off(dev);
return ret;
}

void hevc_d_hw_remove(struct hevc_d_dev *dev)
{
pm_runtime_disable(dev->dev);
if (!pm_runtime_status_suspended(dev->dev))
hevc_d_hw_power_off(dev);
pm_runtime_set_suspended(dev->dev);

/*
* IRQ auto freed on unload so no need to do it here
* ioremap auto freed on unload
*/
ictl_uninit(&dev->ic_active1);
ictl_uninit(&dev->ic_active2);
}

6 changes: 4 additions & 2 deletions drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,10 @@ void hevc_d_hw_irq_active2_irq(struct hevc_d_dev *dev,
struct hevc_d_hw_irq_ent *ient,
hevc_d_irq_callback irq_cb, void *ctx);

int hevc_d_hw_start_clock(struct hevc_d_dev *dev);
void hevc_d_hw_stop_clock(struct hevc_d_dev *dev);
int hevc_d_hw_power_on(struct hevc_d_dev *dev);
void hevc_d_hw_power_off(struct hevc_d_dev *dev);
int hevc_d_hw_pm_get(struct hevc_d_dev *dev);
void hevc_d_hw_pm_put(struct hevc_d_dev *dev);

int hevc_d_hw_probe(struct hevc_d_dev *dev);
void hevc_d_hw_remove(struct hevc_d_dev *dev);
Expand Down
Loading
Loading