diff --git a/Documentation/devicetree/bindings/media/exynos5-gsc.txt b/Documentation/devicetree/bindings/media/exynos5-gsc.txt index 5fe9372abb37..26ca25b6d264 100644 --- a/Documentation/devicetree/bindings/media/exynos5-gsc.txt +++ b/Documentation/devicetree/bindings/media/exynos5-gsc.txt @@ -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 diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index f85ed2c2951c..2dc638d0b371 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -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>; diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi index 4a2398f5bb97..5bdec23eb72f 100755 --- a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi +++ b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi @@ -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 }; }; }; diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3-trip-points.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu3-trip-points.dtsi new file mode 100644 index 000000000000..8037b2f69f05 --- /dev/null +++ b/arch/arm/boot/dts/exynos5422-odroidxu3-trip-points.dtsi @@ -0,0 +1,100 @@ +/* + * Device tree sources for default OdroidXU3/Exynos5422 thermal zone definition + * + * Copyright (c) 2015 Lukasz Majewski + * Anand Moon + * + * 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>; + }; + }; diff --git a/arch/arm/configs/odroidxu3_defconfig b/arch/arm/configs/odroidxu3_defconfig index ff7d99f01d44..11ec72a52225 100644 --- a/arch/arm/configs/odroidxu3_defconfig +++ b/arch/arm/configs/odroidxu3_defconfig @@ -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 diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 787bd16c19e5..cbb03768f5d7 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -24,12 +24,11 @@ #include #include #include +#include #include #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 = { diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 7ad7b9dc2243..696217e9af66 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -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; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 9f03b791b711..82505025d96c 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -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); + } }