mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-11 13:27:06 +09:00
Merge branch 'odroidxu4-4.9.y' of github.com:hardkernel/linux into odroidxu4-4.9.y
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
G-Scaler is used for scaling and color space conversion on EXYNOS5 SoCs.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "samsung,exynos5-gsc"
|
||||
- compatible: should be "samsung,exynos5-gsc" (for Exynos 5250, 5420 and
|
||||
5422 SoCs) or "samsung,exynos5433-gsc" (Exynos 5433)
|
||||
- reg: should contain G-Scaler physical address location and length.
|
||||
- interrupts: should contain G-Scaler interrupt number
|
||||
|
||||
|
||||
@@ -191,6 +191,15 @@
|
||||
clock-names = "fout_epll", "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
|
||||
};
|
||||
|
||||
g2d@10850000 {
|
||||
compatible = "samsung,exynos5250-g2d";
|
||||
reg = <0x10850000 0x1000>;
|
||||
interrupts = <0 91 0>;
|
||||
clocks = <&clock CLK_G2D>;
|
||||
clock-names = "fimg2d";
|
||||
iommus = <&sysmmu_g2dr>, <&sysmmu_g2dw>;
|
||||
};
|
||||
|
||||
mfc: codec@11000000 {
|
||||
compatible = "samsung,mfc-v7";
|
||||
reg = <0x11000000 0x10000>;
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
cooling-min-state = <0>;
|
||||
cooling-max-state = <3>;
|
||||
#cooling-cells = <2>;
|
||||
cooling-levels = <0 150 190 252>;
|
||||
cooling-levels = <0 110 170 230>;
|
||||
};
|
||||
|
||||
mali: mali@0x11800000 {
|
||||
@@ -124,90 +124,28 @@
|
||||
|
||||
thermal-zones {
|
||||
cpu0_thermal: cpu0-thermal {
|
||||
thermal-sensors = <&tmu_cpu0 0 &tmu_cpu1 0 &tmu_cpu2 0 &tmu_cpu3 0>;
|
||||
polling-delay-passive = <250>;
|
||||
polling-delay = <1000>;
|
||||
trips {
|
||||
cpu_alert0: cpu-alert-0 {
|
||||
temperature = <70000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_alert1: cpu-alert-1 {
|
||||
temperature = <75000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_alert2: cpu-alert-2 {
|
||||
temperature = <80000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_alert3: cpu-alert-3 {
|
||||
temperature = <90000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_alert4: cpu-alert-4 {
|
||||
temperature = <95000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_alert5: cpu-alert-5 {
|
||||
temperature = <103000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_alert6: cpu-alert-6 {
|
||||
temperature = <110000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_criti0: cpu-crit-0 {
|
||||
temperature = <115000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&cpu_alert0>;
|
||||
cooling-device = <&fan0 0 1>;
|
||||
};
|
||||
map1 {
|
||||
trip = <&cpu_alert1>;
|
||||
cooling-device = <&fan0 1 2>;
|
||||
};
|
||||
map2 {
|
||||
trip = <&cpu_alert2>;
|
||||
cooling-device = <&fan0 2 3>;
|
||||
};
|
||||
/*
|
||||
* First trip for A7: 1000Mhz
|
||||
* First trip for A15: 1400Mhz
|
||||
*/
|
||||
map3 {
|
||||
trip = <&cpu_alert3>;
|
||||
cooling-device = <&cpu0 0 6>;
|
||||
};
|
||||
map4 {
|
||||
trip = <&cpu_alert3>;
|
||||
cooling-device = <&cpu4 0 3>;
|
||||
};
|
||||
|
||||
/*
|
||||
* Second trip for A15: 600Mhz
|
||||
* Second trip for A7: 600Mhz
|
||||
*/
|
||||
map5 {
|
||||
trip = <&cpu_alert4>;
|
||||
cooling-device = <&cpu0 3 7>;
|
||||
};
|
||||
map6 {
|
||||
trip = <&cpu_alert4>;
|
||||
cooling-device = <&cpu4 3 14>;
|
||||
};
|
||||
};
|
||||
thermal-sensors = <&tmu_cpu0 0>;
|
||||
#define CPU_THERMAL_ZONE_NUM 0
|
||||
#include "exynos5422-odroidxu3-trip-points.dtsi"
|
||||
#undef CPU_THERMAL_ZONE_NUM
|
||||
};
|
||||
cpu1_thermal: cpu1-thermal {
|
||||
thermal-sensors = <&tmu_cpu1 0>;
|
||||
#define CPU_THERMAL_ZONE_NUM 1
|
||||
#include "exynos5422-odroidxu3-trip-points.dtsi"
|
||||
#undef CPU_THERMAL_ZONE_NUM
|
||||
};
|
||||
cpu2_thermal: cpu2-thermal {
|
||||
thermal-sensors = <&tmu_cpu2 0>;
|
||||
#define CPU_THERMAL_ZONE_NUM 2
|
||||
#include "exynos5422-odroidxu3-trip-points.dtsi"
|
||||
#undef CPU_THERMAL_ZONE_NUM
|
||||
};
|
||||
cpu3_thermal: cpu3-thermal {
|
||||
thermal-sensors = <&tmu_cpu3 0>;
|
||||
#define CPU_THERMAL_ZONE_NUM 3
|
||||
#include "exynos5422-odroidxu3-trip-points.dtsi"
|
||||
#undef CPU_THERMAL_ZONE_NUM
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
100
arch/arm/boot/dts/exynos5422-odroidxu3-trip-points.dtsi
Normal file
100
arch/arm/boot/dts/exynos5422-odroidxu3-trip-points.dtsi
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Device tree sources for default OdroidXU3/Exynos5422 thermal zone definition
|
||||
*
|
||||
* Copyright (c) 2015 Lukasz Majewski <l.majewski@samsung.com>
|
||||
* Anand Moon <linux.amoon@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _TOKENPASTE(x, y) x ## y
|
||||
#define TOKENPASTE(x, y) _TOKENPASTE(x, y)
|
||||
#define UNIQIFY(label) TOKENPASTE(label, CPU_THERMAL_ZONE_NUM)
|
||||
|
||||
polling-delay-passive = <250>;
|
||||
polling-delay = <1000>;
|
||||
trips {
|
||||
UNIQIFY(cpu_alert0): cpu-alert-0 {
|
||||
temperature = <70000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "active";
|
||||
};
|
||||
UNIQIFY(cpu_alert1): cpu-alert-1 {
|
||||
temperature = <75000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "active";
|
||||
};
|
||||
UNIQIFY(cpu_alert2): cpu-alert-2 {
|
||||
temperature = <80000>; /* millicelsius */
|
||||
hysteresis = <10000>; /* millicelsius */
|
||||
type = "active";
|
||||
};
|
||||
UNIQIFY(cpu_alert3): cpu-alert-3 {
|
||||
temperature = <85000>; /* millicelsius */
|
||||
hysteresis = <3000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
UNIQIFY(cpu_alert4): cpu-alert-4 {
|
||||
temperature = <90000>; /* millicelsius */
|
||||
hysteresis = <3000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
UNIQIFY(cpu_alert5): cpu-alert-5 {
|
||||
temperature = <95000>; /* millicelsius */
|
||||
hysteresis = <3000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
UNIQIFY(cpu_criti0): cpu-crit-0 {
|
||||
temperature = <115000>; /* millicelsius */
|
||||
hysteresis = <3000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&UNIQIFY(cpu_alert0)>;
|
||||
cooling-device = <&fan0 0 1>;
|
||||
};
|
||||
map1 {
|
||||
trip = <&UNIQIFY(cpu_alert1)>;
|
||||
cooling-device = <&fan0 1 2>;
|
||||
};
|
||||
map2 {
|
||||
trip = <&UNIQIFY(cpu_alert2)>;
|
||||
cooling-device = <&fan0 2 3>;
|
||||
};
|
||||
/*
|
||||
* When reaching cpu_alert3, reduce A15 cores by 1 step.
|
||||
* The 2GHz step causes high thermals on multithreaded workloads
|
||||
* so better performance is gained by managing it out early.
|
||||
*/
|
||||
map3 {
|
||||
trip = <&UNIQIFY(cpu_alert3)>;
|
||||
cooling-device = <&cpu4 0 1>;
|
||||
};
|
||||
/*
|
||||
* When reaching cpu_alert4, reduce A15 cores by 3 steps
|
||||
* to further manage the performance level while keeping
|
||||
* thermals under control.
|
||||
*/
|
||||
map4 {
|
||||
trip = <&UNIQIFY(cpu_alert4)>;
|
||||
cooling-device = <&cpu4 2 4>;
|
||||
};
|
||||
/*
|
||||
* When reaching cpu_alert5, reduce all CPUs to ensure thermal
|
||||
* safety. A7 cores don't produce much thermal load so they are
|
||||
* reduced less to optimise performance.
|
||||
*/
|
||||
map5 {
|
||||
trip = <&UNIQIFY(cpu_alert5)>;
|
||||
cooling-device = <&cpu0 0 2>;
|
||||
};
|
||||
map6 {
|
||||
trip = <&UNIQIFY(cpu_alert5)>;
|
||||
cooling-device = <&cpu4 5 14>;
|
||||
};
|
||||
};
|
||||
@@ -3229,7 +3229,7 @@ CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE=y
|
||||
# CONFIG_VIDEO_XILINX is not set
|
||||
CONFIG_V4L_MEM2MEM_DRIVERS=y
|
||||
# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set
|
||||
CONFIG_VIDEO_SAMSUNG_S5P_G2D=y
|
||||
# CONFIG_VIDEO_SAMSUNG_S5P_G2D is not set
|
||||
CONFIG_VIDEO_SAMSUNG_S5P_JPEG=y
|
||||
CONFIG_VIDEO_SAMSUNG_S5P_MFC=y
|
||||
CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=y
|
||||
@@ -3545,6 +3545,7 @@ CONFIG_DRM_EXYNOS_HDMI=y
|
||||
#
|
||||
# Sub-drivers
|
||||
#
|
||||
CONFIG_DRM_EXYNOS_G2D=y
|
||||
CONFIG_DRM_EXYNOS_IPP=y
|
||||
CONFIG_DRM_EXYNOS_FIMC=y
|
||||
CONFIG_DRM_EXYNOS_ROTATOR=y
|
||||
|
||||
@@ -24,12 +24,11 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
|
||||
#include "gsc-core.h"
|
||||
|
||||
#define GSC_CLOCK_GATE_NAME "gscl"
|
||||
|
||||
static const struct gsc_fmt gsc_formats[] = {
|
||||
{
|
||||
.name = "RGB565",
|
||||
@@ -39,8 +38,8 @@ static const struct gsc_fmt gsc_formats[] = {
|
||||
.num_planes = 1,
|
||||
.num_comp = 1,
|
||||
}, {
|
||||
.name = "XRGB-8-8-8-8, 32 bpp",
|
||||
.pixelformat = V4L2_PIX_FMT_RGB32,
|
||||
.name = "BGRX-8-8-8-8, 32 bpp",
|
||||
.pixelformat = V4L2_PIX_FMT_BGR32,
|
||||
.depth = { 32 },
|
||||
.color = GSC_RGB,
|
||||
.num_planes = 1,
|
||||
@@ -409,7 +408,7 @@ int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
|
||||
if (pix_mp->field == V4L2_FIELD_ANY)
|
||||
pix_mp->field = V4L2_FIELD_NONE;
|
||||
else if (pix_mp->field != V4L2_FIELD_NONE) {
|
||||
pr_err("Not supported field order(%d)\n", pix_mp->field);
|
||||
pr_debug("Not supported field order(%d)\n", pix_mp->field);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -441,7 +440,7 @@ int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
|
||||
v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
|
||||
&pix_mp->height, min_h, max_h, mod_y, 0);
|
||||
if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
|
||||
pr_info("Image size has been modified from %dx%d to %dx%d",
|
||||
pr_debug("Image size has been modified from %dx%d to %dx%d\n",
|
||||
tmp_w, tmp_h, pix_mp->width, pix_mp->height);
|
||||
|
||||
pix_mp->num_planes = fmt->num_planes;
|
||||
@@ -451,12 +450,25 @@ int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
|
||||
else /* SD */
|
||||
pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
||||
|
||||
|
||||
for (i = 0; i < pix_mp->num_planes; ++i) {
|
||||
int bpl = (pix_mp->width * fmt->depth[i]) >> 3;
|
||||
pix_mp->plane_fmt[i].bytesperline = bpl;
|
||||
pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
|
||||
struct v4l2_plane_pix_format *plane_fmt = &pix_mp->plane_fmt[i];
|
||||
u32 bpl = plane_fmt->bytesperline;
|
||||
|
||||
if (fmt->num_comp == 1 && /* Packed */
|
||||
(bpl == 0 || (bpl * 8 / fmt->depth[i]) < pix_mp->width))
|
||||
bpl = pix_mp->width * fmt->depth[i] / 8;
|
||||
|
||||
if (fmt->num_comp > 1 && /* Planar */
|
||||
(bpl == 0 || bpl < pix_mp->width))
|
||||
bpl = pix_mp->width;
|
||||
|
||||
if (i != 0 && fmt->num_comp == 3)
|
||||
bpl /= 2;
|
||||
|
||||
plane_fmt->bytesperline = bpl;
|
||||
plane_fmt->sizeimage = max(pix_mp->width * pix_mp->height *
|
||||
fmt->depth[i] / 8,
|
||||
plane_fmt->sizeimage);
|
||||
pr_debug("[%d]: bpl: %d, sizeimage: %d",
|
||||
i, bpl, pix_mp->plane_fmt[i].sizeimage);
|
||||
}
|
||||
@@ -964,7 +976,19 @@ static struct gsc_driverdata gsc_v_100_drvdata = {
|
||||
[3] = &gsc_v_100_variant,
|
||||
},
|
||||
.num_entities = 4,
|
||||
.lclk_frequency = 266000000UL,
|
||||
.clk_names = { "gscl" },
|
||||
.num_clocks = 1,
|
||||
};
|
||||
|
||||
static struct gsc_driverdata gsc_5433_drvdata = {
|
||||
.variant = {
|
||||
[0] = &gsc_v_100_variant,
|
||||
[1] = &gsc_v_100_variant,
|
||||
[2] = &gsc_v_100_variant,
|
||||
},
|
||||
.num_entities = 3,
|
||||
.clk_names = { "pclk", "aclk", "aclk_xiu", "aclk_gsclbend" },
|
||||
.num_clocks = 4,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_gsc_match[] = {
|
||||
@@ -972,52 +996,135 @@ static const struct of_device_id exynos_gsc_match[] = {
|
||||
.compatible = "samsung,exynos5-gsc",
|
||||
.data = &gsc_v_100_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "samsung,exynos5433-gsc",
|
||||
.data = &gsc_5433_drvdata,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_gsc_match);
|
||||
|
||||
static void *gsc_get_drv_data(struct platform_device *pdev)
|
||||
{
|
||||
struct gsc_driverdata *driver_data = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(exynos_gsc_match, pdev->dev.of_node);
|
||||
if (match)
|
||||
driver_data = (struct gsc_driverdata *)match->data;
|
||||
|
||||
return driver_data;
|
||||
}
|
||||
|
||||
static void gsc_clk_put(struct gsc_dev *gsc)
|
||||
{
|
||||
if (!IS_ERR(gsc->clock))
|
||||
clk_unprepare(gsc->clock);
|
||||
}
|
||||
|
||||
static int gsc_clk_get(struct gsc_dev *gsc)
|
||||
static int gsc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gsc_dev *gsc;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct gsc_driverdata *drv_data = of_device_get_match_data(dev);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n");
|
||||
gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL);
|
||||
if (!gsc)
|
||||
return -ENOMEM;
|
||||
|
||||
gsc->clock = devm_clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
|
||||
if (IS_ERR(gsc->clock)) {
|
||||
dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n",
|
||||
GSC_CLOCK_GATE_NAME);
|
||||
return PTR_ERR(gsc->clock);
|
||||
}
|
||||
|
||||
ret = clk_prepare(gsc->clock);
|
||||
if (ret < 0) {
|
||||
dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n",
|
||||
GSC_CLOCK_GATE_NAME);
|
||||
gsc->clock = ERR_PTR(-EINVAL);
|
||||
ret = of_alias_get_id(pdev->dev.of_node, "gsc");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gsc->id = ret;
|
||||
if (gsc->id >= drv_data->num_entities) {
|
||||
dev_err(dev, "Invalid platform device id: %d\n", gsc->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gsc->num_clocks = drv_data->num_clocks;
|
||||
gsc->variant = drv_data->variant[gsc->id];
|
||||
gsc->pdev = pdev;
|
||||
|
||||
init_waitqueue_head(&gsc->irq_queue);
|
||||
spin_lock_init(&gsc->slock);
|
||||
mutex_init(&gsc->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
gsc->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(gsc->regs))
|
||||
return PTR_ERR(gsc->regs);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "failed to get IRQ resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < gsc->num_clocks; i++) {
|
||||
gsc->clock[i] = devm_clk_get(dev, drv_data->clk_names[i]);
|
||||
if (IS_ERR(gsc->clock[i])) {
|
||||
dev_err(dev, "failed to get clock: %s\n",
|
||||
drv_data->clk_names[i]);
|
||||
return PTR_ERR(gsc->clock[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < gsc->num_clocks; i++) {
|
||||
ret = clk_prepare_enable(gsc->clock[i]);
|
||||
if (ret) {
|
||||
dev_err(dev, "clock prepare failed for clock: %s\n",
|
||||
drv_data->clk_names[i]);
|
||||
while (--i >= 0)
|
||||
clk_disable_unprepare(gsc->clock[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, res->start, gsc_irq_handler,
|
||||
0, pdev->name, gsc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to install irq (%d)\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = v4l2_device_register(dev, &gsc->v4l2_dev);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
ret = gsc_register_m2m_device(gsc);
|
||||
if (ret)
|
||||
goto err_v4l2;
|
||||
|
||||
platform_set_drvdata(pdev, gsc);
|
||||
|
||||
gsc_hw_set_sw_reset(gsc);
|
||||
gsc_wait_reset(gsc);
|
||||
|
||||
vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
|
||||
|
||||
dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id);
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_v4l2:
|
||||
v4l2_device_unregister(&gsc->v4l2_dev);
|
||||
err_clk:
|
||||
for (i = gsc->num_clocks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(gsc->clock[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gsc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gsc_dev *gsc = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
gsc_unregister_m2m_device(gsc);
|
||||
v4l2_device_unregister(&gsc->v4l2_dev);
|
||||
|
||||
vb2_dma_contig_clear_max_seg_size(&pdev->dev);
|
||||
for (i = 0; i < gsc->num_clocks; i++)
|
||||
clk_disable_unprepare(gsc->clock[i]);
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int gsc_m2m_suspend(struct gsc_dev *gsc)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -1040,7 +1147,7 @@ static int gsc_m2m_suspend(struct gsc_dev *gsc)
|
||||
return timeout == 0 ? -EAGAIN : 0;
|
||||
}
|
||||
|
||||
static int gsc_m2m_resume(struct gsc_dev *gsc)
|
||||
static void gsc_m2m_resume(struct gsc_dev *gsc)
|
||||
{
|
||||
struct gsc_ctx *ctx;
|
||||
unsigned long flags;
|
||||
@@ -1053,179 +1160,54 @@ static int gsc_m2m_resume(struct gsc_dev *gsc)
|
||||
|
||||
if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state))
|
||||
gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gsc_dev *gsc;
|
||||
struct resource *res;
|
||||
struct gsc_driverdata *drv_data = gsc_get_drv_data(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL);
|
||||
if (!gsc)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_alias_get_id(pdev->dev.of_node, "gsc");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gsc->id = ret;
|
||||
if (gsc->id >= drv_data->num_entities) {
|
||||
dev_err(dev, "Invalid platform device id: %d\n", gsc->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gsc->variant = drv_data->variant[gsc->id];
|
||||
gsc->pdev = pdev;
|
||||
|
||||
init_waitqueue_head(&gsc->irq_queue);
|
||||
spin_lock_init(&gsc->slock);
|
||||
mutex_init(&gsc->lock);
|
||||
gsc->clock = ERR_PTR(-EINVAL);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
gsc->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(gsc->regs))
|
||||
return PTR_ERR(gsc->regs);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "failed to get IRQ resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = gsc_clk_get(gsc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(dev, res->start, gsc_irq_handler,
|
||||
0, pdev->name, gsc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to install irq (%d)\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = v4l2_device_register(dev, &gsc->v4l2_dev);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
ret = gsc_register_m2m_device(gsc);
|
||||
if (ret)
|
||||
goto err_v4l2;
|
||||
|
||||
platform_set_drvdata(pdev, gsc);
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0)
|
||||
goto err_m2m;
|
||||
|
||||
vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
|
||||
|
||||
dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
return 0;
|
||||
|
||||
err_m2m:
|
||||
gsc_unregister_m2m_device(gsc);
|
||||
err_v4l2:
|
||||
v4l2_device_unregister(&gsc->v4l2_dev);
|
||||
err_clk:
|
||||
gsc_clk_put(gsc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gsc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gsc_dev *gsc = platform_get_drvdata(pdev);
|
||||
|
||||
gsc_unregister_m2m_device(gsc);
|
||||
v4l2_device_unregister(&gsc->v4l2_dev);
|
||||
|
||||
vb2_dma_contig_clear_max_seg_size(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
gsc_clk_put(gsc);
|
||||
|
||||
dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct gsc_dev *gsc = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
|
||||
pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state);
|
||||
|
||||
ret = clk_enable(gsc->clock);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (i = 0; i < gsc->num_clocks; i++) {
|
||||
ret = clk_prepare_enable(gsc->clock[i]);
|
||||
if (ret) {
|
||||
while (--i >= 0)
|
||||
clk_disable_unprepare(gsc->clock[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
gsc_hw_set_sw_reset(gsc);
|
||||
gsc_wait_reset(gsc);
|
||||
gsc_m2m_resume(gsc);
|
||||
|
||||
return gsc_m2m_resume(gsc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct gsc_dev *gsc = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
ret = gsc_m2m_suspend(gsc);
|
||||
if (!ret)
|
||||
clk_disable(gsc->clock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
|
||||
for (i = gsc->num_clocks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(gsc->clock[i]);
|
||||
|
||||
pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gsc_resume(struct device *dev)
|
||||
{
|
||||
struct gsc_dev *gsc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
|
||||
|
||||
/* Do not resume if the device was idle before system suspend */
|
||||
spin_lock_irqsave(&gsc->slock, flags);
|
||||
if (!test_and_clear_bit(ST_SUSPEND, &gsc->state) ||
|
||||
!gsc_m2m_opened(gsc)) {
|
||||
spin_unlock_irqrestore(&gsc->slock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&gsc->slock, flags);
|
||||
|
||||
if (!pm_runtime_suspended(dev))
|
||||
return gsc_runtime_resume(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsc_suspend(struct device *dev)
|
||||
{
|
||||
struct gsc_dev *gsc = dev_get_drvdata(dev);
|
||||
|
||||
pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
|
||||
|
||||
if (test_and_set_bit(ST_SUSPEND, &gsc->state))
|
||||
return 0;
|
||||
|
||||
if (!pm_runtime_suspended(dev))
|
||||
return gsc_runtime_suspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops gsc_pm_ops = {
|
||||
.suspend = gsc_suspend,
|
||||
.resume = gsc_resume,
|
||||
.runtime_suspend = gsc_runtime_suspend,
|
||||
.runtime_resume = gsc_runtime_resume,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver gsc_driver = {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
|
||||
#define GSC_MAX_DEVS 4
|
||||
#define GSC_MAX_CLOCKS 4
|
||||
#define GSC_M2M_BUF_NUM 0
|
||||
#define GSC_MAX_CTRL_NUM 10
|
||||
#define GSC_SC_ALIGN_4 4
|
||||
@@ -48,9 +49,6 @@
|
||||
#define GSC_CTX_ABORT (1 << 7)
|
||||
|
||||
enum gsc_dev_flags {
|
||||
/* for global */
|
||||
ST_SUSPEND,
|
||||
|
||||
/* for m2m node */
|
||||
ST_M2M_OPEN,
|
||||
ST_M2M_RUN,
|
||||
@@ -306,12 +304,12 @@ struct gsc_variant {
|
||||
* struct gsc_driverdata - per device type driver data for init time.
|
||||
*
|
||||
* @variant: the variant information for this driver.
|
||||
* @lclk_frequency: G-Scaler clock frequency
|
||||
* @num_entities: the number of g-scalers
|
||||
*/
|
||||
struct gsc_driverdata {
|
||||
struct gsc_variant *variant[GSC_MAX_DEVS];
|
||||
unsigned long lclk_frequency;
|
||||
const char *clk_names[GSC_MAX_CLOCKS];
|
||||
int num_clocks;
|
||||
int num_entities;
|
||||
};
|
||||
|
||||
@@ -335,7 +333,8 @@ struct gsc_dev {
|
||||
struct platform_device *pdev;
|
||||
struct gsc_variant *variant;
|
||||
u16 id;
|
||||
struct clk *clock;
|
||||
int num_clocks;
|
||||
struct clk *clock[GSC_MAX_CLOCKS];
|
||||
void __iomem *regs;
|
||||
wait_queue_head_t irq_queue;
|
||||
struct gsc_m2m_device m2m;
|
||||
|
||||
@@ -66,12 +66,29 @@ static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
|
||||
return ret > 0 ? 0 : ret;
|
||||
}
|
||||
|
||||
static void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx)
|
||||
{
|
||||
struct vb2_v4l2_buffer *src_vb, *dst_vb;
|
||||
|
||||
while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
|
||||
src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
|
||||
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) {
|
||||
dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
|
||||
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void gsc_m2m_stop_streaming(struct vb2_queue *q)
|
||||
{
|
||||
struct gsc_ctx *ctx = q->drv_priv;
|
||||
|
||||
__gsc_m2m_job_abort(ctx);
|
||||
|
||||
__gsc_m2m_cleanup_queue(ctx);
|
||||
|
||||
pm_runtime_put(&ctx->gsc_dev->pdev->dev);
|
||||
}
|
||||
|
||||
@@ -365,14 +382,8 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh,
|
||||
|
||||
max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
|
||||
gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
|
||||
if (reqbufs->count > max_cnt) {
|
||||
if (reqbufs->count > max_cnt)
|
||||
return -EINVAL;
|
||||
} else if (reqbufs->count == 0) {
|
||||
if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
||||
gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
|
||||
else
|
||||
gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
|
||||
}
|
||||
|
||||
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
|
||||
}
|
||||
@@ -664,8 +675,8 @@ static int gsc_m2m_open(struct file *file)
|
||||
|
||||
error_ctrls:
|
||||
gsc_ctrls_delete(ctx);
|
||||
error_fh:
|
||||
v4l2_fh_del(&ctx->fh);
|
||||
error_fh:
|
||||
v4l2_fh_exit(&ctx->fh);
|
||||
kfree(ctx);
|
||||
unlock:
|
||||
@@ -766,30 +777,29 @@ int gsc_register_m2m_device(struct gsc_dev *gsc)
|
||||
gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
|
||||
if (IS_ERR(gsc->m2m.m2m_dev)) {
|
||||
dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
|
||||
ret = PTR_ERR(gsc->m2m.m2m_dev);
|
||||
goto err_m2m_r1;
|
||||
return PTR_ERR(gsc->m2m.m2m_dev);
|
||||
}
|
||||
|
||||
ret = video_register_device(&gsc->vdev, VFL_TYPE_GRABBER, -1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s(): failed to register video device\n", __func__);
|
||||
goto err_m2m_r2;
|
||||
goto err_m2m_release;
|
||||
}
|
||||
|
||||
pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
|
||||
return 0;
|
||||
|
||||
err_m2m_r2:
|
||||
err_m2m_release:
|
||||
v4l2_m2m_release(gsc->m2m.m2m_dev);
|
||||
err_m2m_r1:
|
||||
video_device_release(gsc->m2m.vfd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gsc_unregister_m2m_device(struct gsc_dev *gsc)
|
||||
{
|
||||
if (gsc)
|
||||
if (gsc) {
|
||||
v4l2_m2m_release(gsc->m2m.m2m_dev);
|
||||
video_unregister_device(&gsc->vdev);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user