Message ID | 20171017110752.25096-1-m.szyprowski@samsung.com |
---|---|
Headers | show |
Series | Exynos DRM: rewrite IPP subsystem and userspace API | expand |
On Tue, Oct 17, 2017 at 01:07:43PM +0200, Marek Szyprowski wrote: > Dear all, > > This patchset performs complete rewrite of Exynos DRM IPP subsystem and > its userspace API. > > Why such rewrite is needed? Exynos DRM IPP API is over-engineered in > general, but not really extensible on the other side. It is also buggy, > with significant design flaws: > - Userspace API covers memory-2-memory picture operations together with > CRTC writeback and duplicating features, which belongs to video plane. > - Lack of support of the all required image formats (for example NV12 > Samsung-tiled cannot be used due to lack of pixel format modifier > support). > - Userspace API designed only to mimic hardware behaviour, not easy to > understand. > - Lack of proper input validation in the core, drivers also didn't do that > correctly, so it was possible to set incorrect parameters and easil > trigger IOMMU fault or memory trash. > - Drivers were partially disfunctional or supported only a subset of modes. > > Due to the above limitations and issues the Exynos DRM IPP API was not > used by any of the open-source projects. I assume that it is safe to remove > this broken API without any damage to open-source community. All remaining > users (mainly Tizen project related) will be updated to the new version. > > This patchset changes Exynos DRM IPP subsystem to something useful. The > userspace API is much simpler, state-less and easy to understand. Also > the code of the core and driver is significantly smaller and easier to > understand. > > Patches were tested on Exynos4412 based Odroid U3, Exynos5422 > Odroid XU3 and Exynos5433 TM2 boards, on top of Linux next-20171016 kernel. When submitting uapi changes, please link to the corresponding userspace work. Otherwise pretty hard to find this stuff. -Daniel > > Best regards > Marek Szyprowski > Samsung R&D Institute Poland > > > Changelog: > > v3: > - fixed minor issues and added features pointed by other developers: > * fixed missing ipp_unregister (Hoegeun) > * added missing limits to FIMC and GSC driver (Hoegeun) > * added more specific compatible strings to GSC driver (Hoegeun) > * added Exynos5433 support to GSC driver (Hoegeun) > * added autosuspend runtime PM to all IPP drivers (Tobias) > - added Exynos5433 support to Scaler driver (thanks to Andrzej) > - dropped Exynos5420 clk patch, which has been alredy merged > > v2: https://www.spinics.net/lists/dri-devel/msg153418.html > - fixed minor issues pointed by other developers: > * fixed possible null pointer dereferrence (Tobias) > * changed limits_size to limits_count (Tobias) > * renamed struct exynos_drm_ipp_format to drm_exynos_ipp_format (Andrzej) > * added proper return value from exynos_drm_ipp_get_res_ioctl when no IPP > driver is present (Andrzej) > * properly aligned all uapi structures to be 32/64 bit safe (Emil) > * properly initialize all strucutres > - added new Exynos Scaler driver from Andrzej Pietrasiewicz > > v1: https://www.spinics.net/lists/linux-samsung-soc/msg60492.html > - initial version of IPP v2 > > My previous works in this area: > > "[RFC v2 0/2] Exynos DRM: add Picture Processor extension" > https://www.spinics.net/lists/dri-devel/msg140669.html > - removed usage of DRM objects and properties - replaced them with simple > list of parameters with predefined IDs > > "[RFC 0/4] Exynos DRM: add Picture Processor extension" > https://www.spinics.net/lists/linux-samsung-soc/msg59323.html > - moved this feature from DRM core to Exynos DRM driver > - changed name from framebuffer processor to picture processor > - simplified code to cover only things needed by Exynos drivers > - implemented simple fifo task scheduler > - cleaned up rotator driver conversion (removed IPP remainings) > > "[RFC 0/2] New feature: Framebuffer processors" > https://www.spinics.net/lists/linux-samsung-soc/msg54810.html > - generic approach implemented in DRM core, rejected > > > Patch summary: > > Andrzej Pietrasiewicz (3): > drm/exynos: Add driver for Exynos Scaler module > ARM: dts: exynos: Add mem-2-mem Scaler devices > ARM64: dts: exynos: Add mem-2-mem Scaler devices > > Marek Szyprowski (6): > drm/exynos: ipp: Remove Exynos DRM IPP subsystem > drm/exynos: ipp: Add IPP v2 framework > drm/exynos: rotator: Convert driver to IPP v2 core API > drm/exynos: gsc: Convert driver to IPP v2 core API > drm/exynos: Add generic support for devices shared with V4L2 subsystem > drm/exynos: fimc: Convert driver to IPP v2 core API > > .../devicetree/bindings/gpu/samsung-scaler.txt | 26 + > arch/arm/boot/dts/exynos5420.dtsi | 35 + > arch/arm64/boot/dts/exynos/exynos5433.dtsi | 42 + > drivers/gpu/drm/exynos/Kconfig | 17 +- > drivers/gpu/drm/exynos/Makefile | 1 + > drivers/gpu/drm/exynos/exynos_drm_drv.c | 36 +- > drivers/gpu/drm/exynos/exynos_drm_drv.h | 5 +- > drivers/gpu/drm/exynos/exynos_drm_fimc.c | 930 +++----- > drivers/gpu/drm/exynos/exynos_drm_fimc.h | 23 - > drivers/gpu/drm/exynos/exynos_drm_gsc.c | 995 +++------ > drivers/gpu/drm/exynos/exynos_drm_gsc.h | 24 - > drivers/gpu/drm/exynos/exynos_drm_ipp.c | 2240 ++++++-------------- > drivers/gpu/drm/exynos/exynos_drm_ipp.h | 357 ++-- > drivers/gpu/drm/exynos/exynos_drm_rotator.c | 740 ++----- > drivers/gpu/drm/exynos/exynos_drm_rotator.h | 19 - > drivers/gpu/drm/exynos/exynos_drm_scaler.c | 717 +++++++ > drivers/gpu/drm/exynos/regs-scaler.h | 426 ++++ > include/uapi/drm/exynos_drm.h | 326 +-- > 18 files changed, 3057 insertions(+), 3902 deletions(-) > create mode 100644 Documentation/devicetree/bindings/gpu/samsung-scaler.txt > delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_fimc.h > delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_gsc.h > delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h > create mode 100644 drivers/gpu/drm/exynos/exynos_drm_scaler.c > create mode 100644 drivers/gpu/drm/exynos/regs-scaler.h > > -- > 2.14.2 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Daniel, On 2017-10-17 14:24, Daniel Vetter wrote: > On Tue, Oct 17, 2017 at 01:07:43PM +0200, Marek Szyprowski wrote: >> This patchset performs complete rewrite of Exynos DRM IPP subsystem and >> its userspace API. >> >> Why such rewrite is needed? Exynos DRM IPP API is over-engineered in >> general, but not really extensible on the other side. It is also buggy, >> with significant design flaws: >> - Userspace API covers memory-2-memory picture operations together with >> CRTC writeback and duplicating features, which belongs to video plane. >> - Lack of support of the all required image formats (for example NV12 >> Samsung-tiled cannot be used due to lack of pixel format modifier >> support). >> - Userspace API designed only to mimic hardware behaviour, not easy to >> understand. >> - Lack of proper input validation in the core, drivers also didn't do that >> correctly, so it was possible to set incorrect parameters and easil >> trigger IOMMU fault or memory trash. >> - Drivers were partially disfunctional or supported only a subset of modes. >> >> Due to the above limitations and issues the Exynos DRM IPP API was not >> used by any of the open-source projects. I assume that it is safe to remove >> this broken API without any damage to open-source community. All remaining >> users (mainly Tizen project related) will be updated to the new version. >> >> This patchset changes Exynos DRM IPP subsystem to something useful. The >> userspace API is much simpler, state-less and easy to understand. Also >> the code of the core and driver is significantly smaller and easier to >> understand. >> >> Patches were tested on Exynos4412 based Odroid U3, Exynos5422 >> Odroid XU3 and Exynos5433 TM2 boards, on top of Linux next-20171016 kernel. > When submitting uapi changes, please link to the corresponding userspace > work. Otherwise pretty hard to find this stuff. A simple userspace test tool has been sent together with v1: https://www.spinics.net/lists/linux-samsung-soc/msg60498.html Tobias Jakobi has added support for this new API to his fork of libdrm and mpv video player: https://github.com/tobiasjakobi/libdrm/tree/ippv2 https://github.com/tobiasjakobi/mpv Best regards -- Marek Szyprowski, PhD Samsung R&D Institute Poland -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hey Marek, Marek Szyprowski wrote: > This patch adapts Exynos DRM rotator driver to new IPP v2 core API. you probably mean "DRM gsc driver" here (or GSC, not sure about capitalisation). - Tobias > The side effect of this conversion is a switch to driver component API > to register properly in the Exynos DRM core. > > Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> > Tested-by: Hoegeun Kwon <hoegeun.kwon@samsung.com> > --- > drivers/gpu/drm/exynos/Kconfig | 3 +- > drivers/gpu/drm/exynos/exynos_drm_drv.c | 1 + > drivers/gpu/drm/exynos/exynos_drm_gsc.c | 995 ++++++++++---------------------- > drivers/gpu/drm/exynos/exynos_drm_gsc.h | 24 - > 4 files changed, 315 insertions(+), 708 deletions(-) > delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_gsc.h > > diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig > index c10c9ca0d8b4..4bb9edb00601 100644 > --- a/drivers/gpu/drm/exynos/Kconfig > +++ b/drivers/gpu/drm/exynos/Kconfig > @@ -111,7 +111,8 @@ config DRM_EXYNOS_ROTATOR > > config DRM_EXYNOS_GSC > bool "GScaler" > - depends on BROKEN && ARCH_EXYNOS5 && VIDEO_SAMSUNG_EXYNOS_GSC=n > + depends on ARCH_EXYNOS5 && VIDEO_SAMSUNG_EXYNOS_GSC=n > + select DRM_EXYNOS_IPP > help > Choose this option if you want to use Exynos GSC for DRM. > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c > index 3ade2b0ad15d..cac0d84385d3 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c > +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c > @@ -260,6 +260,7 @@ static struct exynos_drm_driver_info exynos_drm_drivers[] = { > DRM_COMPONENT_DRIVER > }, { > DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), > + DRM_COMPONENT_DRIVER > }, { > &exynos_drm_platform_driver, > DRM_VIRTUAL_DEVICE > diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c > index 0506b2b17ac1..f99d4fea9df1 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c > +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c > @@ -12,18 +12,20 @@ > * > */ > #include <linux/kernel.h> > +#include <linux/component.h> > #include <linux/platform_device.h> > #include <linux/clk.h> > #include <linux/pm_runtime.h> > #include <linux/mfd/syscon.h> > +#include <linux/of_device.h> > #include <linux/regmap.h> > > #include <drm/drmP.h> > #include <drm/exynos_drm.h> > #include "regs-gsc.h" > #include "exynos_drm_drv.h" > +#include "exynos_drm_iommu.h" > #include "exynos_drm_ipp.h" > -#include "exynos_drm_gsc.h" > > /* > * GSC stands for General SCaler and > @@ -31,26 +33,10 @@ > * input DMA reads image data from the memory. > * output DMA writes image data to memory. > * GSC supports image rotation and image effect functions. > - * > - * M2M operation : supports crop/scale/rotation/csc so on. > - * Memory ----> GSC H/W ----> Memory. > - * Writeback operation : supports cloned screen with FIMD. > - * FIMD ----> GSC H/W ----> Memory. > - * Output operation : supports direct display using local path. > - * Memory ----> GSC H/W ----> FIMD, Mixer. > */ > > -/* > - * TODO > - * 1. check suspend/resume api if needed. > - * 2. need to check use case platform_device_id. > - * 3. check src/dst size with, height. > - * 4. added check_prepare api for right register. > - * 5. need to add supported list in prop_list. > - * 6. check prescaler/scaler optimization. > - */ > > -#define GSC_MAX_DEVS 4 > +#define GSC_MAX_CLOCKS 8 > #define GSC_MAX_SRC 4 > #define GSC_MAX_DST 16 > #define GSC_RESET_TIMEOUT 50 > @@ -65,8 +51,6 @@ > #define GSC_SC_DOWN_RATIO_4_8 131072 > #define GSC_SC_DOWN_RATIO_3_8 174762 > #define GSC_SC_DOWN_RATIO_2_8 262144 > -#define GSC_REFRESH_MIN 12 > -#define GSC_REFRESH_MAX 60 > #define GSC_CROP_MAX 8192 > #define GSC_CROP_MIN 32 > #define GSC_SCALE_MAX 4224 > @@ -77,10 +61,9 @@ > #define GSC_COEF_H_8T 8 > #define GSC_COEF_V_4T 4 > #define GSC_COEF_DEPTH 3 > +#define GSC_AUTOSUSPEND_DELAY 2000 > > #define get_gsc_context(dev) platform_get_drvdata(to_platform_device(dev)) > -#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ > - struct gsc_context, ippdrv); > #define gsc_read(offset) readl(ctx->regs + (offset)) > #define gsc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) > > @@ -124,7 +107,6 @@ struct gsc_capability { > /* > * A structure of gsc context. > * > - * @ippdrv: prepare initialization using ippdrv. > * @regs_res: register resources. > * @regs: memory mapped io registers. > * @sysreg: handle to SYSREG block regmap. > @@ -137,12 +119,18 @@ struct gsc_capability { > * @suspended: qos operations. > */ > struct gsc_context { > - struct exynos_drm_ippdrv ippdrv; > + struct exynos_drm_ipp ipp; > + struct drm_device *drm_dev; > + struct device *dev; > + struct exynos_drm_ipp_task *task; > + struct exynos_drm_ipp_formats *formats; > + > struct resource *regs_res; > void __iomem *regs; > struct regmap *sysreg; > - struct mutex lock; > - struct clk *gsc_clk; > + const char **clk_names; > + struct clk *clocks[GSC_MAX_CLOCKS]; > + int num_clocks; > struct gsc_scaler sc; > int id; > int irq; > @@ -150,6 +138,19 @@ struct gsc_context { > bool suspended; > }; > > +/** > + * struct gsc_driverdata - per device type driver data for init time. > + * > + * @limits: picture size limits array > + * @clk_names: names of clocks needed by this variant > + * @num_clocks: the number of clocks needed by this variant > + */ > +struct gsc_driverdata { > + const struct drm_exynos_ipp_limit *limits; > + const char *clk_names[GSC_MAX_CLOCKS]; > + int num_clocks; > +}; > + > /* 8-tap Filter Coefficient */ > static const int h_coef_8t[GSC_COEF_RATIO][GSC_COEF_ATTR][GSC_COEF_H_8T] = { > { /* Ratio <= 65536 (~8:8) */ > @@ -438,25 +439,6 @@ static int gsc_sw_reset(struct gsc_context *ctx) > return 0; > } > > -static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) > -{ > - unsigned int gscblk_cfg; > - > - if (!ctx->sysreg) > - return; > - > - regmap_read(ctx->sysreg, SYSREG_GSCBLK_CFG1, &gscblk_cfg); > - > - if (enable) > - gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) | > - GSC_BLK_GSCL_WB_IN_SRC_SEL(ctx->id) | > - GSC_BLK_SW_RESET_WB_DEST(ctx->id); > - else > - gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id); > - > - regmap_write(ctx->sysreg, SYSREG_GSCBLK_CFG1, gscblk_cfg); > -} > - > static void gsc_handle_irq(struct gsc_context *ctx, bool enable, > bool overflow, bool done) > { > @@ -487,10 +469,8 @@ static void gsc_handle_irq(struct gsc_context *ctx, bool enable, > } > > > -static int gsc_src_set_fmt(struct device *dev, u32 fmt) > +static int gsc_src_set_fmt(struct gsc_context *ctx, u32 fmt) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > u32 cfg; > > DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); > @@ -549,7 +529,7 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt) > GSC_IN_YUV420_2P); > break; > default: > - dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt); > + dev_err(ctx->dev, "invalid target yuv order 0x%x.\n", fmt); > return -EINVAL; > } > > @@ -558,105 +538,81 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt) > return 0; > } > > -static int gsc_src_set_transf(struct device *dev, > - enum drm_exynos_degree degree, > - enum drm_exynos_flip flip, bool *swap) > +static int gsc_src_set_transf(struct gsc_context *ctx, unsigned int rotation) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > + unsigned int degree = rotation & DRM_MODE_ROTATE_MASK; > u32 cfg; > > - DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); > - > cfg = gsc_read(GSC_IN_CON); > cfg &= ~GSC_IN_ROT_MASK; > > switch (degree) { > - case EXYNOS_DRM_DEGREE_0: > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > + case DRM_MODE_ROTATE_0: > + if (rotation & DRM_MODE_REFLECT_Y) > cfg |= GSC_IN_ROT_XFLIP; > - if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > + if (rotation & DRM_MODE_REFLECT_X) > cfg |= GSC_IN_ROT_YFLIP; > break; > - case EXYNOS_DRM_DEGREE_90: > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > + case DRM_MODE_ROTATE_90: > + if (rotation & DRM_MODE_REFLECT_Y) > cfg |= GSC_IN_ROT_90_XFLIP; > - else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > + else if (rotation & DRM_MODE_REFLECT_X) > cfg |= GSC_IN_ROT_90_YFLIP; > else > cfg |= GSC_IN_ROT_90; > break; > - case EXYNOS_DRM_DEGREE_180: > + case DRM_MODE_ROTATE_180: > cfg |= GSC_IN_ROT_180; > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > + if (rotation & DRM_MODE_REFLECT_Y) > cfg &= ~GSC_IN_ROT_XFLIP; > - if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > + if (rotation & DRM_MODE_REFLECT_X) > cfg &= ~GSC_IN_ROT_YFLIP; > break; > - case EXYNOS_DRM_DEGREE_270: > + case DRM_MODE_ROTATE_270: > cfg |= GSC_IN_ROT_270; > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > + if (rotation & DRM_MODE_REFLECT_Y) > cfg &= ~GSC_IN_ROT_XFLIP; > - if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > + if (rotation & DRM_MODE_REFLECT_X) > cfg &= ~GSC_IN_ROT_YFLIP; > break; > - default: > - dev_err(ippdrv->dev, "invalid degree value %d.\n", degree); > - return -EINVAL; > } > > gsc_write(cfg, GSC_IN_CON); > > ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0; > - *swap = ctx->rotation; > - > return 0; > } > > -static int gsc_src_set_size(struct device *dev, int swap, > - struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) > +static int gsc_src_set_size(struct gsc_context *ctx, > + struct exynos_drm_ipp_buffer *buf) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct drm_exynos_pos img_pos = *pos; > struct gsc_scaler *sc = &ctx->sc; > u32 cfg; > > - DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n", > - swap, pos->x, pos->y, pos->w, pos->h); > - > - if (swap) { > - img_pos.w = pos->h; > - img_pos.h = pos->w; > - } > - > /* pixel offset */ > - cfg = (GSC_SRCIMG_OFFSET_X(img_pos.x) | > - GSC_SRCIMG_OFFSET_Y(img_pos.y)); > + cfg = (GSC_SRCIMG_OFFSET_X(buf->rect.x) | > + GSC_SRCIMG_OFFSET_Y(buf->rect.y)); > gsc_write(cfg, GSC_SRCIMG_OFFSET); > > /* cropped size */ > - cfg = (GSC_CROPPED_WIDTH(img_pos.w) | > - GSC_CROPPED_HEIGHT(img_pos.h)); > + cfg = (GSC_CROPPED_WIDTH(buf->rect.w) | > + GSC_CROPPED_HEIGHT(buf->rect.h)); > gsc_write(cfg, GSC_CROPPED_SIZE); > > - DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize); > - > /* original size */ > cfg = gsc_read(GSC_SRCIMG_SIZE); > cfg &= ~(GSC_SRCIMG_HEIGHT_MASK | > GSC_SRCIMG_WIDTH_MASK); > > - cfg |= (GSC_SRCIMG_WIDTH(sz->hsize) | > - GSC_SRCIMG_HEIGHT(sz->vsize)); > + cfg |= (GSC_SRCIMG_WIDTH(buf->buf.width) | > + GSC_SRCIMG_HEIGHT(buf->buf.height)); > > gsc_write(cfg, GSC_SRCIMG_SIZE); > > cfg = gsc_read(GSC_IN_CON); > cfg &= ~GSC_IN_RGB_TYPE_MASK; > > - DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range); > - > - if (pos->w >= GSC_WIDTH_ITU_709) > + if (buf->rect.w >= GSC_WIDTH_ITU_709) > if (sc->range) > cfg |= GSC_IN_RGB_HD_WIDE; > else > @@ -673,30 +629,15 @@ static int gsc_src_set_size(struct device *dev, int swap, > } > > static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id, > - enum drm_exynos_ipp_buf_type buf_type) > + bool enqueue) > { > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - bool masked; > + bool masked = !enqueue; > u32 cfg; > u32 mask = 0x00000001 << buf_id; > > - DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); > - > /* mask register set */ > cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); > > - switch (buf_type) { > - case IPP_BUF_ENQUEUE: > - masked = false; > - break; > - case IPP_BUF_DEQUEUE: > - masked = true; > - break; > - default: > - dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); > - return -EINVAL; > - } > - > /* sequence id */ > cfg &= ~mask; > cfg |= masked << buf_id; > @@ -707,64 +648,19 @@ static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id, > return 0; > } > > -static int gsc_src_set_addr(struct device *dev, > - struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, > - enum drm_exynos_ipp_buf_type buf_type) > +static int gsc_src_set_addr(struct gsc_context *ctx, u32 buf_id, > + struct exynos_drm_ipp_buffer *buf) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; > - struct drm_exynos_ipp_property *property; > - > - if (!c_node) { > - DRM_ERROR("failed to get c_node.\n"); > - return -EFAULT; > - } > - > - property = &c_node->property; > - > - DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", > - property->prop_id, buf_id, buf_type); > - > - if (buf_id > GSC_MAX_SRC) { > - dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id); > - return -EINVAL; > - } > - > /* address register set */ > - switch (buf_type) { > - case IPP_BUF_ENQUEUE: > - gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], > - GSC_IN_BASE_ADDR_Y(buf_id)); > - gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], > - GSC_IN_BASE_ADDR_CB(buf_id)); > - gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], > - GSC_IN_BASE_ADDR_CR(buf_id)); > - break; > - case IPP_BUF_DEQUEUE: > - gsc_write(0x0, GSC_IN_BASE_ADDR_Y(buf_id)); > - gsc_write(0x0, GSC_IN_BASE_ADDR_CB(buf_id)); > - gsc_write(0x0, GSC_IN_BASE_ADDR_CR(buf_id)); > - break; > - default: > - /* bypass */ > - break; > - } > + gsc_write(buf->dma_addr[0], GSC_IN_BASE_ADDR_Y(buf_id)); > + gsc_write(buf->dma_addr[1], GSC_IN_BASE_ADDR_CB(buf_id)); > + gsc_write(buf->dma_addr[2], GSC_IN_BASE_ADDR_CR(buf_id)); > > - return gsc_src_set_buf_seq(ctx, buf_id, buf_type); > + return gsc_src_set_buf_seq(ctx, buf_id, true); > } > > -static struct exynos_drm_ipp_ops gsc_src_ops = { > - .set_fmt = gsc_src_set_fmt, > - .set_transf = gsc_src_set_transf, > - .set_size = gsc_src_set_size, > - .set_addr = gsc_src_set_addr, > -}; > - > -static int gsc_dst_set_fmt(struct device *dev, u32 fmt) > +static int gsc_dst_set_fmt(struct gsc_context *ctx, u32 fmt) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > u32 cfg; > > DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); > @@ -819,9 +715,6 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt) > cfg |= (GSC_OUT_CHROMA_ORDER_CBCR | > GSC_OUT_YUV420_2P); > break; > - default: > - dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt); > - return -EINVAL; > } > > gsc_write(cfg, GSC_OUT_CON); > @@ -829,61 +722,6 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt) > return 0; > } > > -static int gsc_dst_set_transf(struct device *dev, > - enum drm_exynos_degree degree, > - enum drm_exynos_flip flip, bool *swap) > -{ > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - u32 cfg; > - > - DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); > - > - cfg = gsc_read(GSC_IN_CON); > - cfg &= ~GSC_IN_ROT_MASK; > - > - switch (degree) { > - case EXYNOS_DRM_DEGREE_0: > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > - cfg |= GSC_IN_ROT_XFLIP; > - if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > - cfg |= GSC_IN_ROT_YFLIP; > - break; > - case EXYNOS_DRM_DEGREE_90: > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > - cfg |= GSC_IN_ROT_90_XFLIP; > - else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > - cfg |= GSC_IN_ROT_90_YFLIP; > - else > - cfg |= GSC_IN_ROT_90; > - break; > - case EXYNOS_DRM_DEGREE_180: > - cfg |= GSC_IN_ROT_180; > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > - cfg &= ~GSC_IN_ROT_XFLIP; > - if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > - cfg &= ~GSC_IN_ROT_YFLIP; > - break; > - case EXYNOS_DRM_DEGREE_270: > - cfg |= GSC_IN_ROT_270; > - if (flip & EXYNOS_DRM_FLIP_VERTICAL) > - cfg &= ~GSC_IN_ROT_XFLIP; > - if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) > - cfg &= ~GSC_IN_ROT_YFLIP; > - break; > - default: > - dev_err(ippdrv->dev, "invalid degree value %d.\n", degree); > - return -EINVAL; > - } > - > - gsc_write(cfg, GSC_IN_CON); > - > - ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0; > - *swap = ctx->rotation; > - > - return 0; > -} > - > static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio) > { > DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst); > @@ -919,9 +757,8 @@ static void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *shfactor) > } > > static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, > - struct drm_exynos_pos *src, struct drm_exynos_pos *dst) > + struct drm_exynos_ipp_task_rect *src, struct drm_exynos_ipp_task_rect *dst) > { > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > u32 cfg; > u32 src_w, src_h, dst_w, dst_h; > int ret = 0; > @@ -939,13 +776,13 @@ static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, > > ret = gsc_get_ratio_shift(src_w, dst_w, &sc->pre_hratio); > if (ret) { > - dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); > + dev_err(ctx->dev, "failed to get ratio horizontal.\n"); > return ret; > } > > ret = gsc_get_ratio_shift(src_h, dst_h, &sc->pre_vratio); > if (ret) { > - dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); > + dev_err(ctx->dev, "failed to get ratio vertical.\n"); > return ret; > } > > @@ -1039,47 +876,31 @@ static void gsc_set_scaler(struct gsc_context *ctx, struct gsc_scaler *sc) > gsc_write(cfg, GSC_MAIN_V_RATIO); > } > > -static int gsc_dst_set_size(struct device *dev, int swap, > - struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) > +static int gsc_dst_set_size(struct gsc_context *ctx, > + struct exynos_drm_ipp_buffer *buf) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct drm_exynos_pos img_pos = *pos; > struct gsc_scaler *sc = &ctx->sc; > u32 cfg; > > - DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n", > - swap, pos->x, pos->y, pos->w, pos->h); > - > - if (swap) { > - img_pos.w = pos->h; > - img_pos.h = pos->w; > - } > - > /* pixel offset */ > - cfg = (GSC_DSTIMG_OFFSET_X(pos->x) | > - GSC_DSTIMG_OFFSET_Y(pos->y)); > + cfg = (GSC_DSTIMG_OFFSET_X(buf->rect.x) | > + GSC_DSTIMG_OFFSET_Y(buf->rect.y)); > gsc_write(cfg, GSC_DSTIMG_OFFSET); > > /* scaled size */ > - cfg = (GSC_SCALED_WIDTH(img_pos.w) | GSC_SCALED_HEIGHT(img_pos.h)); > + cfg = (GSC_SCALED_WIDTH(buf->rect.w) | GSC_SCALED_HEIGHT(buf->rect.h)); > gsc_write(cfg, GSC_SCALED_SIZE); > > - DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize); > - > /* original size */ > cfg = gsc_read(GSC_DSTIMG_SIZE); > - cfg &= ~(GSC_DSTIMG_HEIGHT_MASK | > - GSC_DSTIMG_WIDTH_MASK); > - cfg |= (GSC_DSTIMG_WIDTH(sz->hsize) | > - GSC_DSTIMG_HEIGHT(sz->vsize)); > + cfg &= ~(GSC_DSTIMG_HEIGHT_MASK | GSC_DSTIMG_WIDTH_MASK); > + cfg |= (GSC_DSTIMG_WIDTH(buf->buf.width) | GSC_DSTIMG_HEIGHT(buf->buf.height)); > gsc_write(cfg, GSC_DSTIMG_SIZE); > > cfg = gsc_read(GSC_OUT_CON); > cfg &= ~GSC_OUT_RGB_TYPE_MASK; > > - DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range); > - > - if (pos->w >= GSC_WIDTH_ITU_709) > + if (buf->rect.w >= GSC_WIDTH_ITU_709) > if (sc->range) > cfg |= GSC_OUT_RGB_HD_WIDE; > else > @@ -1112,34 +933,16 @@ static int gsc_dst_get_buf_seq(struct gsc_context *ctx) > } > > static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, > - enum drm_exynos_ipp_buf_type buf_type) > + bool enqueue) > { > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - bool masked; > + bool masked = !enqueue; > u32 cfg; > u32 mask = 0x00000001 << buf_id; > int ret = 0; > > - DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); > - > - mutex_lock(&ctx->lock); > - > /* mask register set */ > cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); > > - switch (buf_type) { > - case IPP_BUF_ENQUEUE: > - masked = false; > - break; > - case IPP_BUF_DEQUEUE: > - masked = true; > - break; > - default: > - dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); > - ret = -EINVAL; > - goto err_unlock; > - } > - > /* sequence id */ > cfg &= ~mask; > cfg |= masked << buf_id; > @@ -1148,87 +951,25 @@ static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, > gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK); > > /* interrupt enable */ > - if (buf_type == IPP_BUF_ENQUEUE && > - gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START) > + if (enqueue && gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START) > gsc_handle_irq(ctx, true, false, true); > > /* interrupt disable */ > - if (buf_type == IPP_BUF_DEQUEUE && > - gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP) > + if (!enqueue && gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP) > gsc_handle_irq(ctx, false, false, true); > > -err_unlock: > - mutex_unlock(&ctx->lock); > return ret; > } > > -static int gsc_dst_set_addr(struct device *dev, > - struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, > - enum drm_exynos_ipp_buf_type buf_type) > +static int gsc_dst_set_addr(struct gsc_context *ctx, > + u32 buf_id, struct exynos_drm_ipp_buffer *buf) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; > - struct drm_exynos_ipp_property *property; > - > - if (!c_node) { > - DRM_ERROR("failed to get c_node.\n"); > - return -EFAULT; > - } > - > - property = &c_node->property; > - > - DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", > - property->prop_id, buf_id, buf_type); > - > - if (buf_id > GSC_MAX_DST) { > - dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id); > - return -EINVAL; > - } > - > /* address register set */ > - switch (buf_type) { > - case IPP_BUF_ENQUEUE: > - gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], > - GSC_OUT_BASE_ADDR_Y(buf_id)); > - gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], > - GSC_OUT_BASE_ADDR_CB(buf_id)); > - gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], > - GSC_OUT_BASE_ADDR_CR(buf_id)); > - break; > - case IPP_BUF_DEQUEUE: > - gsc_write(0x0, GSC_OUT_BASE_ADDR_Y(buf_id)); > - gsc_write(0x0, GSC_OUT_BASE_ADDR_CB(buf_id)); > - gsc_write(0x0, GSC_OUT_BASE_ADDR_CR(buf_id)); > - break; > - default: > - /* bypass */ > - break; > - } > - > - return gsc_dst_set_buf_seq(ctx, buf_id, buf_type); > -} > - > -static struct exynos_drm_ipp_ops gsc_dst_ops = { > - .set_fmt = gsc_dst_set_fmt, > - .set_transf = gsc_dst_set_transf, > - .set_size = gsc_dst_set_size, > - .set_addr = gsc_dst_set_addr, > -}; > - > -static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable) > -{ > - DRM_DEBUG_KMS("enable[%d]\n", enable); > + gsc_write(buf->dma_addr[0], GSC_OUT_BASE_ADDR_Y(buf_id)); > + gsc_write(buf->dma_addr[1], GSC_OUT_BASE_ADDR_CB(buf_id)); > + gsc_write(buf->dma_addr[2], GSC_OUT_BASE_ADDR_CR(buf_id)); > > - if (enable) { > - clk_prepare_enable(ctx->gsc_clk); > - ctx->suspended = false; > - } else { > - clk_disable_unprepare(ctx->gsc_clk); > - ctx->suspended = true; > - } > - > - return 0; > + return gsc_dst_set_buf_seq(ctx, buf_id, true); > } > > static int gsc_get_src_buf_index(struct gsc_context *ctx) > @@ -1249,20 +990,20 @@ static int gsc_get_src_buf_index(struct gsc_context *ctx) > } > } > > + DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, > + curr_index, buf_id); > + > if (buf_id == GSC_MAX_SRC) { > DRM_ERROR("failed to get in buffer index.\n"); > return -EINVAL; > } > > - ret = gsc_src_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); > + ret = gsc_src_set_buf_seq(ctx, buf_id, false); > if (ret < 0) { > DRM_ERROR("failed to dequeue.\n"); > return ret; > } > > - DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, > - curr_index, buf_id); > - > return buf_id; > } > > @@ -1289,7 +1030,7 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx) > return -EINVAL; > } > > - ret = gsc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); > + ret = gsc_dst_set_buf_seq(ctx, buf_id, false); > if (ret < 0) { > DRM_ERROR("failed to dequeue.\n"); > return ret; > @@ -1304,215 +1045,55 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx) > static irqreturn_t gsc_irq_handler(int irq, void *dev_id) > { > struct gsc_context *ctx = dev_id; > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; > - struct drm_exynos_ipp_event_work *event_work = > - c_node->event_work; > u32 status; > - int buf_id[EXYNOS_DRM_OPS_MAX]; > + int err = 0; > > DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); > > status = gsc_read(GSC_IRQ); > if (status & GSC_IRQ_STATUS_OR_IRQ) { > - dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n", > + dev_err(ctx->dev, "occurred overflow at %d, status 0x%x.\n", > ctx->id, status); > - return IRQ_NONE; > + err = -EINVAL; > } > > if (status & GSC_IRQ_STATUS_OR_FRM_DONE) { > - dev_dbg(ippdrv->dev, "occurred frame done at %d, status 0x%x.\n", > - ctx->id, status); > - > - buf_id[EXYNOS_DRM_OPS_SRC] = gsc_get_src_buf_index(ctx); > - if (buf_id[EXYNOS_DRM_OPS_SRC] < 0) > - return IRQ_HANDLED; > - > - buf_id[EXYNOS_DRM_OPS_DST] = gsc_get_dst_buf_index(ctx); > - if (buf_id[EXYNOS_DRM_OPS_DST] < 0) > - return IRQ_HANDLED; > + int src_buf_id, dst_buf_id; > > - DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n", > - buf_id[EXYNOS_DRM_OPS_SRC], buf_id[EXYNOS_DRM_OPS_DST]); > - > - event_work->ippdrv = ippdrv; > - event_work->buf_id[EXYNOS_DRM_OPS_SRC] = > - buf_id[EXYNOS_DRM_OPS_SRC]; > - event_work->buf_id[EXYNOS_DRM_OPS_DST] = > - buf_id[EXYNOS_DRM_OPS_DST]; > - queue_work(ippdrv->event_workq, &event_work->work); > - } > - > - return IRQ_HANDLED; > -} > - > -static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) > -{ > - struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list; > - > - prop_list->version = 1; > - prop_list->writeback = 1; > - prop_list->refresh_min = GSC_REFRESH_MIN; > - prop_list->refresh_max = GSC_REFRESH_MAX; > - prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) | > - (1 << EXYNOS_DRM_FLIP_HORIZONTAL); > - prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | > - (1 << EXYNOS_DRM_DEGREE_90) | > - (1 << EXYNOS_DRM_DEGREE_180) | > - (1 << EXYNOS_DRM_DEGREE_270); > - prop_list->csc = 1; > - prop_list->crop = 1; > - prop_list->crop_max.hsize = GSC_CROP_MAX; > - prop_list->crop_max.vsize = GSC_CROP_MAX; > - prop_list->crop_min.hsize = GSC_CROP_MIN; > - prop_list->crop_min.vsize = GSC_CROP_MIN; > - prop_list->scale = 1; > - prop_list->scale_max.hsize = GSC_SCALE_MAX; > - prop_list->scale_max.vsize = GSC_SCALE_MAX; > - prop_list->scale_min.hsize = GSC_SCALE_MIN; > - prop_list->scale_min.vsize = GSC_SCALE_MIN; > - > - return 0; > -} > - > -static inline bool gsc_check_drm_flip(enum drm_exynos_flip flip) > -{ > - switch (flip) { > - case EXYNOS_DRM_FLIP_NONE: > - case EXYNOS_DRM_FLIP_VERTICAL: > - case EXYNOS_DRM_FLIP_HORIZONTAL: > - case EXYNOS_DRM_FLIP_BOTH: > - return true; > - default: > - DRM_DEBUG_KMS("invalid flip\n"); > - return false; > - } > -} > - > -static int gsc_ippdrv_check_property(struct device *dev, > - struct drm_exynos_ipp_property *property) > -{ > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list; > - struct drm_exynos_ipp_config *config; > - struct drm_exynos_pos *pos; > - struct drm_exynos_sz *sz; > - bool swap; > - int i; > - > - for_each_ipp_ops(i) { > - if ((i == EXYNOS_DRM_OPS_SRC) && > - (property->cmd == IPP_CMD_WB)) > - continue; > - > - config = &property->config[i]; > - pos = &config->pos; > - sz = &config->sz; > - > - /* check for flip */ > - if (!gsc_check_drm_flip(config->flip)) { > - DRM_ERROR("invalid flip.\n"); > - goto err_property; > - } > - > - /* check for degree */ > - switch (config->degree) { > - case EXYNOS_DRM_DEGREE_90: > - case EXYNOS_DRM_DEGREE_270: > - swap = true; > - break; > - case EXYNOS_DRM_DEGREE_0: > - case EXYNOS_DRM_DEGREE_180: > - swap = false; > - break; > - default: > - DRM_ERROR("invalid degree.\n"); > - goto err_property; > - } > + dev_dbg(ctx->dev, "occurred frame done at %d, status 0x%x.\n", > + ctx->id, status); > > - /* check for buffer bound */ > - if ((pos->x + pos->w > sz->hsize) || > - (pos->y + pos->h > sz->vsize)) { > - DRM_ERROR("out of buf bound.\n"); > - goto err_property; > - } > + src_buf_id = gsc_get_src_buf_index(ctx); > + dst_buf_id = gsc_get_dst_buf_index(ctx); > > - /* check for crop */ > - if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { > - if (swap) { > - if ((pos->h < pp->crop_min.hsize) || > - (sz->vsize > pp->crop_max.hsize) || > - (pos->w < pp->crop_min.vsize) || > - (sz->hsize > pp->crop_max.vsize)) { > - DRM_ERROR("out of crop size.\n"); > - goto err_property; > - } > - } else { > - if ((pos->w < pp->crop_min.hsize) || > - (sz->hsize > pp->crop_max.hsize) || > - (pos->h < pp->crop_min.vsize) || > - (sz->vsize > pp->crop_max.vsize)) { > - DRM_ERROR("out of crop size.\n"); > - goto err_property; > - } > - } > - } > + DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n", src_buf_id, > + dst_buf_id); > > - /* check for scale */ > - if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { > - if (swap) { > - if ((pos->h < pp->scale_min.hsize) || > - (sz->vsize > pp->scale_max.hsize) || > - (pos->w < pp->scale_min.vsize) || > - (sz->hsize > pp->scale_max.vsize)) { > - DRM_ERROR("out of scale size.\n"); > - goto err_property; > - } > - } else { > - if ((pos->w < pp->scale_min.hsize) || > - (sz->hsize > pp->scale_max.hsize) || > - (pos->h < pp->scale_min.vsize) || > - (sz->vsize > pp->scale_max.vsize)) { > - DRM_ERROR("out of scale size.\n"); > - goto err_property; > - } > - } > - } > + if (src_buf_id < 0 || dst_buf_id < 0) > + err = -EINVAL; > } > > - return 0; > - > -err_property: > - for_each_ipp_ops(i) { > - if ((i == EXYNOS_DRM_OPS_SRC) && > - (property->cmd == IPP_CMD_WB)) > - continue; > + if (ctx->task) { > + struct exynos_drm_ipp_task *task = ctx->task; > > - config = &property->config[i]; > - pos = &config->pos; > - sz = &config->sz; > - > - DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", > - i ? "dst" : "src", config->flip, config->degree, > - pos->x, pos->y, pos->w, pos->h, > - sz->hsize, sz->vsize); > + ctx->task = NULL; > + pm_runtime_mark_last_busy(ctx->dev); > + pm_runtime_put_autosuspend(ctx->dev); > + exynos_drm_ipp_task_done(task, err); > } > > - return -EINVAL; > + return IRQ_HANDLED; > } > > - > -static int gsc_ippdrv_reset(struct device *dev) > +static int gsc_reset(struct gsc_context *ctx) > { > - struct gsc_context *ctx = get_gsc_context(dev); > struct gsc_scaler *sc = &ctx->sc; > int ret; > > /* reset h/w block */ > ret = gsc_sw_reset(ctx); > if (ret < 0) { > - dev_err(dev, "failed to reset hardware.\n"); > + dev_err(ctx->dev, "failed to reset hardware.\n"); > return ret; > } > > @@ -1523,96 +1104,29 @@ static int gsc_ippdrv_reset(struct device *dev) > return 0; > } > > -static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) > +static int gsc_start(struct gsc_context *ctx) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; > - struct drm_exynos_ipp_property *property; > - struct drm_exynos_ipp_config *config; > - struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; > - struct drm_exynos_ipp_set_wb set_wb; > u32 cfg; > - int ret, i; > - > - DRM_DEBUG_KMS("cmd[%d]\n", cmd); > - > - if (!c_node) { > - DRM_ERROR("failed to get c_node.\n"); > - return -EINVAL; > - } > - > - property = &c_node->property; > > gsc_handle_irq(ctx, true, false, true); > > - for_each_ipp_ops(i) { > - config = &property->config[i]; > - img_pos[i] = config->pos; > - } > + /* enable one shot */ > + cfg = gsc_read(GSC_ENABLE); > + cfg &= ~(GSC_ENABLE_ON_CLEAR_MASK | > + GSC_ENABLE_CLK_GATE_MODE_MASK); > + cfg |= GSC_ENABLE_ON_CLEAR_ONESHOT; > + gsc_write(cfg, GSC_ENABLE); > > - switch (cmd) { > - case IPP_CMD_M2M: > - /* enable one shot */ > - cfg = gsc_read(GSC_ENABLE); > - cfg &= ~(GSC_ENABLE_ON_CLEAR_MASK | > - GSC_ENABLE_CLK_GATE_MODE_MASK); > - cfg |= GSC_ENABLE_ON_CLEAR_ONESHOT; > - gsc_write(cfg, GSC_ENABLE); > - > - /* src dma memory */ > - cfg = gsc_read(GSC_IN_CON); > - cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); > - cfg |= GSC_IN_PATH_MEMORY; > - gsc_write(cfg, GSC_IN_CON); > - > - /* dst dma memory */ > - cfg = gsc_read(GSC_OUT_CON); > - cfg |= GSC_OUT_PATH_MEMORY; > - gsc_write(cfg, GSC_OUT_CON); > - break; > - case IPP_CMD_WB: > - set_wb.enable = 1; > - set_wb.refresh = property->refresh_rate; > - gsc_set_gscblk_fimd_wb(ctx, set_wb.enable); > - exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); > - > - /* src local path */ > - cfg = gsc_read(GSC_IN_CON); > - cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); > - cfg |= (GSC_IN_PATH_LOCAL | GSC_IN_LOCAL_FIMD_WB); > - gsc_write(cfg, GSC_IN_CON); > - > - /* dst dma memory */ > - cfg = gsc_read(GSC_OUT_CON); > - cfg |= GSC_OUT_PATH_MEMORY; > - gsc_write(cfg, GSC_OUT_CON); > - break; > - case IPP_CMD_OUTPUT: > - /* src dma memory */ > - cfg = gsc_read(GSC_IN_CON); > - cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); > - cfg |= GSC_IN_PATH_MEMORY; > - gsc_write(cfg, GSC_IN_CON); > - > - /* dst local path */ > - cfg = gsc_read(GSC_OUT_CON); > - cfg |= GSC_OUT_PATH_MEMORY; > - gsc_write(cfg, GSC_OUT_CON); > - break; > - default: > - ret = -EINVAL; > - dev_err(dev, "invalid operations.\n"); > - return ret; > - } > + /* src dma memory */ > + cfg = gsc_read(GSC_IN_CON); > + cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); > + cfg |= GSC_IN_PATH_MEMORY; > + gsc_write(cfg, GSC_IN_CON); > > - ret = gsc_set_prescaler(ctx, &ctx->sc, > - &img_pos[EXYNOS_DRM_OPS_SRC], > - &img_pos[EXYNOS_DRM_OPS_DST]); > - if (ret) { > - dev_err(dev, "failed to set prescaler.\n"); > - return ret; > - } > + /* dst dma memory */ > + cfg = gsc_read(GSC_OUT_CON); > + cfg |= GSC_OUT_PATH_MEMORY; > + gsc_write(cfg, GSC_OUT_CON); > > gsc_set_scaler(ctx, &ctx->sc); > > @@ -1623,66 +1137,132 @@ static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) > return 0; > } > > -static void gsc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) > +static int gsc_commit(struct exynos_drm_ipp *ipp, > + struct exynos_drm_ipp_task *task) > { > - struct gsc_context *ctx = get_gsc_context(dev); > - struct drm_exynos_ipp_set_wb set_wb = {0, 0}; > - u32 cfg; > + struct gsc_context *ctx = container_of(ipp, struct gsc_context, ipp); > + > + pm_runtime_get_sync(ctx->dev); > + ctx->task = task; > + > + gsc_reset(ctx); > + gsc_src_set_fmt(ctx, task->src.buf.fourcc); > + gsc_src_set_transf(ctx, task->transform.rotation); > + gsc_src_set_size(ctx, &task->src); > + gsc_src_set_addr(ctx, 0, &task->src); > + gsc_dst_set_fmt(ctx, task->dst.buf.fourcc); > + gsc_dst_set_size(ctx, &task->dst); > + gsc_dst_set_addr(ctx, 0, &task->dst); > + gsc_set_prescaler(ctx, &ctx->sc, &task->src.rect, &task->dst.rect); > + gsc_start(ctx); > > - DRM_DEBUG_KMS("cmd[%d]\n", cmd); > + return 0; > +} > > - switch (cmd) { > - case IPP_CMD_M2M: > - /* bypass */ > - break; > - case IPP_CMD_WB: > - gsc_set_gscblk_fimd_wb(ctx, set_wb.enable); > - exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); > - break; > - case IPP_CMD_OUTPUT: > - default: > - dev_err(dev, "invalid operations.\n"); > - break; > +static void gsc_abort(struct exynos_drm_ipp *ipp, > + struct exynos_drm_ipp_task *task) > +{ > + struct gsc_context *ctx = > + container_of(ipp, struct gsc_context, ipp); > + > + gsc_reset(ctx); > + if (ctx->task) { > + struct exynos_drm_ipp_task *task = ctx->task; > + > + ctx->task = NULL; > + pm_runtime_mark_last_busy(ctx->dev); > + pm_runtime_put_autosuspend(ctx->dev); > + exynos_drm_ipp_task_done(task, -EIO); > } > +} > > - gsc_handle_irq(ctx, false, false, true); > +static struct exynos_drm_ipp_funcs ipp_funcs = { > + .commit = gsc_commit, > + .abort = gsc_abort, > +}; > > - /* reset sequence */ > - gsc_write(0xff, GSC_OUT_BASE_ADDR_Y_MASK); > - gsc_write(0xff, GSC_OUT_BASE_ADDR_CB_MASK); > - gsc_write(0xff, GSC_OUT_BASE_ADDR_CR_MASK); > +static int gsc_bind(struct device *dev, struct device *master, void *data) > +{ > + struct gsc_context *ctx = dev_get_drvdata(dev); > + struct drm_device *drm_dev = data; > + struct exynos_drm_ipp *ipp = &ctx->ipp; > > - cfg = gsc_read(GSC_ENABLE); > - cfg &= ~GSC_ENABLE_ON; > - gsc_write(cfg, GSC_ENABLE); > + ctx->drm_dev = drm_dev; > + drm_iommu_attach_device(drm_dev, dev); > + > + exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs, > + DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE | > + DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT, > + ctx->formats, "gsc"); > + > + dev_info(dev, "The exynos gscaler is probed successfully\n"); > + > + return 0; > +} > + > +static void gsc_unbind(struct device *dev, struct device *master, > + void *data) > +{ > + struct gsc_context *ctx = dev_get_drvdata(dev); > + struct drm_device *drm_dev = data; > + struct exynos_drm_ipp *ipp = &ctx->ipp; > + > + exynos_drm_ipp_unregister(drm_dev, ipp); > + drm_iommu_detach_device(drm_dev, dev); > } > > +static const struct component_ops gsc_component_ops = { > + .bind = gsc_bind, > + .unbind = gsc_unbind, > +}; > + > +static const unsigned int gsc_formats[] = { > + DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_BGRX8888, > + DRM_FORMAT_NV12, DRM_FORMAT_NV16, DRM_FORMAT_NV21, DRM_FORMAT_NV61, > + DRM_FORMAT_UYVY, DRM_FORMAT_VYUY, DRM_FORMAT_YUYV, DRM_FORMAT_YVYU, > + DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, DRM_FORMAT_YUV422, > +}; > + > static int gsc_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > + struct gsc_driverdata *driver_data; > + struct exynos_drm_ipp_formats *formats; > struct gsc_context *ctx; > struct resource *res; > - struct exynos_drm_ippdrv *ippdrv; > - int ret; > + int ret, i; > > ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); > if (!ctx) > return -ENOMEM; > > - if (dev->of_node) { > - ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, > - "samsung,sysreg"); > - if (IS_ERR(ctx->sysreg)) { > - dev_warn(dev, "failed to get system register.\n"); > - ctx->sysreg = NULL; > - } > + formats = devm_kzalloc(dev, sizeof(*formats) * > + (ARRAY_SIZE(gsc_formats) + 1), GFP_KERNEL); > + if (!formats) > + return -ENOMEM; > + > + > + driver_data = (struct gsc_driverdata *)of_device_get_match_data(dev); > + ctx->dev = dev; > + ctx->num_clocks = driver_data->num_clocks; > + ctx->clk_names = driver_data->clk_names; > + > + for (i = 0; i < ARRAY_SIZE(gsc_formats); i++) { > + formats[i].fourcc = gsc_formats[i]; > + formats[i].type = DRM_EXYNOS_IPP_FORMAT_SOURCE | > + DRM_EXYNOS_IPP_FORMAT_DESTINATION; > + formats[i].limits = driver_data->limits; > } > + ctx->formats = formats; > > /* clock control */ > - ctx->gsc_clk = devm_clk_get(dev, "gscl"); > - if (IS_ERR(ctx->gsc_clk)) { > - dev_err(dev, "failed to get gsc clock.\n"); > - return PTR_ERR(ctx->gsc_clk); > + for (i = 0; i < ctx->num_clocks; i++) { > + ctx->clocks[i] = devm_clk_get(dev, ctx->clk_names[i]); > + if (IS_ERR(ctx->clocks[i])) { > + dev_err(dev, "failed to get clock: %s\n", > + ctx->clk_names[i]); > + return PTR_ERR(ctx->clocks[i]); > + } > } > > /* resource memory */ > @@ -1699,8 +1279,8 @@ static int gsc_probe(struct platform_device *pdev) > } > > ctx->irq = res->start; > - ret = devm_request_threaded_irq(dev, ctx->irq, NULL, gsc_irq_handler, > - IRQF_ONESHOT, "drm_gsc", ctx); > + ret = devm_request_irq(dev, ctx->irq, gsc_irq_handler, 0, > + dev_name(dev), ctx); > if (ret < 0) { > dev_err(dev, "failed to request irq.\n"); > return ret; > @@ -1709,38 +1289,22 @@ static int gsc_probe(struct platform_device *pdev) > /* context initailization */ > ctx->id = pdev->id; > > - ippdrv = &ctx->ippdrv; > - ippdrv->dev = dev; > - ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &gsc_src_ops; > - ippdrv->ops[EXYNOS_DRM_OPS_DST] = &gsc_dst_ops; > - ippdrv->check_property = gsc_ippdrv_check_property; > - ippdrv->reset = gsc_ippdrv_reset; > - ippdrv->start = gsc_ippdrv_start; > - ippdrv->stop = gsc_ippdrv_stop; > - ret = gsc_init_prop_list(ippdrv); > - if (ret < 0) { > - dev_err(dev, "failed to init property list.\n"); > - return ret; > - } > - > - DRM_DEBUG_KMS("id[%d]ippdrv[%pK]\n", ctx->id, ippdrv); > - > - mutex_init(&ctx->lock); > platform_set_drvdata(pdev, ctx); > > + pm_runtime_use_autosuspend(dev); > + pm_runtime_set_autosuspend_delay(dev, GSC_AUTOSUSPEND_DELAY); > pm_runtime_enable(dev); > > - ret = exynos_drm_ippdrv_register(ippdrv); > - if (ret < 0) { > - dev_err(dev, "failed to register drm gsc device.\n"); > - goto err_ippdrv_register; > - } > + ret = component_add(dev, &gsc_component_ops); > + if (ret) > + goto err_pm_dis; > > dev_info(dev, "drm gsc registered successfully.\n"); > > return 0; > > -err_ippdrv_register: > +err_pm_dis: > + pm_runtime_dont_use_autosuspend(dev); > pm_runtime_disable(dev); > return ret; > } > @@ -1748,13 +1312,8 @@ static int gsc_probe(struct platform_device *pdev) > static int gsc_remove(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > - struct gsc_context *ctx = get_gsc_context(dev); > - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; > - > - exynos_drm_ippdrv_unregister(ippdrv); > - mutex_destroy(&ctx->lock); > > - pm_runtime_set_suspended(dev); > + pm_runtime_dont_use_autosuspend(dev); > pm_runtime_disable(dev); > > return 0; > @@ -1763,19 +1322,32 @@ static int gsc_remove(struct platform_device *pdev) > static int __maybe_unused gsc_runtime_suspend(struct device *dev) > { > struct gsc_context *ctx = get_gsc_context(dev); > + int i; > > DRM_DEBUG_KMS("id[%d]\n", ctx->id); > > - return gsc_clk_ctrl(ctx, false); > + for (i = ctx->num_clocks - 1; i >= 0; i--) > + clk_disable_unprepare(ctx->clocks[i]); > + > + return 0; > } > > static int __maybe_unused gsc_runtime_resume(struct device *dev) > { > struct gsc_context *ctx = get_gsc_context(dev); > + int i; > > DRM_DEBUG_KMS("id[%d]\n", ctx->id); > > - return gsc_clk_ctrl(ctx, true); > + for (i = 0; i < ctx->num_clocks; i++) { > + int ret = clk_prepare_enable(ctx->clocks[i]); > + if (ret) { > + while (--i > 0) > + clk_disable_unprepare(ctx->clocks[i]); > + return ret; > + } > + } > + return 0; > } > > static const struct dev_pm_ops gsc_pm_ops = { > @@ -1784,9 +1356,66 @@ static const struct dev_pm_ops gsc_pm_ops = { > SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) > }; > > +static const struct drm_exynos_ipp_limit gsc_5250_limits[] = { > + { IPP_SIZE_LIMIT(BUFFER, .h = {32, 4800, 16}, .v = {16, 3344, 16}) }, > + { IPP_SIZE_LIMIT(AREA, .h = {16, 4800, 2}, .v = {8, 3344, 2}) }, > + { IPP_SIZE_LIMIT(ROTATED, .h = {32, 2048}, .v = {16, 2048}) }, > + { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8}, > + .v = { (1 << 16) / 16, (1 << 16) * 8}) }, > + { } > +}; > + > +static const struct drm_exynos_ipp_limit gsc_5420_limits[] = { > + { IPP_SIZE_LIMIT(BUFFER, .h = {32, 4800, 16}, .v = {16, 3344, 16}) }, > + { IPP_SIZE_LIMIT(AREA, .h = {16, 4800, 2}, .v = {8, 3344, 2}) }, > + { IPP_SIZE_LIMIT(ROTATED, .h = {16, 2016}, .v = {8, 2016}) }, > + { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8}, > + .v = { (1 << 16) / 16, (1 << 16) * 8}) }, > + { } > +}; > + > +static const struct drm_exynos_ipp_limit gsc_5433_limits[] = { > + { IPP_SIZE_LIMIT(BUFFER, .h = {32, 8191, 16}, .v = {16, 8191, 16}) }, > + { IPP_SIZE_LIMIT(AREA, .h = {16, 4800, 2}, .v = {8, 3344, 2}) }, > + { IPP_SIZE_LIMIT(ROTATED, .h = {16, 2047}, .v = {8, 2047}) }, > + { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8}, > + .v = { (1 << 16) / 16, (1 << 16) * 8}) }, > + { } > +}; > + > +static struct gsc_driverdata gsc_exynos5250_drvdata = { > + .clk_names = {"gscl"}, > + .num_clocks = 1, > + .limits = gsc_5250_limits, > +}; > + > +static struct gsc_driverdata gsc_exynos5420_drvdata = { > + .clk_names = {"gscl"}, > + .num_clocks = 1, > + .limits = gsc_5420_limits, > +}; > + > +static struct gsc_driverdata gsc_exynos5433_drvdata = { > + .clk_names = {"pclk", "aclk", "aclk_xiu", "aclk_gsclbend"}, > + .num_clocks = 4, > + .limits = gsc_5433_limits, > +}; > + > static const struct of_device_id exynos_drm_gsc_of_match[] = { > - { .compatible = "samsung,exynos5-gsc" }, > - { }, > + { > + .compatible = "samsung,exynos5-gsc", > + .data = &gsc_exynos5250_drvdata, > + }, { > + .compatible = "samsung,exynos5250-gsc", > + .data = &gsc_exynos5250_drvdata, > + }, { > + .compatible = "samsung,exynos5420-gsc", > + .data = &gsc_exynos5420_drvdata, > + }, { > + .compatible = "samsung,exynos5433-gsc", > + .data = &gsc_exynos5433_drvdata, > + }, { > + }, > }; > MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match); > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.h b/drivers/gpu/drm/exynos/exynos_drm_gsc.h > deleted file mode 100644 > index 29ec1c5efcf2..000000000000 > --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.h > +++ /dev/null > @@ -1,24 +0,0 @@ > -/* > - * Copyright (c) 2012 Samsung Electronics Co., Ltd. > - * > - * Authors: > - * Eunchul Kim <chulspro.kim@samsung.com> > - * Jinyoung Jeon <jy0.jeon@samsung.com> > - * Sangmin Lee <lsmin.lee@samsung.com> > - * > - * This program is free software; you can redistribute it and/or modify it > - * under the terms of the GNU General Public License as published by the > - * Free Software Foundation; either version 2 of the License, or (at your > - * option) any later version. > - */ > - > -#ifndef _EXYNOS_DRM_GSC_H_ > -#define _EXYNOS_DRM_GSC_H_ > - > -/* > - * TODO > - * FIMD output interface notifier callback. > - * Mixer output interface notifier callback. > - */ > - > -#endif /* _EXYNOS_DRM_GSC_H_ */ > -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Oct 17, 2017 at 01:07:50PM +0200, Marek Szyprowski wrote: > From: Andrzej Pietrasiewicz <andrzej.p@samsung.com> > > Exynos Scaler is a hardware module, which processes graphic data fetched > from memory and transfers the resultant dato another memory buffer. > Graphics data can be up/down-scaled, rotated, flipped and converted color > space. Scaler hardware modules are a part of Exynos5420 and newer Exynos > SoCs. > > Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com> > Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> > --- > .../devicetree/bindings/gpu/samsung-scaler.txt | 26 + In the future, please split bindings to separate patches. Acked-by: Rob Herring <robh@kernel.org> > drivers/gpu/drm/exynos/Kconfig | 6 + > drivers/gpu/drm/exynos/Makefile | 1 + > drivers/gpu/drm/exynos/exynos_drm_drv.c | 3 + > drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 + > drivers/gpu/drm/exynos/exynos_drm_ipp.h | 4 + > drivers/gpu/drm/exynos/exynos_drm_scaler.c | 717 +++++++++++++++++++++ > drivers/gpu/drm/exynos/regs-scaler.h | 426 ++++++++++++ > 8 files changed, 1184 insertions(+) > create mode 100644 Documentation/devicetree/bindings/gpu/samsung-scaler.txt > create mode 100644 drivers/gpu/drm/exynos/exynos_drm_scaler.c > create mode 100644 drivers/gpu/drm/exynos/regs-scaler.h -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html