- Add S5PV210 FIMD variant support. - Add IPP v2 framework. . it is a rewritten version of the Exynos mem-to-mem image processing framework which supprts color space conversion, image up/down-scaling and rotation. This new version replaces existing userspace API with new easy-to-use and simple ones so we have already applied the use of these API to real user, Tizen Platform[1], and also makes existing Scaler, FIMC, GScaler and Rotator drivers to use IPP v2 core API. And below are patch lists we have applied to a real user, https://git.tizen.org/cgit/platform/adaptation/samsung_exynos/libtdm-exynos/log/?h=tizen&qt=grep&q=ipp https://git.tizen.org/cgit/platform/adaptation/samsung_exynos/libtdm-exynos/commit/?h=tizen&id=b59be207365d10efd489e6f71c8a045b558c44fe https://git.tizen.org/cgit/platform/kernel/linux-exynos/log/?h=tizen&qt=grep&q=ipp TDM(Tizen Display Manager) is a Display HAL for Tizen platform. Ps. Only real user using IPP API is Tizen. [1] https://www.tizen.org/ - Two cleanups . One is to just remove mode_set callback from MIPI-DSI driver because drm_display_mode data is already available from crtc atomic state. . And other is to just use new return type, vm_fault_t for page fault handler. Signed-off-by: Dave Airlie <airlied@redhat.com> # gpg: Signature made Mon 14 May 2018 14:23:53 AEST # gpg: using RSA key 573834890C4312B8 # gpg: Can't check signature: public key not found Link: https://patchwork.freedesktop.org/patch/msgid/1526276453-29879-1-git-send-email-inki.dae@samsung.com
Unified · Split
| @@ -0,0 +1,27 @@ | |||
| 1 | * Samsung Exynos Image Scaler | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible : value should be one of the following: | ||
| 5 | (a) "samsung,exynos5420-scaler" for Scaler IP in Exynos5420 | ||
| 6 | (b) "samsung,exynos5433-scaler" for Scaler IP in Exynos5433 | ||
| 7 | |||
| 8 | - reg : Physical base address of the IP registers and length of memory | ||
| 9 | mapped region. | ||
| 10 | |||
| 11 | - interrupts : Interrupt specifier for scaler interrupt, according to format | ||
| 12 | specific to interrupt parent. | ||
| 13 | |||
| 14 | - clocks : Clock specifier for scaler clock, according to generic clock | ||
| 15 | bindings. (See Documentation/devicetree/bindings/clock/exynos*.txt) | ||
| 16 | |||
| 17 | - clock-names : Names of clocks. For exynos scaler, it should be "mscl" | ||
| 18 | on 5420 and "pclk", "aclk" and "aclk_xiu" on 5433. | ||
| 19 | |||
| 20 | Example: | ||
| 21 | scaler@12800000 { | ||
| 22 | compatible = "samsung,exynos5420-scaler"; | ||
| 23 | reg = <0x12800000 0x1294>; | ||
| 24 | interrupts = <0 220 IRQ_TYPE_LEVEL_HIGH>; | ||
| 25 | clocks = <&clock CLK_MSCL0>; | ||
| 26 | clock-names = "mscl"; | ||
| 27 | }; | ||
| @@ -56,7 +56,9 @@ static int dumb_vga_get_modes(struct drm_connector *connector) · 55 unmodified lines | |||
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | drm_mode_connector_update_edid_property(connector, edid); | 58 | drm_mode_connector_update_edid_property(connector, edid); |
| 59 | return drm_add_edid_modes(connector, edid); | 59 | ret = drm_add_edid_modes(connector, edid); |
| 60 | kfree(edid); | ||
| 61 | return ret; | ||
| 60 | 62 | ||
| 61 | fallback: | 63 | fallback: |
| 62 | /* | 64 | /* |
| @@ -1,6 +1,6 @@ | |||
| 1 | config DRM_EXYNOS | 1 | config DRM_EXYNOS |
| 2 | tristate "DRM Support for Samsung SoC EXYNOS Series" | 2 | tristate "DRM Support for Samsung SoC EXYNOS Series" |
| 3 | depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM) | 3 | depends on OF && DRM && (ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || ARCH_MULTIPLATFORM) |
| 4 | select DRM_KMS_HELPER | 4 | select DRM_KMS_HELPER |
| 5 | select VIDEOMODE_HELPERS | 5 | select VIDEOMODE_HELPERS |
| 6 | select SND_SOC_HDMI_CODEC if SND_SOC | 6 | select SND_SOC_HDMI_CODEC if SND_SOC |
| @@ -95,21 +95,31 @@ config DRM_EXYNOS_G2D · 88 unmodified lines | |||
| 95 | help | 95 | help |
| 96 | Choose this option if you want to use Exynos G2D for DRM. | 96 | Choose this option if you want to use Exynos G2D for DRM. |
| 97 | 97 | ||
| 98 | config DRM_EXYNOS_IPP | ||
| 99 | bool | ||
| 100 | |||
| 98 | config DRM_EXYNOS_FIMC | 101 | config DRM_EXYNOS_FIMC |
| 99 | bool "FIMC" | 102 | bool "FIMC" |
| 100 | depends on BROKEN && MFD_SYSCON | 103 | select DRM_EXYNOS_IPP |
| 101 | help | 104 | help |
| 102 | Choose this option if you want to use Exynos FIMC for DRM. | 105 | Choose this option if you want to use Exynos FIMC for DRM. |
| 103 | 106 | ||
| 104 | config DRM_EXYNOS_ROTATOR | 107 | config DRM_EXYNOS_ROTATOR |
| 105 | bool "Rotator" | 108 | bool "Rotator" |
| 106 | depends on BROKEN | 109 | select DRM_EXYNOS_IPP |
| 107 | help | 110 | help |
| 108 | Choose this option if you want to use Exynos Rotator for DRM. | 111 | Choose this option if you want to use Exynos Rotator for DRM. |
| 109 | 112 | ||
| 113 | config DRM_EXYNOS_SCALER | ||
| 114 | bool "Scaler" | ||
| 115 | select DRM_EXYNOS_IPP | ||
| 116 | help | ||
| 117 | Choose this option if you want to use Exynos Scaler for DRM. | ||
| 118 | |||
| 110 | config DRM_EXYNOS_GSC | 119 | config DRM_EXYNOS_GSC |
| 111 | bool "GScaler" | 120 | bool "GScaler" |
| 112 | depends on BROKEN && ARCH_EXYNOS5 && VIDEO_SAMSUNG_EXYNOS_GSC=n | 121 | depends on VIDEO_SAMSUNG_EXYNOS_GSC=n |
| 122 | select DRM_EXYNOS_IPP | ||
| 113 | help | 123 | help |
| 114 | Choose this option if you want to use Exynos GSC for DRM. | 124 | Choose this option if you want to use Exynos GSC for DRM. |
| 115 | 125 | ||
| @@ -18,8 +18,10 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER) += exynos_mixer.o · 17 unmodified lines | |||
| 18 | exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o | 18 | exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o |
| 19 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o | 19 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o |
| 20 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o | 20 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o |
| 21 | exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o | ||
| 21 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o | 22 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o |
| 22 | exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o | 23 | exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o |
| 24 | exynosdrm-$(CONFIG_DRM_EXYNOS_SCALER) += exynos_drm_scaler.o | ||
| 23 | exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o | 25 | exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o |
| 24 | exynosdrm-$(CONFIG_DRM_EXYNOS_MIC) += exynos_drm_mic.o | 26 | exynosdrm-$(CONFIG_DRM_EXYNOS_MIC) += exynos_drm_mic.o |
| 25 | 27 | ||
| @@ -27,15 +27,23 @@ · 26 unmodified lines | |||
| 27 | #include "exynos_drm_fb.h" | 27 | #include "exynos_drm_fb.h" |
| 28 | #include "exynos_drm_gem.h" | 28 | #include "exynos_drm_gem.h" |
| 29 | #include "exynos_drm_plane.h" | 29 | #include "exynos_drm_plane.h" |
| 30 | #include "exynos_drm_ipp.h" | ||
| 30 | #include "exynos_drm_vidi.h" | 31 | #include "exynos_drm_vidi.h" |
| 31 | #include "exynos_drm_g2d.h" | 32 | #include "exynos_drm_g2d.h" |
| 32 | #include "exynos_drm_iommu.h" | 33 | #include "exynos_drm_iommu.h" |
| 33 | 34 | ||
| 34 | #define DRIVER_NAME "exynos" | 35 | #define DRIVER_NAME "exynos" |
| 35 | #define DRIVER_DESC "Samsung SoC DRM" | 36 | #define DRIVER_DESC "Samsung SoC DRM" |
| 36 | #define DRIVER_DATE "20110530" | 37 | #define DRIVER_DATE "20180330" |
| 38 | |||
| 39 | /* | ||
| 40 | * Interface history: | ||
| 41 | * | ||
| 42 | * 1.0 - Original version | ||
| 43 | * 1.1 - Upgrade IPP driver to version 2.0 | ||
| 44 | */ | ||
| 37 | #define DRIVER_MAJOR 1 | 45 | #define DRIVER_MAJOR 1 |
| 38 | #define DRIVER_MINOR 0 | 46 | #define DRIVER_MINOR 1 |
| 39 | 47 | ||
| 40 | static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) | 48 | static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) |
| 41 | { | 49 | { |
| @@ -88,6 +96,16 @@ static const struct drm_ioctl_desc exynos_ioctls[] = { · 46 unmodified lines | |||
| 88 | DRM_AUTH | DRM_RENDER_ALLOW), | 96 | DRM_AUTH | DRM_RENDER_ALLOW), |
| 89 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, | 97 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, |
| 90 | DRM_AUTH | DRM_RENDER_ALLOW), | 98 | DRM_AUTH | DRM_RENDER_ALLOW), |
| 99 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_RESOURCES, | ||
| 100 | exynos_drm_ipp_get_res_ioctl, | ||
| 101 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
| 102 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_CAPS, exynos_drm_ipp_get_caps_ioctl, | ||
| 103 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
| 104 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_LIMITS, | ||
| 105 | exynos_drm_ipp_get_limits_ioctl, | ||
| 106 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
| 107 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_COMMIT, exynos_drm_ipp_commit_ioctl, | ||
| 108 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
| 91 | }; | 109 | }; |
| 92 | 110 | ||
| 93 | static const struct file_operations exynos_drm_driver_fops = { | 111 | static const struct file_operations exynos_drm_driver_fops = { |
| @@ -184,6 +202,7 @@ struct exynos_drm_driver_info { · 90 unmodified lines | |||
| 184 | #define DRM_COMPONENT_DRIVER BIT(0) /* supports component framework */ | 202 | #define DRM_COMPONENT_DRIVER BIT(0) /* supports component framework */ |
| 185 | #define DRM_VIRTUAL_DEVICE BIT(1) /* create virtual platform device */ | 203 | #define DRM_VIRTUAL_DEVICE BIT(1) /* create virtual platform device */ |
| 186 | #define DRM_DMA_DEVICE BIT(2) /* can be used for dma allocations */ | 204 | #define DRM_DMA_DEVICE BIT(2) /* can be used for dma allocations */ |
| 205 | #define DRM_FIMC_DEVICE BIT(3) /* devices shared with V4L2 subsystem */ | ||
| 187 | 206 | ||
| 188 | #define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL) | 207 | #define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL) |
| 189 | 208 | ||
| @@ -223,10 +242,16 @@ static struct exynos_drm_driver_info exynos_drm_drivers[] = { · 33 unmodified lines | |||
| 223 | DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D), | 242 | DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D), |
| 224 | }, { | 243 | }, { |
| 225 | DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC), | 244 | DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC), |
| 245 | DRM_COMPONENT_DRIVER | DRM_FIMC_DEVICE, | ||
| 226 | }, { | 246 | }, { |
| 227 | DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR), | 247 | DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR), |
| 248 | DRM_COMPONENT_DRIVER | ||
| 249 | }, { | ||
| 250 | DRV_PTR(scaler_driver, CONFIG_DRM_EXYNOS_SCALER), | ||
| 251 | DRM_COMPONENT_DRIVER | ||
| 228 | }, { | 252 | }, { |
| 229 | DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), | 253 | DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), |
| 254 | DRM_COMPONENT_DRIVER | ||
| 230 | }, { | 255 | }, { |
| 231 | &exynos_drm_platform_driver, | 256 | &exynos_drm_platform_driver, |
| 232 | DRM_VIRTUAL_DEVICE | 257 | DRM_VIRTUAL_DEVICE |
| @@ -254,7 +279,11 @@ static struct component_match *exynos_drm_match_add(struct device *dev) · 21 unmodified lines | |||
| 254 | &info->driver->driver, | 279 | &info->driver->driver, |
| 255 | (void *)platform_bus_type.match))) { | 280 | (void *)platform_bus_type.match))) { |
| 256 | put_device(p); | 281 | put_device(p); |
| 257 | component_match_add(dev, &match, compare_dev, d); | 282 | |
| 283 | if (!(info->flags & DRM_FIMC_DEVICE) || | ||
| 284 | exynos_drm_check_fimc_device(d) == 0) | ||
| 285 | component_match_add(dev, &match, | ||
| 286 | compare_dev, d); | ||
| 258 | p = d; | 287 | p = d; |
| 259 | } | 288 | } |
| 260 | put_device(p); | 289 | put_device(p); |
| @@ -273,6 +273,15 @@ static inline int exynos_dpi_bind(struct drm_device *dev, · 272 unmodified lines | |||
| 273 | } | 273 | } |
| 274 | #endif | 274 | #endif |
| 275 | 275 | ||
| 276 | #ifdef CONFIG_DRM_EXYNOS_FIMC | ||
| 277 | int exynos_drm_check_fimc_device(struct device *dev); | ||
| 278 | #else | ||
| 279 | static inline int exynos_drm_check_fimc_device(struct device *dev) | ||
| 280 | { | ||
| 281 | return 0; | ||
| 282 | } | ||
| 283 | #endif | ||
| 284 | |||
| 276 | int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, | 285 | int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, |
| 277 | bool nonblock); | 286 | bool nonblock); |
| 278 | 287 | ||
| @@ -288,6 +297,7 @@ extern struct platform_driver vidi_driver; · 9 unmodified lines | |||
| 288 | extern struct platform_driver g2d_driver; | 297 | extern struct platform_driver g2d_driver; |
| 289 | extern struct platform_driver fimc_driver; | 298 | extern struct platform_driver fimc_driver; |
| 290 | extern struct platform_driver rotator_driver; | 299 | extern struct platform_driver rotator_driver; |
| 300 | extern struct platform_driver scaler_driver; | ||
| 291 | extern struct platform_driver gsc_driver; | 301 | extern struct platform_driver gsc_driver; |
| 292 | extern struct platform_driver mic_driver; | 302 | extern struct platform_driver mic_driver; |
| 293 | #endif | 303 | #endif |
| @@ -270,7 +270,6 @@ struct exynos_dsi { · 269 unmodified lines | |||
| 270 | u32 lanes; | 270 | u32 lanes; |
| 271 | u32 mode_flags; | 271 | u32 mode_flags; |
| 272 | u32 format; | 272 | u32 format; |
| 273 | struct videomode vm; | ||
| 274 | 273 | ||
| 275 | int state; | 274 | int state; |
| 276 | struct drm_property *brightness; | 275 | struct drm_property *brightness; |
| @@ -881,30 +880,30 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) · 604 unmodified lines | |||
| 881 | 880 | ||
| 882 | static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi) | 881 | static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi) |
| 883 | { | 882 | { |
| 884 | struct videomode *vm = &dsi->vm; | 883 | struct drm_display_mode *m = &dsi->encoder.crtc->state->adjusted_mode; |
| 885 | unsigned int num_bits_resol = dsi->driver_data->num_bits_resol; | 884 | unsigned int num_bits_resol = dsi->driver_data->num_bits_resol; |
| 886 | u32 reg; | 885 | u32 reg; |
| 887 | 886 | ||
| 888 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | 887 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { |
| 889 | reg = DSIM_CMD_ALLOW(0xf) | 888 | reg = DSIM_CMD_ALLOW(0xf) |
| 890 | | DSIM_STABLE_VFP(vm->vfront_porch) | 889 | | DSIM_STABLE_VFP(m->vsync_start - m->vdisplay) |
| 891 | | DSIM_MAIN_VBP(vm->vback_porch); | 890 | | DSIM_MAIN_VBP(m->vtotal - m->vsync_end); |
| 892 | exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg); | 891 | exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg); |
| 893 | 892 | ||
| 894 | reg = DSIM_MAIN_HFP(vm->hfront_porch) | 893 | reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay) |
| 895 | | DSIM_MAIN_HBP(vm->hback_porch); | 894 | | DSIM_MAIN_HBP(m->htotal - m->hsync_end); |
| 896 | exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg); | 895 | exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg); |
| 897 | 896 | ||
| 898 | reg = DSIM_MAIN_VSA(vm->vsync_len) | 897 | reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start) |
| 899 | | DSIM_MAIN_HSA(vm->hsync_len); | 898 | | DSIM_MAIN_HSA(m->hsync_end - m->hsync_start); |
| 900 | exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg); | 899 | exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg); |
| 901 | } | 900 | } |
| 902 | reg = DSIM_MAIN_HRESOL(vm->hactive, num_bits_resol) | | 901 | reg = DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) | |
| 903 | DSIM_MAIN_VRESOL(vm->vactive, num_bits_resol); | 902 | DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol); |
| 904 | 903 | ||
| 905 | exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg); | 904 | exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg); |
| 906 | 905 | ||
| 907 | dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive); | 906 | dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay); |
| 908 | } | 907 | } |
| 909 | 908 | ||
| 910 | static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable) | 909 | static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable) |
| @@ -1485,26 +1484,7 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder) · 574 unmodified lines | |||
| 1485 | return 0; | 1484 | return 0; |
| 1486 | } | 1485 | } |
| 1487 | 1486 | ||
| 1488 | static void exynos_dsi_mode_set(struct drm_encoder *encoder, | ||
| 1489 | struct drm_display_mode *mode, | ||
| 1490 | struct drm_display_mode *adjusted_mode) | ||
| 1491 | { | ||
| 1492 | struct exynos_dsi *dsi = encoder_to_dsi(encoder); | ||
| 1493 | struct videomode *vm = &dsi->vm; | ||
| 1494 | struct drm_display_mode *m = adjusted_mode; | ||
| 1495 | |||
| 1496 | vm->hactive = m->hdisplay; | ||
| 1497 | vm->vactive = m->vdisplay; | ||
| 1498 | vm->vfront_porch = m->vsync_start - m->vdisplay; | ||
| 1499 | vm->vback_porch = m->vtotal - m->vsync_end; | ||
| 1500 | vm->vsync_len = m->vsync_end - m->vsync_start; | ||
| 1501 | vm->hfront_porch = m->hsync_start - m->hdisplay; | ||
| 1502 | vm->hback_porch = m->htotal - m->hsync_end; | ||
| 1503 | vm->hsync_len = m->hsync_end - m->hsync_start; | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { | 1487 | static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { |
| 1507 | .mode_set = exynos_dsi_mode_set, | ||
| 1508 | .enable = exynos_dsi_enable, | 1488 | .enable = exynos_dsi_enable, |
| 1509 | .disable = exynos_dsi_disable, | 1489 | .disable = exynos_dsi_disable, |
| 1510 | }; | 1490 | }; |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 12 | * | 12 | * |
| 13 | */ | 13 | */ |
| 14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 15 | #include <linux/component.h> | ||
| 15 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
| 16 | #include <linux/mfd/syscon.h> | 17 | #include <linux/mfd/syscon.h> |
| 17 | #include <linux/regmap.h> | 18 | #include <linux/regmap.h> |
| @@ -24,8 +25,8 @@ · 6 unmodified lines | |||
| 24 | #include <drm/exynos_drm.h> | 25 | #include <drm/exynos_drm.h> |
| 25 | #include "regs-fimc.h" | 26 | #include "regs-fimc.h" |
| 26 | #include "exynos_drm_drv.h" | 27 | #include "exynos_drm_drv.h" |
| 28 | #include "exynos_drm_iommu.h" | ||
| 27 | #include "exynos_drm_ipp.h" | 29 | #include "exynos_drm_ipp.h" |
| 28 | #include "exynos_drm_fimc.h" | ||
| 29 | 30 | ||
| 30 | /* | 31 | /* |
| 31 | * FIMC stands for Fully Interactive Mobile Camera and | 32 | * FIMC stands for Fully Interactive Mobile Camera and |
| @@ -33,23 +34,6 @@ · 1 unmodified line | |||
| 33 | * input DMA reads image data from the memory. | 34 | * input DMA reads image data from the memory. |
| 34 | * output DMA writes image data to memory. | 35 | * output DMA writes image data to memory. |
| 35 | * FIMC supports image rotation and image effect functions. | 36 | * FIMC supports image rotation and image effect functions. |
| 36 | * | ||
| 37 | * M2M operation : supports crop/scale/rotation/csc so on. | ||
| 38 | * Memory ----> FIMC H/W ----> Memory. | ||
| 39 | * Writeback operation : supports cloned screen with FIMD. | ||
| 40 | * FIMD ----> FIMC H/W ----> Memory. | ||
| 41 | * Output operation : supports direct display using local path. | ||
| 42 | * Memory ----> FIMC H/W ----> FIMD. | ||
| 43 | */ | ||
| 44 | |||
| 45 | /* | ||
| 46 | * TODO | ||
| 47 | * 1. check suspend/resume api if needed. | ||
| 48 | * 2. need to check use case platform_device_id. | ||
| 49 | * 3. check src/dst size with, height. | ||
| 50 | * 4. added check_prepare api for right register. | ||
| 51 | * 5. need to add supported list in prop_list. | ||
| 52 | * 6. check prescaler/scaler optimization. | ||
| 53 | */ | 37 | */ |
| 54 | 38 | ||
| 55 | #define FIMC_MAX_DEVS 4 | 39 | #define FIMC_MAX_DEVS 4 |
| @@ -59,29 +43,19 @@ · 3 unmodified lines | |||
| 59 | #define FIMC_BUF_STOP 1 | 43 | #define FIMC_BUF_STOP 1 |
| 60 | #define FIMC_BUF_START 2 | 44 | #define FIMC_BUF_START 2 |
| 61 | #define FIMC_WIDTH_ITU_709 1280 | 45 | #define FIMC_WIDTH_ITU_709 1280 |
| 62 | #define FIMC_REFRESH_MAX 60 | 46 | #define FIMC_AUTOSUSPEND_DELAY 2000 |
| 63 | #define FIMC_REFRESH_MIN 12 | 47 | |
| 64 | #define FIMC_CROP_MAX 8192 | 48 | static unsigned int fimc_mask = 0xc; |
| 65 | #define FIMC_CROP_MIN 32 | 49 | module_param_named(fimc_devs, fimc_mask, uint, 0644); |
| 66 | #define FIMC_SCALE_MAX 4224 | 50 | MODULE_PARM_DESC(fimc_devs, "Alias mask for assigning FIMC devices to Exynos DRM"); |
| 67 | #define FIMC_SCALE_MIN 32 | ||
| 68 | 51 | ||
| 69 | #define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) | 52 | #define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) |
| 70 | #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ | ||
| 71 | struct fimc_context, ippdrv); | ||
| 72 | enum fimc_wb { | ||
| 73 | FIMC_WB_NONE, | ||
| 74 | FIMC_WB_A, | ||
| 75 | FIMC_WB_B, | ||
| 76 | }; | ||
| 77 | 53 | ||
| 78 | enum { | 54 | enum { |
| 79 | FIMC_CLK_LCLK, | 55 | FIMC_CLK_LCLK, |
| 80 | FIMC_CLK_GATE, | 56 | FIMC_CLK_GATE, |
| 81 | FIMC_CLK_WB_A, | 57 | FIMC_CLK_WB_A, |
| 82 | FIMC_CLK_WB_B, | 58 | FIMC_CLK_WB_B, |
| 83 | FIMC_CLK_MUX, | ||
| 84 | FIMC_CLK_PARENT, | ||
| 85 | FIMC_CLKS_MAX | 59 | FIMC_CLKS_MAX |
| 86 | }; | 60 | }; |
| 87 | 61 | ||
| @@ -90,12 +64,8 @@ static const char * const fimc_clock_names[] = { · 2 unmodified lines | |||
| 90 | [FIMC_CLK_GATE] = "fimc", | 64 | [FIMC_CLK_GATE] = "fimc", |
| 91 | [FIMC_CLK_WB_A] = "pxl_async0", | 65 | [FIMC_CLK_WB_A] = "pxl_async0", |
| 92 | [FIMC_CLK_WB_B] = "pxl_async1", | 66 | [FIMC_CLK_WB_B] = "pxl_async1", |
| 93 | [FIMC_CLK_MUX] = "mux", | ||
| 94 | [FIMC_CLK_PARENT] = "parent", | ||
| 95 | }; | 67 | }; |
| 96 | 68 | ||
| 97 | #define FIMC_DEFAULT_LCLK_FREQUENCY 133000000UL | ||
| 98 | |||
| 99 | /* | 69 | /* |
| 100 | * A structure of scaler. | 70 | * A structure of scaler. |
| 101 | * | 71 | * |
| @@ -107,7 +77,7 @@ static const char * const fimc_clock_names[] = { · 5 unmodified lines | |||
| 107 | * @vratio: vertical ratio. | 77 | * @vratio: vertical ratio. |
| 108 | */ | 78 | */ |
| 109 | struct fimc_scaler { | 79 | struct fimc_scaler { |
| 110 | bool range; | 80 | bool range; |
| 111 | bool bypass; | 81 | bool bypass; |
| 112 | bool up_h; | 82 | bool up_h; |
| 113 | bool up_v; | 83 | bool up_v; |
| @@ -115,57 +85,33 @@ struct fimc_scaler { · 1 unmodified line | |||
| 115 | u32 vratio; | 85 | u32 vratio; |
| 116 | }; | 86 | }; |
| 117 | 87 | ||
| 118 | /* | ||
| 119 | * A structure of scaler capability. | ||
| 120 | * | ||
| 121 | * find user manual table 43-1. | ||
| 122 | * @in_hori: scaler input horizontal size. | ||
| 123 | * @bypass: scaler bypass mode. | ||
| 124 | * @dst_h_wo_rot: target horizontal size without output rotation. | ||
| 125 | * @dst_h_rot: target horizontal size with output rotation. | ||
| 126 | * @rl_w_wo_rot: real width without input rotation. | ||
| 127 | * @rl_h_rot: real height without output rotation. | ||
| 128 | */ | ||
| 129 | struct fimc_capability { | ||
| 130 | /* scaler */ | ||
| 131 | u32 in_hori; | ||
| 132 | u32 bypass; | ||
| 133 | /* output rotator */ | ||
| 134 | u32 dst_h_wo_rot; | ||
| 135 | u32 dst_h_rot; | ||
| 136 | /* input rotator */ | ||
| 137 | u32 rl_w_wo_rot; | ||
| 138 | u32 rl_h_rot; | ||
| 139 | }; | ||
| 140 | |||
| 141 | /* | 88 | /* |
| 142 | * A structure of fimc context. | 89 | * A structure of fimc context. |
| 143 | * | 90 | * |
| 144 | * @ippdrv: prepare initialization using ippdrv. | ||
| 145 | * @regs_res: register resources. | 91 | * @regs_res: register resources. |
| 146 | * @regs: memory mapped io registers. | 92 | * @regs: memory mapped io registers. |
| 147 | * @lock: locking of operations. | 93 | * @lock: locking of operations. |
| 148 | * @clocks: fimc clocks. | 94 | * @clocks: fimc clocks. |
| 149 | * @clk_frequency: LCLK clock frequency. | ||
| 150 | * @sysreg: handle to SYSREG block regmap. | ||
| 151 | * @sc: scaler infomations. | 95 | * @sc: scaler infomations. |
| 152 | * @pol: porarity of writeback. | 96 | * @pol: porarity of writeback. |
| 153 | * @id: fimc id. | 97 | * @id: fimc id. |
| 154 | * @irq: irq number. | 98 | * @irq: irq number. |
| 155 | * @suspended: qos operations. | ||
| 156 | */ | 99 | */ |
| 157 | struct fimc_context { | 100 | struct fimc_context { |
| 158 | struct exynos_drm_ippdrv ippdrv; | 101 | struct exynos_drm_ipp ipp; |
| 102 | struct drm_device *drm_dev; | ||
| 103 | struct device *dev; | ||
| 104 | struct exynos_drm_ipp_task *task; | ||
| 105 | struct exynos_drm_ipp_formats *formats; | ||
| 106 | unsigned int num_formats; | ||
| 107 | |||
| 159 | struct resource *regs_res; | 108 | struct resource *regs_res; |
| 160 | void __iomem *regs; | 109 | void __iomem *regs; |
| 161 | spinlock_t lock; | 110 | spinlock_t lock; |
| 162 | struct clk *clocks[FIMC_CLKS_MAX]; | 111 | struct clk *clocks[FIMC_CLKS_MAX]; |
| 163 | u32 clk_frequency; | ||
| 164 | struct regmap *sysreg; | ||
| 165 | struct fimc_scaler sc; | 112 | struct fimc_scaler sc; |
| 166 | int id; | 113 | int id; |
| 167 | int irq; | 114 | int irq; |
| 168 | bool suspended; | ||
| 169 | }; | 115 | }; |
| 170 | 116 | ||
| 171 | static u32 fimc_read(struct fimc_context *ctx, u32 reg) | 117 | static u32 fimc_read(struct fimc_context *ctx, u32 reg) |
| @@ -217,19 +163,10 @@ static void fimc_sw_reset(struct fimc_context *ctx) · 45 unmodified lines | |||
| 217 | fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ); | 163 | fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ); |
| 218 | } | 164 | } |
| 219 | 165 | ||
| 220 | static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) | 166 | static void fimc_set_type_ctrl(struct fimc_context *ctx) |
| 221 | { | ||
| 222 | return regmap_update_bits(ctx->sysreg, SYSREG_CAMERA_BLK, | ||
| 223 | SYSREG_FIMD0WB_DEST_MASK, | ||
| 224 | ctx->id << SYSREG_FIMD0WB_DEST_SHIFT); | ||
| 225 | } | ||
| 226 | |||
| 227 | static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) | ||
| 228 | { | 167 | { |
| 229 | u32 cfg; | 168 | u32 cfg; |
| 230 | 169 | ||
| 231 | DRM_DEBUG_KMS("wb[%d]\n", wb); | ||
| 232 | |||
| 233 | cfg = fimc_read(ctx, EXYNOS_CIGCTRL); | 170 | cfg = fimc_read(ctx, EXYNOS_CIGCTRL); |
| 234 | cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | | 171 | cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | |
| 235 | EXYNOS_CIGCTRL_SELCAM_ITU_MASK | | 172 | EXYNOS_CIGCTRL_SELCAM_ITU_MASK | |
| @@ -238,23 +175,10 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) · 2 unmodified lines | |||
| 238 | EXYNOS_CIGCTRL_SELWB_CAMIF_MASK | | 175 | EXYNOS_CIGCTRL_SELWB_CAMIF_MASK | |
| 239 | EXYNOS_CIGCTRL_SELWRITEBACK_MASK); | 176 | EXYNOS_CIGCTRL_SELWRITEBACK_MASK); |
| 240 | 177 | ||
| 241 | switch (wb) { | 178 | cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A | |
| 242 | case FIMC_WB_A: | 179 | EXYNOS_CIGCTRL_SELWRITEBACK_A | |
| 243 | cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_A | | 180 | EXYNOS_CIGCTRL_SELCAM_MIPI_A | |
| 244 | EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); | 181 | EXYNOS_CIGCTRL_SELCAM_FIMC_ITU); |
| 245 | break; | ||
| 246 | case FIMC_WB_B: | ||
| 247 | cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_B | | ||
| 248 | EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); | ||
| 249 | break; | ||
| 250 | case FIMC_WB_NONE: | ||
| 251 | default: | ||
| 252 | cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A | | ||
| 253 | EXYNOS_CIGCTRL_SELWRITEBACK_A | | ||
| 254 | EXYNOS_CIGCTRL_SELCAM_MIPI_A | | ||
| 255 | EXYNOS_CIGCTRL_SELCAM_FIMC_ITU); | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | 182 | ||
| 259 | fimc_write(ctx, cfg, EXYNOS_CIGCTRL); | 183 | fimc_write(ctx, cfg, EXYNOS_CIGCTRL); |
| 260 | } | 184 | } |
| @@ -296,7 +220,6 @@ static void fimc_clear_irq(struct fimc_context *ctx) · 35 unmodified lines | |||
| 296 | 220 | ||
| 297 | static bool fimc_check_ovf(struct fimc_context *ctx) | 221 | static bool fimc_check_ovf(struct fimc_context *ctx) |
| 298 | { | 222 | { |
| 299 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 300 | u32 status, flag; | 223 | u32 status, flag; |
| 301 | 224 | ||
| 302 | status = fimc_read(ctx, EXYNOS_CISTATUS); | 225 | status = fimc_read(ctx, EXYNOS_CISTATUS); |
| @@ -310,7 +233,7 @@ static bool fimc_check_ovf(struct fimc_context *ctx) · 7 unmodified lines | |||
| 310 | EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | | 233 | EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | |
| 311 | EXYNOS_CIWDOFST_CLROVFICR); | 234 | EXYNOS_CIWDOFST_CLROVFICR); |
| 312 | 235 | ||
| 313 | dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n", | 236 | dev_err(ctx->dev, "occurred overflow at %d, status 0x%x.\n", |
| 314 | ctx->id, status); | 237 | ctx->id, status); |
| 315 | return true; | 238 | return true; |
| 316 | } | 239 | } |
| @@ -376,10 +299,8 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) · 59 unmodified lines | |||
| 376 | fimc_write(ctx, cfg, EXYNOS_CIOCTRL); | 299 | fimc_write(ctx, cfg, EXYNOS_CIOCTRL); |
| 377 | } | 300 | } |
| 378 | 301 | ||
| 379 | 302 | static void fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) | |
| 380 | static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) | ||
| 381 | { | 303 | { |
| 382 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 383 | u32 cfg; | 304 | u32 cfg; |
| 384 | 305 | ||
| 385 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); | 306 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); |
| @@ -392,12 +313,12 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) · 6 unmodified lines | |||
| 392 | case DRM_FORMAT_RGB565: | 313 | case DRM_FORMAT_RGB565: |
| 393 | cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; | 314 | cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; |
| 394 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); | 315 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); |
| 395 | return 0; | 316 | return; |
| 396 | case DRM_FORMAT_RGB888: | 317 | case DRM_FORMAT_RGB888: |
| 397 | case DRM_FORMAT_XRGB8888: | 318 | case DRM_FORMAT_XRGB8888: |
| 398 | cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; | 319 | cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; |
| 399 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); | 320 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); |
| 400 | return 0; | 321 | return; |
| 401 | default: | 322 | default: |
| 402 | /* bypass */ | 323 | /* bypass */ |
| 403 | break; | 324 | break; |
| @@ -438,20 +359,13 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) · 34 unmodified lines | |||
| 438 | cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR | | 359 | cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR | |
| 439 | EXYNOS_MSCTRL_C_INT_IN_2PLANE); | 360 | EXYNOS_MSCTRL_C_INT_IN_2PLANE); |
| 440 | break; | 361 | break; |
| 441 | default: | ||
| 442 | dev_err(ippdrv->dev, "invalid source yuv order 0x%x.\n", fmt); | ||
| 443 | return -EINVAL; | ||
| 444 | } | 362 | } |
| 445 | 363 | ||
| 446 | fimc_write(ctx, cfg, EXYNOS_MSCTRL); | 364 | fimc_write(ctx, cfg, EXYNOS_MSCTRL); |
| 447 | |||
| 448 | return 0; | ||
| 449 | } | 365 | } |
| 450 | 366 | ||
| 451 | static int fimc_src_set_fmt(struct device *dev, u32 fmt) | 367 | static void fimc_src_set_fmt(struct fimc_context *ctx, u32 fmt, bool tiled) |
| 452 | { | 368 | { |
| 453 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 454 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 455 | u32 cfg; | 369 | u32 cfg; |
| 456 | 370 | ||
| 457 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); | 371 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); |
| @@ -485,9 +399,6 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt) · 27 unmodified lines | |||
| 485 | case DRM_FORMAT_NV21: | 399 | case DRM_FORMAT_NV21: |
| 486 | cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; | 400 | cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; |
| 487 | break; | 401 | break; |
| 488 | default: | ||
| 489 | dev_err(ippdrv->dev, "invalid source format 0x%x.\n", fmt); | ||
| 490 | return -EINVAL; | ||
| 491 | } | 402 | } |
| 492 | 403 | ||
| 493 | fimc_write(ctx, cfg, EXYNOS_MSCTRL); | 404 | fimc_write(ctx, cfg, EXYNOS_MSCTRL); |
| @@ -495,22 +406,22 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt) · 1 unmodified line | |||
| 495 | cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM); | 406 | cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM); |
| 496 | cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; | 407 | cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; |
| 497 | 408 | ||
| 498 | cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; | 409 | if (tiled) |
| 410 | cfg |= EXYNOS_CIDMAPARAM_R_MODE_64X32; | ||
| 411 | else | ||
| 412 | cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; | ||
| 499 | 413 | ||
| 500 | fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM); | 414 | fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM); |
| 501 | 415 | ||
| 502 | return fimc_src_set_fmt_order(ctx, fmt); | 416 | fimc_src_set_fmt_order(ctx, fmt); |
| 503 | } | 417 | } |
| 504 | 418 | ||
| 505 | static int fimc_src_set_transf(struct device *dev, | 419 | static void fimc_src_set_transf(struct fimc_context *ctx, unsigned int rotation) |
| 506 | enum drm_exynos_degree degree, | ||
| 507 | enum drm_exynos_flip flip, bool *swap) | ||
| 508 | { | 420 | { |
| 509 | struct fimc_context *ctx = get_fimc_context(dev); | 421 | unsigned int degree = rotation & DRM_MODE_ROTATE_MASK; |
| 510 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 511 | u32 cfg1, cfg2; | 422 | u32 cfg1, cfg2; |
| 512 | 423 | ||
| 513 | DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); | 424 | DRM_DEBUG_KMS("rotation[%x]\n", rotation); |
| 514 | 425 | ||
| 515 | cfg1 = fimc_read(ctx, EXYNOS_MSCTRL); | 426 | cfg1 = fimc_read(ctx, EXYNOS_MSCTRL); |
| 516 | cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | | 427 | cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | |
| @@ -520,61 +431,56 @@ static int fimc_src_set_transf(struct device *dev, · 3 unmodified lines | |||
| 520 | cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; | 431 | cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; |
| 521 | 432 | ||
| 522 | switch (degree) { | 433 | switch (degree) { |
| 523 | case EXYNOS_DRM_DEGREE_0: | 434 | case DRM_MODE_ROTATE_0: |
| 524 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 435 | if (rotation & DRM_MODE_REFLECT_X) |
| 525 | cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; | 436 | cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; |
| 526 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 437 | if (rotation & DRM_MODE_REFLECT_Y) |
| 527 | cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; | 438 | cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; |
| 528 | break; | 439 | break; |
| 529 | case EXYNOS_DRM_DEGREE_90: | 440 | case DRM_MODE_ROTATE_90: |
| 530 | cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; | 441 | cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; |
| 531 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 442 | if (rotation & DRM_MODE_REFLECT_X) |
| 532 | cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; | 443 | cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; |
| 533 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 444 | if (rotation & DRM_MODE_REFLECT_Y) |
| 534 | cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; | 445 | cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; |
| 535 | break; | 446 | break; |
| 536 | case EXYNOS_DRM_DEGREE_180: | 447 | case DRM_MODE_ROTATE_180: |
| 537 | cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | | 448 | cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | |
| 538 | EXYNOS_MSCTRL_FLIP_Y_MIRROR); | 449 | EXYNOS_MSCTRL_FLIP_Y_MIRROR); |
| 539 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 450 | if (rotation & DRM_MODE_REFLECT_X) |
| 540 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; | 451 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; |
| 541 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 452 | if (rotation & DRM_MODE_REFLECT_Y) |
| 542 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; | 453 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; |
| 543 | break; | 454 | break; |
| 544 | case EXYNOS_DRM_DEGREE_270: | 455 | case DRM_MODE_ROTATE_270: |
| 545 | cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | | 456 | cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | |
| 546 | EXYNOS_MSCTRL_FLIP_Y_MIRROR); | 457 | EXYNOS_MSCTRL_FLIP_Y_MIRROR); |
| 547 | cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; | 458 | cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; |
| 548 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 459 | if (rotation & DRM_MODE_REFLECT_X) |
| 549 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; | 460 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; |
| 550 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 461 | if (rotation & DRM_MODE_REFLECT_Y) |
| 551 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; | 462 | cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; |
| 552 | break; | 463 | break; |
| 553 | default: | ||
| 554 | dev_err(ippdrv->dev, "invalid degree value %d.\n", degree); | ||
| 555 | return -EINVAL; | ||
| 556 | } | 464 | } |
| 557 | 465 | ||
| 558 | fimc_write(ctx, cfg1, EXYNOS_MSCTRL); | 466 | fimc_write(ctx, cfg1, EXYNOS_MSCTRL); |
| 559 | fimc_write(ctx, cfg2, EXYNOS_CITRGFMT); | 467 | fimc_write(ctx, cfg2, EXYNOS_CITRGFMT); |
| 560 | *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0; | ||
| 561 | |||
| 562 | return 0; | ||
| 563 | } | 468 | } |
| 564 | 469 | ||
| 565 | static int fimc_set_window(struct fimc_context *ctx, | 470 | static void fimc_set_window(struct fimc_context *ctx, |
| 566 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | 471 | struct exynos_drm_ipp_buffer *buf) |
| 567 | { | 472 | { |
| 568 | u32 cfg, h1, h2, v1, v2; | 473 | u32 cfg, h1, h2, v1, v2; |
| 569 | 474 | ||
| 570 | /* cropped image */ | 475 | /* cropped image */ |
| 571 | h1 = pos->x; | 476 | h1 = buf->rect.x; |
| 572 | h2 = sz->hsize - pos->w - pos->x; | 477 | h2 = buf->buf.width - buf->rect.w - buf->rect.x; |
| 573 | v1 = pos->y; | 478 | v1 = buf->rect.y; |
| 574 | v2 = sz->vsize - pos->h - pos->y; | 479 | v2 = buf->buf.height - buf->rect.h - buf->rect.y; |
| 575 | 480 | ||
| 576 | DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", | 481 | DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", |
| 577 | pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); | 482 | buf->rect.x, buf->rect.y, buf->rect.w, buf->rect.h, |
| 483 | buf->buf.width, buf->buf.height); | ||
| 578 | DRM_DEBUG_KMS("h1[%d]h2[%d]v1[%d]v2[%d]\n", h1, h2, v1, v2); | 484 | DRM_DEBUG_KMS("h1[%d]h2[%d]v1[%d]v2[%d]\n", h1, h2, v1, v2); |
| 579 | 485 | ||
| 580 | /* | 486 | /* |
| @@ -592,42 +498,30 @@ static int fimc_set_window(struct fimc_context *ctx, · 11 unmodified lines | |||
| 592 | cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | | 498 | cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | |
| 593 | EXYNOS_CIWDOFST2_WINVEROFST2(v2)); | 499 | EXYNOS_CIWDOFST2_WINVEROFST2(v2)); |
| 594 | fimc_write(ctx, cfg, EXYNOS_CIWDOFST2); | 500 | fimc_write(ctx, cfg, EXYNOS_CIWDOFST2); |
| 595 | |||
| 596 | return 0; | ||
| 597 | } | 501 | } |
| 598 | 502 | ||
| 599 | static int fimc_src_set_size(struct device *dev, int swap, | 503 | static void fimc_src_set_size(struct fimc_context *ctx, |
| 600 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | 504 | struct exynos_drm_ipp_buffer *buf) |
| 601 | { | 505 | { |
| 602 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 603 | struct drm_exynos_pos img_pos = *pos; | ||
| 604 | struct drm_exynos_sz img_sz = *sz; | ||
| 605 | u32 cfg; | 506 | u32 cfg; |
| 606 | 507 | ||
| 607 | DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n", | 508 | DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", buf->buf.width, buf->buf.height); |
| 608 | swap, sz->hsize, sz->vsize); | ||
| 609 | 509 | ||
| 610 | /* original size */ | 510 | /* original size */ |
| 611 | cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | | 511 | cfg = (EXYNOS_ORGISIZE_HORIZONTAL(buf->buf.width) | |
| 612 | EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize)); | 512 | EXYNOS_ORGISIZE_VERTICAL(buf->buf.height)); |
| 613 | 513 | ||
| 614 | fimc_write(ctx, cfg, EXYNOS_ORGISIZE); | 514 | fimc_write(ctx, cfg, EXYNOS_ORGISIZE); |
| 615 | 515 | ||
| 616 | DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); | 516 | DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", buf->rect.x, buf->rect.y, |
| 617 | 517 | buf->rect.w, buf->rect.h); | |
| 618 | if (swap) { | ||
| 619 | img_pos.w = pos->h; | ||
| 620 | img_pos.h = pos->w; | ||
| 621 | img_sz.hsize = sz->vsize; | ||
| 622 | img_sz.vsize = sz->hsize; | ||
| 623 | } | ||
| 624 | 518 | ||
| 625 | /* set input DMA image size */ | 519 | /* set input DMA image size */ |
| 626 | cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE); | 520 | cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE); |
| 627 | cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | | 521 | cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | |
| 628 | EXYNOS_CIREAL_ISIZE_WIDTH_MASK); | 522 | EXYNOS_CIREAL_ISIZE_WIDTH_MASK); |
| 629 | cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) | | 523 | cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(buf->rect.w) | |
| 630 | EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h)); | 524 | EXYNOS_CIREAL_ISIZE_HEIGHT(buf->rect.h)); |
| 631 | fimc_write(ctx, cfg, EXYNOS_CIREAL_ISIZE); | 525 | fimc_write(ctx, cfg, EXYNOS_CIREAL_ISIZE); |
| 632 | 526 | ||
| 633 | /* | 527 | /* |
| @@ -635,91 +529,34 @@ static int fimc_src_set_size(struct device *dev, int swap, · 1 unmodified line | |||
| 635 | * for now, we support only ITU601 8 bit mode | 529 | * for now, we support only ITU601 8 bit mode |
| 636 | */ | 530 | */ |
| 637 | cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | | 531 | cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | |
| 638 | EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) | | 532 | EXYNOS_CISRCFMT_SOURCEHSIZE(buf->buf.width) | |
| 639 | EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize)); | 533 | EXYNOS_CISRCFMT_SOURCEVSIZE(buf->buf.height)); |
| 640 | fimc_write(ctx, cfg, EXYNOS_CISRCFMT); | 534 | fimc_write(ctx, cfg, EXYNOS_CISRCFMT); |
| 641 | 535 | ||
| 642 | /* offset Y(RGB), Cb, Cr */ | 536 | /* offset Y(RGB), Cb, Cr */ |
| 643 | cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) | | 537 | cfg = (EXYNOS_CIIYOFF_HORIZONTAL(buf->rect.x) | |
| 644 | EXYNOS_CIIYOFF_VERTICAL(img_pos.y)); | 538 | EXYNOS_CIIYOFF_VERTICAL(buf->rect.y)); |
| 645 | fimc_write(ctx, cfg, EXYNOS_CIIYOFF); | 539 | fimc_write(ctx, cfg, EXYNOS_CIIYOFF); |
| 646 | cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) | | 540 | cfg = (EXYNOS_CIICBOFF_HORIZONTAL(buf->rect.x) | |
| 647 | EXYNOS_CIICBOFF_VERTICAL(img_pos.y)); | 541 | EXYNOS_CIICBOFF_VERTICAL(buf->rect.y)); |
| 648 | fimc_write(ctx, cfg, EXYNOS_CIICBOFF); | 542 | fimc_write(ctx, cfg, EXYNOS_CIICBOFF); |
| 649 | cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) | | 543 | cfg = (EXYNOS_CIICROFF_HORIZONTAL(buf->rect.x) | |
| 650 | EXYNOS_CIICROFF_VERTICAL(img_pos.y)); | 544 | EXYNOS_CIICROFF_VERTICAL(buf->rect.y)); |
| 651 | fimc_write(ctx, cfg, EXYNOS_CIICROFF); | 545 | fimc_write(ctx, cfg, EXYNOS_CIICROFF); |
| 652 | 546 | ||
| 653 | return fimc_set_window(ctx, &img_pos, &img_sz); | 547 | fimc_set_window(ctx, buf); |
| 654 | } | 548 | } |
| 655 | 549 | ||
| 656 | static int fimc_src_set_addr(struct device *dev, | 550 | static void fimc_src_set_addr(struct fimc_context *ctx, |
| 657 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | 551 | struct exynos_drm_ipp_buffer *buf) |
| 658 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 659 | { | 552 | { |
| 660 | struct fimc_context *ctx = get_fimc_context(dev); | 553 | fimc_write(ctx, buf->dma_addr[0], EXYNOS_CIIYSA(0)); |
| 661 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | 554 | fimc_write(ctx, buf->dma_addr[1], EXYNOS_CIICBSA(0)); |
| 662 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | 555 | fimc_write(ctx, buf->dma_addr[2], EXYNOS_CIICRSA(0)); |
| 663 | struct drm_exynos_ipp_property *property; | ||
| 664 | struct drm_exynos_ipp_config *config; | ||
| 665 | |||
| 666 | if (!c_node) { | ||
| 667 | DRM_ERROR("failed to get c_node.\n"); | ||
| 668 | return -EINVAL; | ||
| 669 | } | ||
| 670 | |||
| 671 | property = &c_node->property; | ||
| 672 | |||
| 673 | DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", | ||
| 674 | property->prop_id, buf_id, buf_type); | ||
| 675 | |||
| 676 | if (buf_id > FIMC_MAX_SRC) { | ||
| 677 | dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id); | ||
| 678 | return -ENOMEM; | ||
| 679 | } | ||
| 680 | |||
| 681 | /* address register set */ | ||
| 682 | switch (buf_type) { | ||
| 683 | case IPP_BUF_ENQUEUE: | ||
| 684 | config = &property->config[EXYNOS_DRM_OPS_SRC]; | ||
| 685 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y], | ||
| 686 | EXYNOS_CIIYSA0); | ||
| 687 | |||
| 688 | if (config->fmt == DRM_FORMAT_YVU420) { | ||
| 689 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 690 | EXYNOS_CIICBSA0); | ||
| 691 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 692 | EXYNOS_CIICRSA0); | ||
| 693 | } else { | ||
| 694 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 695 | EXYNOS_CIICBSA0); | ||
| 696 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 697 | EXYNOS_CIICRSA0); | ||
| 698 | } | ||
| 699 | break; | ||
| 700 | case IPP_BUF_DEQUEUE: | ||
| 701 | fimc_write(ctx, 0x0, EXYNOS_CIIYSA0); | ||
| 702 | fimc_write(ctx, 0x0, EXYNOS_CIICBSA0); | ||
| 703 | fimc_write(ctx, 0x0, EXYNOS_CIICRSA0); | ||
| 704 | break; | ||
| 705 | default: | ||
| 706 | /* bypass */ | ||
| 707 | break; | ||
| 708 | } | ||
| 709 | |||
| 710 | return 0; | ||
| 711 | } | 556 | } |
| 712 | 557 | ||
| 713 | static struct exynos_drm_ipp_ops fimc_src_ops = { | 558 | static void fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) |
| 714 | .set_fmt = fimc_src_set_fmt, | ||
| 715 | .set_transf = fimc_src_set_transf, | ||
| 716 | .set_size = fimc_src_set_size, | ||
| 717 | .set_addr = fimc_src_set_addr, | ||
| 718 | }; | ||
| 719 | |||
| 720 | static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) | ||
| 721 | { | 559 | { |
| 722 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 723 | u32 cfg; | 560 | u32 cfg; |
| 724 | 561 | ||
| 725 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); | 562 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); |
| @@ -732,11 +569,11 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) · 6 unmodified lines | |||
| 732 | case DRM_FORMAT_RGB565: | 569 | case DRM_FORMAT_RGB565: |
| 733 | cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; | 570 | cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; |
| 734 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); | 571 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); |
| 735 | return 0; | 572 | return; |
| 736 | case DRM_FORMAT_RGB888: | 573 | case DRM_FORMAT_RGB888: |
| 737 | cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; | 574 | cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; |
| 738 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); | 575 | fimc_write(ctx, cfg, EXYNOS_CISCCTRL); |
| 739 | return 0; | 576 | return; |
| 740 | case DRM_FORMAT_XRGB8888: | 577 | case DRM_FORMAT_XRGB8888: |
| 741 | cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | | 578 | cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | |
| 742 | EXYNOS_CISCCTRL_EXTRGB_EXTENSION); | 579 | EXYNOS_CISCCTRL_EXTRGB_EXTENSION); |
| @@ -784,20 +621,13 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) · 41 unmodified lines | |||
| 784 | cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR; | 621 | cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR; |
| 785 | cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; | 622 | cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; |
| 786 | break; | 623 | break; |
| 787 | default: | ||
| 788 | dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt); | ||
| 789 | return -EINVAL; | ||
| 790 | } | 624 | } |
| 791 | 625 | ||
| 792 | fimc_write(ctx, cfg, EXYNOS_CIOCTRL); | 626 | fimc_write(ctx, cfg, EXYNOS_CIOCTRL); |
| 793 | |||
| 794 | return 0; | ||
| 795 | } | 627 | } |
| 796 | 628 | ||
| 797 | static int fimc_dst_set_fmt(struct device *dev, u32 fmt) | 629 | static void fimc_dst_set_fmt(struct fimc_context *ctx, u32 fmt, bool tiled) |
| 798 | { | 630 | { |
| 799 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 800 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 801 | u32 cfg; | 631 | u32 cfg; |
| 802 | 632 | ||
| 803 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); | 633 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); |
| @@ -837,10 +667,6 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt) · 33 unmodified lines | |||
| 837 | case DRM_FORMAT_NV21: | 667 | case DRM_FORMAT_NV21: |
| 838 | cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420; | 668 | cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420; |
| 839 | break; | 669 | break; |
| 840 | default: | ||
| 841 | dev_err(ippdrv->dev, "invalid target format 0x%x.\n", | ||
| 842 | fmt); | ||
| 843 | return -EINVAL; | ||
| 844 | } | 670 | } |
| 845 | 671 | ||
| 846 | fimc_write(ctx, cfg, EXYNOS_CITRGFMT); | 672 | fimc_write(ctx, cfg, EXYNOS_CITRGFMT); |
| @@ -849,73 +675,67 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt) · 2 unmodified lines | |||
| 849 | cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM); | 675 | cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM); |
| 850 | cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; | 676 | cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; |
| 851 | 677 | ||
| 852 | cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; | 678 | if (tiled) |
| 679 | cfg |= EXYNOS_CIDMAPARAM_W_MODE_64X32; | ||
| 680 | else | ||
| 681 | cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; | ||
| 853 | 682 | ||
| 854 | fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM); | 683 | fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM); |
| 855 | 684 | ||
| 856 | return fimc_dst_set_fmt_order(ctx, fmt); | 685 | fimc_dst_set_fmt_order(ctx, fmt); |
| 857 | } | 686 | } |
| 858 | 687 | ||
| 859 | static int fimc_dst_set_transf(struct device *dev, | 688 | static void fimc_dst_set_transf(struct fimc_context *ctx, unsigned int rotation) |
| 860 | enum drm_exynos_degree degree, | ||
| 861 | enum drm_exynos_flip flip, bool *swap) | ||
| 862 | { | 689 | { |
| 863 | struct fimc_context *ctx = get_fimc_context(dev); | 690 | unsigned int degree = rotation & DRM_MODE_ROTATE_MASK; |
| 864 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 865 | u32 cfg; | 691 | u32 cfg; |
| 866 | 692 | ||
| 867 | DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); | 693 | DRM_DEBUG_KMS("rotation[0x%x]\n", rotation); |
| 868 | 694 | ||
| 869 | cfg = fimc_read(ctx, EXYNOS_CITRGFMT); | 695 | cfg = fimc_read(ctx, EXYNOS_CITRGFMT); |
| 870 | cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; | 696 | cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; |
| 871 | cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; | 697 | cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; |
| 872 | 698 | ||
| 873 | switch (degree) { | 699 | switch (degree) { |
| 874 | case EXYNOS_DRM_DEGREE_0: | 700 | case DRM_MODE_ROTATE_0: |
| 875 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 701 | if (rotation & DRM_MODE_REFLECT_X) |
| 876 | cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; | 702 | cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; |
| 877 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 703 | if (rotation & DRM_MODE_REFLECT_Y) |
| 878 | cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | 704 | cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; |
| 879 | break; | 705 | break; |
| 880 | case EXYNOS_DRM_DEGREE_90: | 706 | case DRM_MODE_ROTATE_90: |
| 881 | cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; | 707 | cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; |
| 882 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 708 | if (rotation & DRM_MODE_REFLECT_X) |
| 883 | cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; | 709 | cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; |
| 884 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 710 | if (rotation & DRM_MODE_REFLECT_Y) |
| 885 | cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | 711 | cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; |
| 886 | break; | 712 | break; |
| 887 | case EXYNOS_DRM_DEGREE_180: | 713 | case DRM_MODE_ROTATE_180: |
| 888 | cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR | | 714 | cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR | |
| 889 | EXYNOS_CITRGFMT_FLIP_Y_MIRROR); | 715 | EXYNOS_CITRGFMT_FLIP_Y_MIRROR); |
| 890 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 716 | if (rotation & DRM_MODE_REFLECT_X) |
| 891 | cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; | 717 | cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; |
| 892 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 718 | if (rotation & DRM_MODE_REFLECT_Y) |
| 893 | cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | 719 | cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; |
| 894 | break; | 720 | break; |
| 895 | case EXYNOS_DRM_DEGREE_270: | 721 | case DRM_MODE_ROTATE_270: |
| 896 | cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE | | 722 | cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE | |
| 897 | EXYNOS_CITRGFMT_FLIP_X_MIRROR | | 723 | EXYNOS_CITRGFMT_FLIP_X_MIRROR | |
| 898 | EXYNOS_CITRGFMT_FLIP_Y_MIRROR); | 724 | EXYNOS_CITRGFMT_FLIP_Y_MIRROR); |
| 899 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 725 | if (rotation & DRM_MODE_REFLECT_X) |
| 900 | cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; | 726 | cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; |
| 901 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 727 | if (rotation & DRM_MODE_REFLECT_Y) |
| 902 | cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; | 728 | cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; |
| 903 | break; | 729 | break; |
| 904 | default: | ||
| 905 | dev_err(ippdrv->dev, "invalid degree value %d.\n", degree); | ||
| 906 | return -EINVAL; | ||
| 907 | } | 730 | } |
| 908 | 731 | ||
| 909 | fimc_write(ctx, cfg, EXYNOS_CITRGFMT); | 732 | fimc_write(ctx, cfg, EXYNOS_CITRGFMT); |
| 910 | *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0; | ||
| 911 | |||
| 912 | return 0; | ||
| 913 | } | 733 | } |
| 914 | 734 | ||
| 915 | static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, | 735 | static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, |
| 916 | struct drm_exynos_pos *src, struct drm_exynos_pos *dst) | 736 | struct drm_exynos_ipp_task_rect *src, |
| 737 | struct drm_exynos_ipp_task_rect *dst) | ||
| 917 | { | 738 | { |
| 918 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 919 | u32 cfg, cfg_ext, shfactor; | 739 | u32 cfg, cfg_ext, shfactor; |
| 920 | u32 pre_dst_width, pre_dst_height; | 740 | u32 pre_dst_width, pre_dst_height; |
| 921 | u32 hfactor, vfactor; | 741 | u32 hfactor, vfactor; |
| @@ -942,13 +762,13 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, · 20 unmodified lines | |||
| 942 | /* fimc_ippdrv_check_property assures that dividers are not null */ | 762 | /* fimc_ippdrv_check_property assures that dividers are not null */ |
| 943 | hfactor = fls(src_w / dst_w / 2); | 763 | hfactor = fls(src_w / dst_w / 2); |
| 944 | if (hfactor > FIMC_SHFACTOR / 2) { | 764 | if (hfactor > FIMC_SHFACTOR / 2) { |
| 945 | dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); | 765 | dev_err(ctx->dev, "failed to get ratio horizontal.\n"); |
| 946 | return -EINVAL; | 766 | return -EINVAL; |
| 947 | } | 767 | } |
| 948 | 768 | ||
| 949 | vfactor = fls(src_h / dst_h / 2); | 769 | vfactor = fls(src_h / dst_h / 2); |
| 950 | if (vfactor > FIMC_SHFACTOR / 2) { | 770 | if (vfactor > FIMC_SHFACTOR / 2) { |
| 951 | dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); | 771 | dev_err(ctx->dev, "failed to get ratio vertical.\n"); |
| 952 | return -EINVAL; | 772 | return -EINVAL; |
| 953 | } | 773 | } |
| 954 | 774 | ||
| @@ -1019,83 +839,77 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) · 64 unmodified lines | |||
| 1019 | fimc_write(ctx, cfg_ext, EXYNOS_CIEXTEN); | 839 | fimc_write(ctx, cfg_ext, EXYNOS_CIEXTEN); |
| 1020 | } | 840 | } |
| 1021 | 841 | ||
| 1022 | static int fimc_dst_set_size(struct device *dev, int swap, | 842 | static void fimc_dst_set_size(struct fimc_context *ctx, |
| 1023 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | 843 | struct exynos_drm_ipp_buffer *buf) |
| 1024 | { | 844 | { |
| 1025 | struct fimc_context *ctx = get_fimc_context(dev); | 845 | u32 cfg, cfg_ext; |
| 1026 | struct drm_exynos_pos img_pos = *pos; | ||
| 1027 | struct drm_exynos_sz img_sz = *sz; | ||
| 1028 | u32 cfg; | ||
| 1029 | 846 | ||
| 1030 | DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n", | 847 | DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", buf->buf.width, buf->buf.height); |
| 1031 | swap, sz->hsize, sz->vsize); | ||
| 1032 | 848 | ||
| 1033 | /* original size */ | 849 | /* original size */ |
| 1034 | cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | | 850 | cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(buf->buf.width) | |
| 1035 | EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize)); | 851 | EXYNOS_ORGOSIZE_VERTICAL(buf->buf.height)); |
| 1036 | 852 | ||
| 1037 | fimc_write(ctx, cfg, EXYNOS_ORGOSIZE); | 853 | fimc_write(ctx, cfg, EXYNOS_ORGOSIZE); |
| 1038 | 854 | ||
| 1039 | DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); | 855 | DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", buf->rect.x, buf->rect.y, |
| 856 | buf->rect.w, buf->rect.h); | ||
| 1040 | 857 | ||
| 1041 | /* CSC ITU */ | 858 | /* CSC ITU */ |
| 1042 | cfg = fimc_read(ctx, EXYNOS_CIGCTRL); | 859 | cfg = fimc_read(ctx, EXYNOS_CIGCTRL); |
| 1043 | cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; | 860 | cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; |
| 1044 | 861 | ||
| 1045 | if (sz->hsize >= FIMC_WIDTH_ITU_709) | 862 | if (buf->buf.width >= FIMC_WIDTH_ITU_709) |
| 1046 | cfg |= EXYNOS_CIGCTRL_CSC_ITU709; | 863 | cfg |= EXYNOS_CIGCTRL_CSC_ITU709; |
| 1047 | else | 864 | else |
| 1048 | cfg |= EXYNOS_CIGCTRL_CSC_ITU601; | 865 | cfg |= EXYNOS_CIGCTRL_CSC_ITU601; |
| 1049 | 866 | ||
| 1050 | fimc_write(ctx, cfg, EXYNOS_CIGCTRL); | 867 | fimc_write(ctx, cfg, EXYNOS_CIGCTRL); |
| 1051 | 868 | ||
| 1052 | if (swap) { | 869 | cfg_ext = fimc_read(ctx, EXYNOS_CITRGFMT); |
| 1053 | img_pos.w = pos->h; | ||
| 1054 | img_pos.h = pos->w; | ||
| 1055 | img_sz.hsize = sz->vsize; | ||
| 1056 | img_sz.vsize = sz->hsize; | ||
| 1057 | } | ||
| 1058 | 870 | ||
| 1059 | /* target image size */ | 871 | /* target image size */ |
| 1060 | cfg = fimc_read(ctx, EXYNOS_CITRGFMT); | 872 | cfg = fimc_read(ctx, EXYNOS_CITRGFMT); |
| 1061 | cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | | 873 | cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | |
| 1062 | EXYNOS_CITRGFMT_TARGETV_MASK); | 874 | EXYNOS_CITRGFMT_TARGETV_MASK); |
| 1063 | cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) | | 875 | if (cfg_ext & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) |
| 1064 | EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h)); | 876 | cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(buf->rect.h) | |
| 877 | EXYNOS_CITRGFMT_TARGETVSIZE(buf->rect.w)); | ||
| 878 | else | ||
| 879 | cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(buf->rect.w) | | ||
| 880 | EXYNOS_CITRGFMT_TARGETVSIZE(buf->rect.h)); | ||
| 1065 | fimc_write(ctx, cfg, EXYNOS_CITRGFMT); | 881 | fimc_write(ctx, cfg, EXYNOS_CITRGFMT); |
| 1066 | 882 | ||
| 1067 | /* target area */ | 883 | /* target area */ |
| 1068 | cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h); | 884 | cfg = EXYNOS_CITAREA_TARGET_AREA(buf->rect.w * buf->rect.h); |
| 1069 | fimc_write(ctx, cfg, EXYNOS_CITAREA); | 885 | fimc_write(ctx, cfg, EXYNOS_CITAREA); |
| 1070 | 886 | ||
| 1071 | /* offset Y(RGB), Cb, Cr */ | 887 | /* offset Y(RGB), Cb, Cr */ |
| 1072 | cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) | | 888 | cfg = (EXYNOS_CIOYOFF_HORIZONTAL(buf->rect.x) | |
| 1073 | EXYNOS_CIOYOFF_VERTICAL(img_pos.y)); | 889 | EXYNOS_CIOYOFF_VERTICAL(buf->rect.y)); |
| 1074 | fimc_write(ctx, cfg, EXYNOS_CIOYOFF); | 890 | fimc_write(ctx, cfg, EXYNOS_CIOYOFF); |
| 1075 | cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) | | 891 | cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(buf->rect.x) | |
| 1076 | EXYNOS_CIOCBOFF_VERTICAL(img_pos.y)); | 892 | EXYNOS_CIOCBOFF_VERTICAL(buf->rect.y)); |
| 1077 | fimc_write(ctx, cfg, EXYNOS_CIOCBOFF); | 893 | fimc_write(ctx, cfg, EXYNOS_CIOCBOFF); |
| 1078 | cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) | | 894 | cfg = (EXYNOS_CIOCROFF_HORIZONTAL(buf->rect.x) | |
| 1079 | EXYNOS_CIOCROFF_VERTICAL(img_pos.y)); | 895 | EXYNOS_CIOCROFF_VERTICAL(buf->rect.y)); |
| 1080 | fimc_write(ctx, cfg, EXYNOS_CIOCROFF); | 896 | fimc_write(ctx, cfg, EXYNOS_CIOCROFF); |
| 1081 | |||
| 1082 | return 0; | ||
| 1083 | } | 897 | } |
| 1084 | 898 | ||
| 1085 | static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, | 899 | static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, |
| 1086 | enum drm_exynos_ipp_buf_type buf_type) | 900 | bool enqueue) |
| 1087 | { | 901 | { |
| 1088 | unsigned long flags; | 902 | unsigned long flags; |
| 1089 | u32 buf_num; | 903 | u32 buf_num; |
| 1090 | u32 cfg; | 904 | u32 cfg; |
| 1091 | 905 | ||
| 1092 | DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); | 906 | DRM_DEBUG_KMS("buf_id[%d]enqueu[%d]\n", buf_id, enqueue); |
| 1093 | 907 | ||
| 1094 | spin_lock_irqsave(&ctx->lock, flags); | 908 | spin_lock_irqsave(&ctx->lock, flags); |
| 1095 | 909 | ||
| 1096 | cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ); | 910 | cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ); |
| 1097 | 911 | ||
| 1098 | if (buf_type == IPP_BUF_ENQUEUE) | 912 | if (enqueue) |
| 1099 | cfg |= (1 << buf_id); | 913 | cfg |= (1 << buf_id); |
| 1100 | else | 914 | else |
| 1101 | cfg &= ~(1 << buf_id); | 915 | cfg &= ~(1 << buf_id); |
| @@ -1104,88 +918,29 @@ static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, · 2 unmodified lines | |||
| 1104 | 918 | ||
| 1105 | buf_num = hweight32(cfg); | 919 | buf_num = hweight32(cfg); |
| 1106 | 920 | ||
| 1107 | if (buf_type == IPP_BUF_ENQUEUE && buf_num >= FIMC_BUF_START) | 921 | if (enqueue && buf_num >= FIMC_BUF_START) |
| 1108 | fimc_mask_irq(ctx, true); | 922 | fimc_mask_irq(ctx, true); |
| 1109 | else if (buf_type == IPP_BUF_DEQUEUE && buf_num <= FIMC_BUF_STOP) | 923 | else if (!enqueue && buf_num <= FIMC_BUF_STOP) |
| 1110 | fimc_mask_irq(ctx, false); | 924 | fimc_mask_irq(ctx, false); |
| 1111 | 925 | ||
| 1112 | spin_unlock_irqrestore(&ctx->lock, flags); | 926 | spin_unlock_irqrestore(&ctx->lock, flags); |
| 1113 | } | 927 | } |
| 1114 | 928 | ||
| 1115 | static int fimc_dst_set_addr(struct device *dev, | 929 | static void fimc_dst_set_addr(struct fimc_context *ctx, |
| 1116 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | 930 | struct exynos_drm_ipp_buffer *buf) |
| 1117 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 1118 | { | 931 | { |
| 1119 | struct fimc_context *ctx = get_fimc_context(dev); | 932 | fimc_write(ctx, buf->dma_addr[0], EXYNOS_CIOYSA(0)); |
| 1120 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | 933 | fimc_write(ctx, buf->dma_addr[1], EXYNOS_CIOCBSA(0)); |
| 1121 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | 934 | fimc_write(ctx, buf->dma_addr[2], EXYNOS_CIOCRSA(0)); |
| 1122 | struct drm_exynos_ipp_property *property; | ||
| 1123 | struct drm_exynos_ipp_config *config; | ||
| 1124 | |||
| 1125 | if (!c_node) { | ||
| 1126 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1127 | return -EINVAL; | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | property = &c_node->property; | ||
| 1131 | |||
| 1132 | DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", | ||
| 1133 | property->prop_id, buf_id, buf_type); | ||
| 1134 | 935 | ||
| 1135 | if (buf_id > FIMC_MAX_DST) { | 936 | fimc_dst_set_buf_seq(ctx, 0, true); |
| 1136 | dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id); | ||
| 1137 | return -ENOMEM; | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | /* address register set */ | ||
| 1141 | switch (buf_type) { | ||
| 1142 | case IPP_BUF_ENQUEUE: | ||
| 1143 | config = &property->config[EXYNOS_DRM_OPS_DST]; | ||
| 1144 | |||
| 1145 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y], | ||
| 1146 | EXYNOS_CIOYSA(buf_id)); | ||
| 1147 | |||
| 1148 | if (config->fmt == DRM_FORMAT_YVU420) { | ||
| 1149 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 1150 | EXYNOS_CIOCBSA(buf_id)); | ||
| 1151 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 1152 | EXYNOS_CIOCRSA(buf_id)); | ||
| 1153 | } else { | ||
| 1154 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 1155 | EXYNOS_CIOCBSA(buf_id)); | ||
| 1156 | fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 1157 | EXYNOS_CIOCRSA(buf_id)); | ||
| 1158 | } | ||
| 1159 | break; | ||
| 1160 | case IPP_BUF_DEQUEUE: | ||
| 1161 | fimc_write(ctx, 0x0, EXYNOS_CIOYSA(buf_id)); | ||
| 1162 | fimc_write(ctx, 0x0, EXYNOS_CIOCBSA(buf_id)); | ||
| 1163 | fimc_write(ctx, 0x0, EXYNOS_CIOCRSA(buf_id)); | ||
| 1164 | break; | ||
| 1165 | default: | ||
| 1166 | /* bypass */ | ||
| 1167 | break; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | fimc_dst_set_buf_seq(ctx, buf_id, buf_type); | ||
| 1171 | |||
| 1172 | return 0; | ||
| 1173 | } | 937 | } |
| 1174 | 938 | ||
| 1175 | static struct exynos_drm_ipp_ops fimc_dst_ops = { | 939 | static void fimc_stop(struct fimc_context *ctx); |
| 1176 | .set_fmt = fimc_dst_set_fmt, | ||
| 1177 | .set_transf = fimc_dst_set_transf, | ||
| 1178 | .set_size = fimc_dst_set_size, | ||
| 1179 | .set_addr = fimc_dst_set_addr, | ||
| 1180 | }; | ||
| 1181 | 940 | ||
| 1182 | static irqreturn_t fimc_irq_handler(int irq, void *dev_id) | 941 | static irqreturn_t fimc_irq_handler(int irq, void *dev_id) |
| 1183 | { | 942 | { |
| 1184 | struct fimc_context *ctx = dev_id; | 943 | struct fimc_context *ctx = dev_id; |
| 1185 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1186 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | ||
| 1187 | struct drm_exynos_ipp_event_work *event_work = | ||
| 1188 | c_node->event_work; | ||
| 1189 | int buf_id; | 944 | int buf_id; |
| 1190 | 945 | ||
| 1191 | DRM_DEBUG_KMS("fimc id[%d]\n", ctx->id); | 946 | DRM_DEBUG_KMS("fimc id[%d]\n", ctx->id); |
| @@ -1203,170 +958,19 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id) · 11 unmodified lines | |||
| 1203 | 958 | ||
| 1204 | DRM_DEBUG_KMS("buf_id[%d]\n", buf_id); | 959 | DRM_DEBUG_KMS("buf_id[%d]\n", buf_id); |
| 1205 | 960 | ||
| 1206 | fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); | 961 | if (ctx->task) { |
| 1207 | 962 | struct exynos_drm_ipp_task *task = ctx->task; | |
| 1208 | event_work->ippdrv = ippdrv; | ||
| 1209 | event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id; | ||
| 1210 | queue_work(ippdrv->event_workq, &event_work->work); | ||
| 1211 | |||
| 1212 | return IRQ_HANDLED; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) | ||
| 1216 | { | ||
| 1217 | struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list; | ||
| 1218 | |||
| 1219 | prop_list->version = 1; | ||
| 1220 | prop_list->writeback = 1; | ||
| 1221 | prop_list->refresh_min = FIMC_REFRESH_MIN; | ||
| 1222 | prop_list->refresh_max = FIMC_REFRESH_MAX; | ||
| 1223 | prop_list->flip = (1 << EXYNOS_DRM_FLIP_NONE) | | ||
| 1224 | (1 << EXYNOS_DRM_FLIP_VERTICAL) | | ||
| 1225 | (1 << EXYNOS_DRM_FLIP_HORIZONTAL); | ||
| 1226 | prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | | ||
| 1227 | (1 << EXYNOS_DRM_DEGREE_90) | | ||
| 1228 | (1 << EXYNOS_DRM_DEGREE_180) | | ||
| 1229 | (1 << EXYNOS_DRM_DEGREE_270); | ||
| 1230 | prop_list->csc = 1; | ||
| 1231 | prop_list->crop = 1; | ||
| 1232 | prop_list->crop_max.hsize = FIMC_CROP_MAX; | ||
| 1233 | prop_list->crop_max.vsize = FIMC_CROP_MAX; | ||
| 1234 | prop_list->crop_min.hsize = FIMC_CROP_MIN; | ||
| 1235 | prop_list->crop_min.vsize = FIMC_CROP_MIN; | ||
| 1236 | prop_list->scale = 1; | ||
| 1237 | prop_list->scale_max.hsize = FIMC_SCALE_MAX; | ||
| 1238 | prop_list->scale_max.vsize = FIMC_SCALE_MAX; | ||
| 1239 | prop_list->scale_min.hsize = FIMC_SCALE_MIN; | ||
| 1240 | prop_list->scale_min.vsize = FIMC_SCALE_MIN; | ||
| 1241 | |||
| 1242 | return 0; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip) | ||
| 1246 | { | ||
| 1247 | switch (flip) { | ||
| 1248 | case EXYNOS_DRM_FLIP_NONE: | ||
| 1249 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 1250 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 1251 | case EXYNOS_DRM_FLIP_BOTH: | ||
| 1252 | return true; | ||
| 1253 | default: | ||
| 1254 | DRM_DEBUG_KMS("invalid flip\n"); | ||
| 1255 | return false; | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | static int fimc_ippdrv_check_property(struct device *dev, | ||
| 1260 | struct drm_exynos_ipp_property *property) | ||
| 1261 | { | ||
| 1262 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1263 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1264 | struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list; | ||
| 1265 | struct drm_exynos_ipp_config *config; | ||
| 1266 | struct drm_exynos_pos *pos; | ||
| 1267 | struct drm_exynos_sz *sz; | ||
| 1268 | bool swap; | ||
| 1269 | int i; | ||
| 1270 | |||
| 1271 | for_each_ipp_ops(i) { | ||
| 1272 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1273 | (property->cmd == IPP_CMD_WB)) | ||
| 1274 | continue; | ||
| 1275 | |||
| 1276 | config = &property->config[i]; | ||
| 1277 | pos = &config->pos; | ||
| 1278 | sz = &config->sz; | ||
| 1279 | |||
| 1280 | /* check for flip */ | ||
| 1281 | if (!fimc_check_drm_flip(config->flip)) { | ||
| 1282 | DRM_ERROR("invalid flip.\n"); | ||
| 1283 | goto err_property; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | /* check for degree */ | ||
| 1287 | switch (config->degree) { | ||
| 1288 | case EXYNOS_DRM_DEGREE_90: | ||
| 1289 | case EXYNOS_DRM_DEGREE_270: | ||
| 1290 | swap = true; | ||
| 1291 | break; | ||
| 1292 | case EXYNOS_DRM_DEGREE_0: | ||
| 1293 | case EXYNOS_DRM_DEGREE_180: | ||
| 1294 | swap = false; | ||
| 1295 | break; | ||
| 1296 | default: | ||
| 1297 | DRM_ERROR("invalid degree.\n"); | ||
| 1298 | goto err_property; | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | /* check for buffer bound */ | ||
| 1302 | if ((pos->x + pos->w > sz->hsize) || | ||
| 1303 | (pos->y + pos->h > sz->vsize)) { | ||
| 1304 | DRM_ERROR("out of buf bound.\n"); | ||
| 1305 | goto err_property; | ||
| 1306 | } | ||
| 1307 | 963 | ||
| 1308 | /* check for crop */ | 964 | ctx->task = NULL; |
| 1309 | if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { | 965 | pm_runtime_mark_last_busy(ctx->dev); |
| 1310 | if (swap) { | 966 | pm_runtime_put_autosuspend(ctx->dev); |
| 1311 | if ((pos->h < pp->crop_min.hsize) || | 967 | exynos_drm_ipp_task_done(task, 0); |
| 1312 | (sz->vsize > pp->crop_max.hsize) || | ||
| 1313 | (pos->w < pp->crop_min.vsize) || | ||
| 1314 | (sz->hsize > pp->crop_max.vsize)) { | ||
| 1315 | DRM_ERROR("out of crop size.\n"); | ||
| 1316 | goto err_property; | ||
| 1317 | } | ||
| 1318 | } else { | ||
| 1319 | if ((pos->w < pp->crop_min.hsize) || | ||
| 1320 | (sz->hsize > pp->crop_max.hsize) || | ||
| 1321 | (pos->h < pp->crop_min.vsize) || | ||
| 1322 | (sz->vsize > pp->crop_max.vsize)) { | ||
| 1323 | DRM_ERROR("out of crop size.\n"); | ||
| 1324 | goto err_property; | ||
| 1325 | } | ||
| 1326 | } | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | /* check for scale */ | ||
| 1330 | if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { | ||
| 1331 | if (swap) { | ||
| 1332 | if ((pos->h < pp->scale_min.hsize) || | ||
| 1333 | (sz->vsize > pp->scale_max.hsize) || | ||
| 1334 | (pos->w < pp->scale_min.vsize) || | ||
| 1335 | (sz->hsize > pp->scale_max.vsize)) { | ||
| 1336 | DRM_ERROR("out of scale size.\n"); | ||
| 1337 | goto err_property; | ||
| 1338 | } | ||
| 1339 | } else { | ||
| 1340 | if ((pos->w < pp->scale_min.hsize) || | ||
| 1341 | (sz->hsize > pp->scale_max.hsize) || | ||
| 1342 | (pos->h < pp->scale_min.vsize) || | ||
| 1343 | (sz->vsize > pp->scale_max.vsize)) { | ||
| 1344 | DRM_ERROR("out of scale size.\n"); | ||
| 1345 | goto err_property; | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | } | ||
| 1349 | } | 968 | } |
| 1350 | 969 | ||
| 1351 | return 0; | 970 | fimc_dst_set_buf_seq(ctx, buf_id, false); |
| 971 | fimc_stop(ctx); | ||
| 1352 | 972 | ||
| 1353 | err_property: | 973 | return IRQ_HANDLED; |
| 1354 | for_each_ipp_ops(i) { | ||
| 1355 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1356 | (property->cmd == IPP_CMD_WB)) | ||
| 1357 | continue; | ||
| 1358 | |||
| 1359 | config = &property->config[i]; | ||
| 1360 | pos = &config->pos; | ||
| 1361 | sz = &config->sz; | ||
| 1362 | |||
| 1363 | DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", | ||
| 1364 | i ? "dst" : "src", config->flip, config->degree, | ||
| 1365 | pos->x, pos->y, pos->w, pos->h, | ||
| 1366 | sz->hsize, sz->vsize); | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | return -EINVAL; | ||
| 1370 | } | 974 | } |
| 1371 | 975 | ||
| 1372 | static void fimc_clear_addr(struct fimc_context *ctx) | 976 | static void fimc_clear_addr(struct fimc_context *ctx) |
| @@ -1386,10 +990,8 @@ static void fimc_clear_addr(struct fimc_context *ctx) · 13 unmodified lines | |||
| 1386 | } | 990 | } |
| 1387 | } | 991 | } |
| 1388 | 992 | ||
| 1389 | static int fimc_ippdrv_reset(struct device *dev) | 993 | static void fimc_reset(struct fimc_context *ctx) |
| 1390 | { | 994 | { |
| 1391 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1392 | |||
| 1393 | /* reset h/w block */ | 995 | /* reset h/w block */ |
| 1394 | fimc_sw_reset(ctx); | 996 | fimc_sw_reset(ctx); |
| 1395 | 997 | ||
| @@ -1397,82 +999,26 @@ static int fimc_ippdrv_reset(struct device *dev) · 1 unmodified line | |||
| 1397 | memset(&ctx->sc, 0x0, sizeof(ctx->sc)); | 999 | memset(&ctx->sc, 0x0, sizeof(ctx->sc)); |
| 1398 | 1000 | ||
| 1399 | fimc_clear_addr(ctx); | 1001 | fimc_clear_addr(ctx); |
| 1400 | |||
| 1401 | return 0; | ||
| 1402 | } | 1002 | } |
| 1403 | 1003 | ||
| 1404 | static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) | 1004 | static void fimc_start(struct fimc_context *ctx) |
| 1405 | { | 1005 | { |
| 1406 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1407 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1408 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | ||
| 1409 | struct drm_exynos_ipp_property *property; | ||
| 1410 | struct drm_exynos_ipp_config *config; | ||
| 1411 | struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; | ||
| 1412 | struct drm_exynos_ipp_set_wb set_wb; | ||
| 1413 | int ret, i; | ||
| 1414 | u32 cfg0, cfg1; | 1006 | u32 cfg0, cfg1; |
| 1415 | 1007 | ||
| 1416 | DRM_DEBUG_KMS("cmd[%d]\n", cmd); | ||
| 1417 | |||
| 1418 | if (!c_node) { | ||
| 1419 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1420 | return -EINVAL; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | property = &c_node->property; | ||
| 1424 | |||
| 1425 | fimc_mask_irq(ctx, true); | 1008 | fimc_mask_irq(ctx, true); |
| 1426 | 1009 | ||
| 1427 | for_each_ipp_ops(i) { | 1010 | /* If set true, we can save jpeg about screen */ |
| 1428 | config = &property->config[i]; | ||
| 1429 | img_pos[i] = config->pos; | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | ret = fimc_set_prescaler(ctx, &ctx->sc, | ||
| 1433 | &img_pos[EXYNOS_DRM_OPS_SRC], | ||
| 1434 | &img_pos[EXYNOS_DRM_OPS_DST]); | ||
| 1435 | if (ret) { | ||
| 1436 | dev_err(dev, "failed to set prescaler.\n"); | ||
| 1437 | return ret; | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | /* If set ture, we can save jpeg about screen */ | ||
| 1441 | fimc_handle_jpeg(ctx, false); | 1011 | fimc_handle_jpeg(ctx, false); |
| 1442 | fimc_set_scaler(ctx, &ctx->sc); | 1012 | fimc_set_scaler(ctx, &ctx->sc); |
| 1443 | 1013 | ||
| 1444 | switch (cmd) { | 1014 | fimc_set_type_ctrl(ctx); |
| 1445 | case IPP_CMD_M2M: | 1015 | fimc_handle_lastend(ctx, false); |
| 1446 | fimc_set_type_ctrl(ctx, FIMC_WB_NONE); | ||
| 1447 | fimc_handle_lastend(ctx, false); | ||
| 1448 | |||
| 1449 | /* setup dma */ | ||
| 1450 | cfg0 = fimc_read(ctx, EXYNOS_MSCTRL); | ||
| 1451 | cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; | ||
| 1452 | cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; | ||
| 1453 | fimc_write(ctx, cfg0, EXYNOS_MSCTRL); | ||
| 1454 | break; | ||
| 1455 | case IPP_CMD_WB: | ||
| 1456 | fimc_set_type_ctrl(ctx, FIMC_WB_A); | ||
| 1457 | fimc_handle_lastend(ctx, true); | ||
| 1458 | |||
| 1459 | /* setup FIMD */ | ||
| 1460 | ret = fimc_set_camblk_fimd0_wb(ctx); | ||
| 1461 | if (ret < 0) { | ||
| 1462 | dev_err(dev, "camblk setup failed.\n"); | ||
| 1463 | return ret; | ||
| 1464 | } | ||
| 1465 | 1016 | ||
| 1466 | set_wb.enable = 1; | 1017 | /* setup dma */ |
| 1467 | set_wb.refresh = property->refresh_rate; | 1018 | cfg0 = fimc_read(ctx, EXYNOS_MSCTRL); |
| 1468 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | 1019 | cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; |
| 1469 | break; | 1020 | cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; |
| 1470 | case IPP_CMD_OUTPUT: | 1021 | fimc_write(ctx, cfg0, EXYNOS_MSCTRL); |
| 1471 | default: | ||
| 1472 | ret = -EINVAL; | ||
| 1473 | dev_err(dev, "invalid operations.\n"); | ||
| 1474 | return ret; | ||
| 1475 | } | ||
| 1476 | 1022 | ||
| 1477 | /* Reset status */ | 1023 | /* Reset status */ |
| 1478 | fimc_write(ctx, 0x0, EXYNOS_CISTATUS); | 1024 | fimc_write(ctx, 0x0, EXYNOS_CISTATUS); |
| @@ -1498,36 +1044,18 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) · 19 unmodified lines | |||
| 1498 | 1044 | ||
| 1499 | fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK); | 1045 | fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK); |
| 1500 | 1046 | ||
| 1501 | if (cmd == IPP_CMD_M2M) | 1047 | fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID); |
| 1502 | fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID); | ||
| 1503 | |||
| 1504 | return 0; | ||
| 1505 | } | 1048 | } |
| 1506 | 1049 | ||
| 1507 | static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) | 1050 | static void fimc_stop(struct fimc_context *ctx) |
| 1508 | { | 1051 | { |
| 1509 | struct fimc_context *ctx = get_fimc_context(dev); | ||
| 1510 | struct drm_exynos_ipp_set_wb set_wb = {0, 0}; | ||
| 1511 | u32 cfg; | 1052 | u32 cfg; |
| 1512 | 1053 | ||
| 1513 | DRM_DEBUG_KMS("cmd[%d]\n", cmd); | 1054 | /* Source clear */ |
| 1514 | 1055 | cfg = fimc_read(ctx, EXYNOS_MSCTRL); | |
| 1515 | switch (cmd) { | 1056 | cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; |
| 1516 | case IPP_CMD_M2M: | 1057 | cfg &= ~EXYNOS_MSCTRL_ENVID; |
| 1517 | /* Source clear */ | 1058 | fimc_write(ctx, cfg, EXYNOS_MSCTRL); |
| 1518 | cfg = fimc_read(ctx, EXYNOS_MSCTRL); | ||
| 1519 | cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; | ||
| 1520 | cfg &= ~EXYNOS_MSCTRL_ENVID; | ||
| 1521 | fimc_write(ctx, cfg, EXYNOS_MSCTRL); | ||
| 1522 | break; | ||
| 1523 | case IPP_CMD_WB: | ||
| 1524 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | ||
| 1525 | break; | ||
| 1526 | case IPP_CMD_OUTPUT: | ||
| 1527 | default: | ||
| 1528 | dev_err(dev, "invalid operations.\n"); | ||
| 1529 | break; | ||
| 1530 | } | ||
| 1531 | 1059 | ||
| 1532 | fimc_mask_irq(ctx, false); | 1060 | fimc_mask_irq(ctx, false); |
| 1533 | 1061 | ||
| @@ -1545,6 +1073,87 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) · 11 unmodified lines | |||
| 1545 | fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE); | 1073 | fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE); |
| 1546 | } | 1074 | } |
| 1547 | 1075 | ||
| 1076 | static int fimc_commit(struct exynos_drm_ipp *ipp, | ||
| 1077 | struct exynos_drm_ipp_task *task) | ||
| 1078 | { | ||
| 1079 | struct fimc_context *ctx = | ||
| 1080 | container_of(ipp, struct fimc_context, ipp); | ||
| 1081 | |||
| 1082 | pm_runtime_get_sync(ctx->dev); | ||
| 1083 | ctx->task = task; | ||
| 1084 | |||
| 1085 | fimc_src_set_fmt(ctx, task->src.buf.fourcc, task->src.buf.modifier); | ||
| 1086 | fimc_src_set_size(ctx, &task->src); | ||
| 1087 | fimc_src_set_transf(ctx, DRM_MODE_ROTATE_0); | ||
| 1088 | fimc_src_set_addr(ctx, &task->src); | ||
| 1089 | fimc_dst_set_fmt(ctx, task->dst.buf.fourcc, task->dst.buf.modifier); | ||
| 1090 | fimc_dst_set_transf(ctx, task->transform.rotation); | ||
| 1091 | fimc_dst_set_size(ctx, &task->dst); | ||
| 1092 | fimc_dst_set_addr(ctx, &task->dst); | ||
| 1093 | fimc_set_prescaler(ctx, &ctx->sc, &task->src.rect, &task->dst.rect); | ||
| 1094 | fimc_start(ctx); | ||
| 1095 | |||
| 1096 | return 0; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | static void fimc_abort(struct exynos_drm_ipp *ipp, | ||
| 1100 | struct exynos_drm_ipp_task *task) | ||
| 1101 | { | ||
| 1102 | struct fimc_context *ctx = | ||
| 1103 | container_of(ipp, struct fimc_context, ipp); | ||
| 1104 | |||
| 1105 | fimc_reset(ctx); | ||
| 1106 | |||
| 1107 | if (ctx->task) { | ||
| 1108 | struct exynos_drm_ipp_task *task = ctx->task; | ||
| 1109 | |||
| 1110 | ctx->task = NULL; | ||
| 1111 | pm_runtime_mark_last_busy(ctx->dev); | ||
| 1112 | pm_runtime_put_autosuspend(ctx->dev); | ||
| 1113 | exynos_drm_ipp_task_done(task, -EIO); | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | static struct exynos_drm_ipp_funcs ipp_funcs = { | ||
| 1118 | .commit = fimc_commit, | ||
| 1119 | .abort = fimc_abort, | ||
| 1120 | }; | ||
| 1121 | |||
| 1122 | static int fimc_bind(struct device *dev, struct device *master, void *data) | ||
| 1123 | { | ||
| 1124 | struct fimc_context *ctx = dev_get_drvdata(dev); | ||
| 1125 | struct drm_device *drm_dev = data; | ||
| 1126 | struct exynos_drm_ipp *ipp = &ctx->ipp; | ||
| 1127 | |||
| 1128 | ctx->drm_dev = drm_dev; | ||
| 1129 | drm_iommu_attach_device(drm_dev, dev); | ||
| 1130 | |||
| 1131 | exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs, | ||
| 1132 | DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE | | ||
| 1133 | DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT, | ||
| 1134 | ctx->formats, ctx->num_formats, "fimc"); | ||
| 1135 | |||
| 1136 | dev_info(dev, "The exynos fimc has been probed successfully\n"); | ||
| 1137 | |||
| 1138 | return 0; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | static void fimc_unbind(struct device *dev, struct device *master, | ||
| 1142 | void *data) | ||
| 1143 | { | ||
| 1144 | struct fimc_context *ctx = dev_get_drvdata(dev); | ||
| 1145 | struct drm_device *drm_dev = data; | ||
| 1146 | struct exynos_drm_ipp *ipp = &ctx->ipp; | ||
| 1147 | |||
| 1148 | exynos_drm_ipp_unregister(drm_dev, ipp); | ||
| 1149 | drm_iommu_detach_device(drm_dev, dev); | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | static const struct component_ops fimc_component_ops = { | ||
| 1153 | .bind = fimc_bind, | ||
| 1154 | .unbind = fimc_unbind, | ||
| 1155 | }; | ||
| 1156 | |||
| 1548 | static void fimc_put_clocks(struct fimc_context *ctx) | 1157 | static void fimc_put_clocks(struct fimc_context *ctx) |
| 1549 | { | 1158 | { |
| 1550 | int i; | 1159 | int i; |
| @@ -1559,7 +1168,7 @@ static void fimc_put_clocks(struct fimc_context *ctx) · 8 unmodified lines | |||
| 1559 | 1168 | ||
| 1560 | static int fimc_setup_clocks(struct fimc_context *ctx) | 1169 | static int fimc_setup_clocks(struct fimc_context *ctx) |
| 1561 | { | 1170 | { |
| 1562 | struct device *fimc_dev = ctx->ippdrv.dev; | 1171 | struct device *fimc_dev = ctx->dev; |
| 1563 | struct device *dev; | 1172 | struct device *dev; |
| 1564 | int ret, i; | 1173 | int ret, i; |
| 1565 | 1174 | ||
| @@ -1574,8 +1183,6 @@ static int fimc_setup_clocks(struct fimc_context *ctx) · 8 unmodified lines | |||
| 1574 | 1183 | ||
| 1575 | ctx->clocks[i] = clk_get(dev, fimc_clock_names[i]); | 1184 | ctx->clocks[i] = clk_get(dev, fimc_clock_names[i]); |
| 1576 | if (IS_ERR(ctx->clocks[i])) { | 1185 | if (IS_ERR(ctx->clocks[i])) { |
| 1577 | if (i >= FIMC_CLK_MUX) | ||
| 1578 | break; | ||
| 1579 | ret = PTR_ERR(ctx->clocks[i]); | 1186 | ret = PTR_ERR(ctx->clocks[i]); |
| 1580 | dev_err(fimc_dev, "failed to get clock: %s\n", | 1187 | dev_err(fimc_dev, "failed to get clock: %s\n", |
| 1581 | fimc_clock_names[i]); | 1188 | fimc_clock_names[i]); |
| @@ -1583,20 +1190,6 @@ static int fimc_setup_clocks(struct fimc_context *ctx) · 1 unmodified line | |||
| 1583 | } | 1190 | } |
| 1584 | } | 1191 | } |
| 1585 | 1192 | ||
| 1586 | /* Optional FIMC LCLK parent clock setting */ | ||
| 1587 | if (!IS_ERR(ctx->clocks[FIMC_CLK_PARENT])) { | ||
| 1588 | ret = clk_set_parent(ctx->clocks[FIMC_CLK_MUX], | ||
| 1589 | ctx->clocks[FIMC_CLK_PARENT]); | ||
| 1590 | if (ret < 0) { | ||
| 1591 | dev_err(fimc_dev, "failed to set parent.\n"); | ||
| 1592 | goto e_clk_free; | ||
| 1593 | } | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | ret = clk_set_rate(ctx->clocks[FIMC_CLK_LCLK], ctx->clk_frequency); | ||
| 1597 | if (ret < 0) | ||
| 1598 | goto e_clk_free; | ||
| 1599 | |||
| 1600 | ret = clk_prepare_enable(ctx->clocks[FIMC_CLK_LCLK]); | 1193 | ret = clk_prepare_enable(ctx->clocks[FIMC_CLK_LCLK]); |
| 1601 | if (!ret) | 1194 | if (!ret) |
| 1602 | return ret; | 1195 | return ret; |
| @@ -1605,57 +1198,118 @@ e_clk_free: · 2 unmodified lines | |||
| 1605 | return ret; | 1198 | return ret; |
| 1606 | } | 1199 | } |
| 1607 | 1200 | ||
| 1608 | static int fimc_parse_dt(struct fimc_context *ctx) | 1201 | int exynos_drm_check_fimc_device(struct device *dev) |
| 1609 | { | 1202 | { |
| 1610 | struct device_node *node = ctx->ippdrv.dev->of_node; | 1203 | unsigned int id = of_alias_get_id(dev->of_node, "fimc"); |
| 1611 | 1204 | ||
| 1612 | /* Handle only devices that support the LCD Writeback data path */ | 1205 | if (id >= 0 && (BIT(id) & fimc_mask)) |
| 1613 | if (!of_property_read_bool(node, "samsung,lcd-wb")) | 1206 | return 0; |
| 1614 | return -ENODEV; | 1207 | return -ENODEV; |
| 1208 | } | ||
| 1615 | 1209 | ||
| 1616 | if (of_property_read_u32(node, "clock-frequency", | 1210 | static const unsigned int fimc_formats[] = { |
| 1617 | &ctx->clk_frequency)) | 1211 | DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, |
| 1618 | ctx->clk_frequency = FIMC_DEFAULT_LCLK_FREQUENCY; | 1212 | DRM_FORMAT_NV12, DRM_FORMAT_NV16, DRM_FORMAT_NV21, DRM_FORMAT_NV61, |
| 1213 | DRM_FORMAT_UYVY, DRM_FORMAT_VYUY, DRM_FORMAT_YUYV, DRM_FORMAT_YVYU, | ||
| 1214 | DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, DRM_FORMAT_YUV422, | ||
| 1215 | DRM_FORMAT_YUV444, | ||
| 1216 | }; | ||
| 1619 | 1217 | ||
| 1620 | ctx->id = of_alias_get_id(node, "fimc"); | 1218 | static const unsigned int fimc_tiled_formats[] = { |
| 1219 | DRM_FORMAT_NV12, DRM_FORMAT_NV21, | ||
| 1220 | }; | ||
| 1621 | 1221 | ||
| 1622 | if (ctx->id < 0) { | 1222 | static const struct drm_exynos_ipp_limit fimc_4210_limits_v1[] = { |
| 1623 | dev_err(ctx->ippdrv.dev, "failed to get node alias id.\n"); | 1223 | { IPP_SIZE_LIMIT(BUFFER, .h = { 16, 8192, 8 }, .v = { 16, 8192, 2 }) }, |
| 1624 | return -EINVAL; | 1224 | { IPP_SIZE_LIMIT(AREA, .h = { 16, 4224, 2 }, .v = { 16, 0, 2 }) }, |
| 1625 | } | 1225 | { IPP_SIZE_LIMIT(ROTATED, .h = { 128, 1920 }, .v = { 128, 0 }) }, |
| 1226 | { IPP_SCALE_LIMIT(.h = { (1 << 16) / 64, (1 << 16) * 64 }, | ||
| 1227 | .v = { (1 << 16) / 64, (1 << 16) * 64 }) }, | ||
| 1228 | }; | ||
| 1626 | 1229 | ||
| 1627 | return 0; | 1230 | static const struct drm_exynos_ipp_limit fimc_4210_limits_v2[] = { |
| 1628 | } | 1231 | { IPP_SIZE_LIMIT(BUFFER, .h = { 16, 8192, 8 }, .v = { 16, 8192, 2 }) }, |
| 1232 | { IPP_SIZE_LIMIT(AREA, .h = { 16, 1920, 2 }, .v = { 16, 0, 2 }) }, | ||
| 1233 | { IPP_SIZE_LIMIT(ROTATED, .h = { 128, 1366 }, .v = { 128, 0 }) }, | ||
| 1234 | { IPP_SCALE_LIMIT(.h = { (1 << 16) / 64, (1 << 16) * 64 }, | ||
| 1235 | .v = { (1 << 16) / 64, (1 << 16) * 64 }) }, | ||
| 1236 | }; | ||
| 1237 | |||
| 1238 | static const struct drm_exynos_ipp_limit fimc_4210_limits_tiled_v1[] = { | ||
| 1239 | { IPP_SIZE_LIMIT(BUFFER, .h = { 128, 1920, 128 }, .v = { 32, 1920, 32 }) }, | ||
| 1240 | { IPP_SIZE_LIMIT(AREA, .h = { 128, 1920, 2 }, .v = { 128, 0, 2 }) }, | ||
| 1241 | { IPP_SCALE_LIMIT(.h = { (1 << 16) / 64, (1 << 16) * 64 }, | ||
| 1242 | .v = { (1 << 16) / 64, (1 << 16) * 64 }) }, | ||
| 1243 | }; | ||
| 1244 | |||
| 1245 | static const struct drm_exynos_ipp_limit fimc_4210_limits_tiled_v2[] = { | ||
| 1246 | { IPP_SIZE_LIMIT(BUFFER, .h = { 128, 1920, 128 }, .v = { 32, 1920, 32 }) }, | ||
| 1247 | { IPP_SIZE_LIMIT(AREA, .h = { 128, 1366, 2 }, .v = { 128, 0, 2 }) }, | ||
| 1248 | { IPP_SCALE_LIMIT(.h = { (1 << 16) / 64, (1 << 16) * 64 }, | ||
| 1249 | .v = { (1 << 16) / 64, (1 << 16) * 64 }) }, | ||
| 1250 | }; | ||
| 1629 | 1251 | ||
| 1630 | static int fimc_probe(struct platform_device *pdev) | 1252 | static int fimc_probe(struct platform_device *pdev) |
| 1631 | { | 1253 | { |
| 1254 | const struct drm_exynos_ipp_limit *limits; | ||
| 1255 | struct exynos_drm_ipp_formats *formats; | ||
| 1632 | struct device *dev = &pdev->dev; | 1256 | struct device *dev = &pdev->dev; |
| 1633 | struct fimc_context *ctx; | 1257 | struct fimc_context *ctx; |
| 1634 | struct resource *res; | 1258 | struct resource *res; |
| 1635 | struct exynos_drm_ippdrv *ippdrv; | ||
| 1636 | int ret; | 1259 | int ret; |
| 1260 | int i, j, num_limits, num_formats; | ||
| 1637 | 1261 | ||
| 1638 | if (!dev->of_node) { | 1262 | if (exynos_drm_check_fimc_device(dev) != 0) |
| 1639 | dev_err(dev, "device tree node not found.\n"); | ||
| 1640 | return -ENODEV; | 1263 | return -ENODEV; |
| 1641 | } | ||
| 1642 | 1264 | ||
| 1643 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | 1265 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); |
| 1644 | if (!ctx) | 1266 | if (!ctx) |
| 1645 | return -ENOMEM; | 1267 | return -ENOMEM; |
| 1646 | 1268 | ||
| 1647 | ctx->ippdrv.dev = dev; | 1269 | ctx->dev = dev; |
| 1270 | ctx->id = of_alias_get_id(dev->of_node, "fimc"); | ||
| 1648 | 1271 | ||
| 1649 | ret = fimc_parse_dt(ctx); | 1272 | /* construct formats/limits array */ |
| 1650 | if (ret < 0) | 1273 | num_formats = ARRAY_SIZE(fimc_formats) + ARRAY_SIZE(fimc_tiled_formats); |
| 1651 | return ret; | 1274 | formats = devm_kzalloc(dev, sizeof(*formats) * num_formats, GFP_KERNEL); |
| 1275 | if (!formats) | ||
| 1276 | return -ENOMEM; | ||
| 1277 | |||
| 1278 | /* linear formats */ | ||
| 1279 | if (ctx->id < 3) { | ||
| 1280 | limits = fimc_4210_limits_v1; | ||
| 1281 | num_limits = ARRAY_SIZE(fimc_4210_limits_v1); | ||
| 1282 | } else { | ||
| 1283 | limits = fimc_4210_limits_v2; | ||
| 1284 | num_limits = ARRAY_SIZE(fimc_4210_limits_v2); | ||
| 1285 | } | ||
| 1286 | for (i = 0; i < ARRAY_SIZE(fimc_formats); i++) { | ||
| 1287 | formats[i].fourcc = fimc_formats[i]; | ||
| 1288 | formats[i].type = DRM_EXYNOS_IPP_FORMAT_SOURCE | | ||
| 1289 | DRM_EXYNOS_IPP_FORMAT_DESTINATION; | ||
| 1290 | formats[i].limits = limits; | ||
| 1291 | formats[i].num_limits = num_limits; | ||
| 1292 | } | ||
| 1652 | 1293 | ||
| 1653 | ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, | 1294 | /* tiled formats */ |
| 1654 | "samsung,sysreg"); | 1295 | if (ctx->id < 3) { |
| 1655 | if (IS_ERR(ctx->sysreg)) { | 1296 | limits = fimc_4210_limits_tiled_v1; |
| 1656 | dev_err(dev, "syscon regmap lookup failed.\n"); | 1297 | num_limits = ARRAY_SIZE(fimc_4210_limits_tiled_v1); |
| 1657 | return PTR_ERR(ctx->sysreg); | 1298 | } else { |
| 1299 | limits = fimc_4210_limits_tiled_v2; | ||
| 1300 | num_limits = ARRAY_SIZE(fimc_4210_limits_tiled_v2); | ||
| 1658 | } | 1301 | } |
| 1302 | for (j = i, i = 0; i < ARRAY_SIZE(fimc_tiled_formats); j++, i++) { | ||
| 1303 | formats[j].fourcc = fimc_tiled_formats[i]; | ||
| 1304 | formats[j].modifier = DRM_FORMAT_MOD_SAMSUNG_64_32_TILE; | ||
| 1305 | formats[j].type = DRM_EXYNOS_IPP_FORMAT_SOURCE | | ||
| 1306 | DRM_EXYNOS_IPP_FORMAT_DESTINATION; | ||
| 1307 | formats[j].limits = limits; | ||
| 1308 | formats[j].num_limits = num_limits; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | ctx->formats = formats; | ||
| 1312 | ctx->num_formats = num_formats; | ||
| 1659 | 1313 | ||
| 1660 | /* resource memory */ | 1314 | /* resource memory */ |
| 1661 | ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1315 | ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| @@ -1670,9 +1324,8 @@ static int fimc_probe(struct platform_device *pdev) · 8 unmodified lines | |||
| 1670 | return -ENOENT; | 1324 | return -ENOENT; |
| 1671 | } | 1325 | } |
| 1672 | 1326 | ||
| 1673 | ctx->irq = res->start; | 1327 | ret = devm_request_irq(dev, res->start, fimc_irq_handler, |
| 1674 | ret = devm_request_threaded_irq(dev, ctx->irq, NULL, fimc_irq_handler, | 1328 | 0, dev_name(dev), ctx); |
| 1675 | IRQF_ONESHOT, "drm_fimc", ctx); | ||
| 1676 | if (ret < 0) { | 1329 | if (ret < 0) { |
| 1677 | dev_err(dev, "failed to request irq.\n"); | 1330 | dev_err(dev, "failed to request irq.\n"); |
| 1678 | return ret; | 1331 | return ret; |
| @@ -1682,39 +1335,24 @@ static int fimc_probe(struct platform_device *pdev) · 3 unmodified lines | |||
| 1682 | if (ret < 0) | 1335 | if (ret < 0) |
| 1683 | return ret; | 1336 | return ret; |
| 1684 | 1337 | ||
| 1685 | ippdrv = &ctx->ippdrv; | ||
| 1686 | ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops; | ||
| 1687 | ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops; | ||
| 1688 | ippdrv->check_property = fimc_ippdrv_check_property; | ||
| 1689 | ippdrv->reset = fimc_ippdrv_reset; | ||
| 1690 | ippdrv->start = fimc_ippdrv_start; | ||
| 1691 | ippdrv->stop = fimc_ippdrv_stop; | ||
| 1692 | ret = fimc_init_prop_list(ippdrv); | ||
| 1693 | if (ret < 0) { | ||
| 1694 | dev_err(dev, "failed to init property list.\n"); | ||
| 1695 | goto err_put_clk; | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | DRM_DEBUG_KMS("id[%d]ippdrv[%pK]\n", ctx->id, ippdrv); | ||
| 1699 | |||
| 1700 | spin_lock_init(&ctx->lock); | 1338 | spin_lock_init(&ctx->lock); |
| 1701 | platform_set_drvdata(pdev, ctx); | 1339 | platform_set_drvdata(pdev, ctx); |
| 1702 | 1340 | ||
| 1341 | pm_runtime_use_autosuspend(dev); | ||
| 1342 | pm_runtime_set_autosuspend_delay(dev, FIMC_AUTOSUSPEND_DELAY); | ||
| 1703 | pm_runtime_enable(dev); | 1343 | pm_runtime_enable(dev); |
| 1704 | 1344 | ||
| 1705 | ret = exynos_drm_ippdrv_register(ippdrv); | 1345 | ret = component_add(dev, &fimc_component_ops); |
| 1706 | if (ret < 0) { | 1346 | if (ret) |
| 1707 | dev_err(dev, "failed to register drm fimc device.\n"); | ||
| 1708 | goto err_pm_dis; | 1347 | goto err_pm_dis; |
| 1709 | } | ||
| 1710 | 1348 | ||
| 1711 | dev_info(dev, "drm fimc registered successfully.\n"); | 1349 | dev_info(dev, "drm fimc registered successfully.\n"); |
| 1712 | 1350 | ||
| 1713 | return 0; | 1351 | return 0; |
| 1714 | 1352 | ||
| 1715 | err_pm_dis: | 1353 | err_pm_dis: |
| 1354 | pm_runtime_dont_use_autosuspend(dev); | ||
| 1716 | pm_runtime_disable(dev); | 1355 | pm_runtime_disable(dev); |
| 1717 | err_put_clk: | ||
| 1718 | fimc_put_clocks(ctx); | 1356 | fimc_put_clocks(ctx); |
| 1719 | 1357 | ||
| 1720 | return ret; | 1358 | return ret; |
| @@ -1724,42 +1362,24 @@ static int fimc_remove(struct platform_device *pdev) · 3 unmodified lines | |||
| 1724 | { | 1362 | { |
| 1725 | struct device *dev = &pdev->dev; | 1363 | struct device *dev = &pdev->dev; |
| 1726 | struct fimc_context *ctx = get_fimc_context(dev); | 1364 | struct fimc_context *ctx = get_fimc_context(dev); |
| 1727 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1728 | 1365 | ||
| 1729 | exynos_drm_ippdrv_unregister(ippdrv); | 1366 | component_del(dev, &fimc_component_ops); |
| 1367 | pm_runtime_dont_use_autosuspend(dev); | ||
| 1368 | pm_runtime_disable(dev); | ||
| 1730 | 1369 | ||
| 1731 | fimc_put_clocks(ctx); | 1370 | fimc_put_clocks(ctx); |
| 1732 | pm_runtime_set_suspended(dev); | ||
| 1733 | pm_runtime_disable(dev); | ||
| 1734 | 1371 | ||
| 1735 | return 0; | 1372 | return 0; |
| 1736 | } | 1373 | } |
| 1737 | 1374 | ||
| 1738 | #ifdef CONFIG_PM | 1375 | #ifdef CONFIG_PM |
| 1739 | static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable) | ||
| 1740 | { | ||
| 1741 | DRM_DEBUG_KMS("enable[%d]\n", enable); | ||
| 1742 | |||
| 1743 | if (enable) { | ||
| 1744 | clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]); | ||
| 1745 | clk_prepare_enable(ctx->clocks[FIMC_CLK_WB_A]); | ||
| 1746 | ctx->suspended = false; | ||
| 1747 | } else { | ||
| 1748 | clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]); | ||
| 1749 | clk_disable_unprepare(ctx->clocks[FIMC_CLK_WB_A]); | ||
| 1750 | ctx->suspended = true; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | return 0; | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | static int fimc_runtime_suspend(struct device *dev) | 1376 | static int fimc_runtime_suspend(struct device *dev) |
| 1757 | { | 1377 | { |
| 1758 | struct fimc_context *ctx = get_fimc_context(dev); | 1378 | struct fimc_context *ctx = get_fimc_context(dev); |
| 1759 | 1379 | ||
| 1760 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); | 1380 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); |
| 1761 | 1381 | clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]); | |
| 1762 | return fimc_clk_ctrl(ctx, false); | 1382 | return 0; |
| 1763 | } | 1383 | } |
| 1764 | 1384 | ||
| 1765 | static int fimc_runtime_resume(struct device *dev) | 1385 | static int fimc_runtime_resume(struct device *dev) |
| @@ -1767,8 +1387,7 @@ static int fimc_runtime_resume(struct device *dev) · 1 unmodified line | |||
| 1767 | struct fimc_context *ctx = get_fimc_context(dev); | 1387 | struct fimc_context *ctx = get_fimc_context(dev); |
| 1768 | 1388 | ||
| 1769 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); | 1389 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); |
| 1770 | 1390 | return clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]); | |
| 1771 | return fimc_clk_ctrl(ctx, true); | ||
| 1772 | } | 1391 | } |
| 1773 | #endif | 1392 | #endif |
| 1774 | 1393 | ||
| @@ -1795,4 +1414,3 @@ struct platform_driver fimc_driver = { · 20 unmodified lines | |||
| 1795 | .pm = &fimc_pm_ops, | 1414 | .pm = &fimc_pm_ops, |
| 1796 | }, | 1415 | }, |
| 1797 | }; | 1416 | }; |
| 1798 | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 6 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 7 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License as published by the | ||
| 11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 12 | * option) any later version. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef _EXYNOS_DRM_FIMC_H_ | ||
| 16 | #define _EXYNOS_DRM_FIMC_H_ | ||
| 17 | |||
| 18 | /* | ||
| 19 | * TODO | ||
| 20 | * FIMD output interface notifier callback. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #endif /* _EXYNOS_DRM_FIMC_H_ */ | ||
| @@ -121,6 +121,12 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = { · 120 unmodified lines | |||
| 121 | .has_limited_fmt = 1, | 121 | .has_limited_fmt = 1, |
| 122 | }; | 122 | }; |
| 123 | 123 | ||
| 124 | static struct fimd_driver_data s5pv210_fimd_driver_data = { | ||
| 125 | .timing_base = 0x0, | ||
| 126 | .has_shadowcon = 1, | ||
| 127 | .has_clksel = 1, | ||
| 128 | }; | ||
| 129 | |||
| 124 | static struct fimd_driver_data exynos3_fimd_driver_data = { | 130 | static struct fimd_driver_data exynos3_fimd_driver_data = { |
| 125 | .timing_base = 0x20000, | 131 | .timing_base = 0x20000, |
| 126 | .lcdblk_offset = 0x210, | 132 | .lcdblk_offset = 0x210, |
| @@ -193,6 +199,8 @@ struct fimd_context { · 66 unmodified lines | |||
| 193 | static const struct of_device_id fimd_driver_dt_match[] = { | 199 | static const struct of_device_id fimd_driver_dt_match[] = { |
| 194 | { .compatible = "samsung,s3c6400-fimd", | 200 | { .compatible = "samsung,s3c6400-fimd", |
| 195 | .data = &s3c64xx_fimd_driver_data }, | 201 | .data = &s3c64xx_fimd_driver_data }, |
| 202 | { .compatible = "samsung,s5pv210-fimd", | ||
| 203 | .data = &s5pv210_fimd_driver_data }, | ||
| 196 | { .compatible = "samsung,exynos3250-fimd", | 204 | { .compatible = "samsung,exynos3250-fimd", |
| 197 | .data = &exynos3_fimd_driver_data }, | 205 | .data = &exynos3_fimd_driver_data }, |
| 198 | { .compatible = "samsung,exynos4210-fimd", | 206 | { .compatible = "samsung,exynos4210-fimd", |
| @@ -431,37 +431,24 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, · 430 unmodified lines | |||
| 431 | return 0; | 431 | return 0; |
| 432 | } | 432 | } |
| 433 | 433 | ||
| 434 | int exynos_drm_gem_fault(struct vm_fault *vmf) | 434 | vm_fault_t exynos_drm_gem_fault(struct vm_fault *vmf) |
| 435 | { | 435 | { |
| 436 | struct vm_area_struct *vma = vmf->vma; | 436 | struct vm_area_struct *vma = vmf->vma; |
| 437 | struct drm_gem_object *obj = vma->vm_private_data; | 437 | struct drm_gem_object *obj = vma->vm_private_data; |
| 438 | struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); | 438 | struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); |
| 439 | unsigned long pfn; | 439 | unsigned long pfn; |
| 440 | pgoff_t page_offset; | 440 | pgoff_t page_offset; |
| 441 | int ret; | ||
| 442 | 441 | ||
| 443 | page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; | 442 | page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; |
| 444 | 443 | ||
| 445 | if (page_offset >= (exynos_gem->size >> PAGE_SHIFT)) { | 444 | if (page_offset >= (exynos_gem->size >> PAGE_SHIFT)) { |
| 446 | DRM_ERROR("invalid page offset\n"); | 445 | DRM_ERROR("invalid page offset\n"); |
| 447 | ret = -EINVAL; | 446 | return VM_FAULT_SIGBUS; |
| 448 | goto out; | ||
| 449 | } | 447 | } |
| 450 | 448 | ||
| 451 | pfn = page_to_pfn(exynos_gem->pages[page_offset]); | 449 | pfn = page_to_pfn(exynos_gem->pages[page_offset]); |
| 452 | ret = vm_insert_mixed(vma, vmf->address, __pfn_to_pfn_t(pfn, PFN_DEV)); | 450 | return vmf_insert_mixed(vma, vmf->address, |
| 453 | 451 | __pfn_to_pfn_t(pfn, PFN_DEV)); | |
| 454 | out: | ||
| 455 | switch (ret) { | ||
| 456 | case 0: | ||
| 457 | case -ERESTARTSYS: | ||
| 458 | case -EINTR: | ||
| 459 | return VM_FAULT_NOPAGE; | ||
| 460 | case -ENOMEM: | ||
| 461 | return VM_FAULT_OOM; | ||
| 462 | default: | ||
| 463 | return VM_FAULT_SIGBUS; | ||
| 464 | } | ||
| 465 | } | 452 | } |
| 466 | 453 | ||
| 467 | static int exynos_drm_gem_mmap_obj(struct drm_gem_object *obj, | 454 | static int exynos_drm_gem_mmap_obj(struct drm_gem_object *obj, |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | |||
| 13 | #define _EXYNOS_DRM_GEM_H_ | 13 | #define _EXYNOS_DRM_GEM_H_ |
| 14 | 14 | ||
| 15 | #include <drm/drm_gem.h> | 15 | #include <drm/drm_gem.h> |
| 16 | #include <linux/mm_types.h> | ||
| 16 | 17 | ||
| 17 | #define to_exynos_gem(x) container_of(x, struct exynos_drm_gem, base) | 18 | #define to_exynos_gem(x) container_of(x, struct exynos_drm_gem, base) |
| 18 | 19 | ||
| @@ -111,7 +112,7 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, · 92 unmodified lines | |||
| 111 | struct drm_mode_create_dumb *args); | 112 | struct drm_mode_create_dumb *args); |
| 112 | 113 | ||
| 113 | /* page fault handler and mmap fault address(virtual) to physical memory. */ | 114 | /* page fault handler and mmap fault address(virtual) to physical memory. */ |
| 114 | int exynos_drm_gem_fault(struct vm_fault *vmf); | 115 | vm_fault_t exynos_drm_gem_fault(struct vm_fault *vmf); |
| 115 | 116 | ||
| 116 | /* set vm_flags and we can change the vm attribute to other one at here. */ | 117 | /* set vm_flags and we can change the vm attribute to other one at here. */ |
| 117 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | 118 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); |
| @@ -12,18 +12,20 @@ · 11 unmodified lines | |||
| 12 | * | 12 | * |
| 13 | */ | 13 | */ |
| 14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 15 | #include <linux/component.h> | ||
| 15 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
| 16 | #include <linux/clk.h> | 17 | #include <linux/clk.h> |
| 17 | #include <linux/pm_runtime.h> | 18 | #include <linux/pm_runtime.h> |
| 18 | #include <linux/mfd/syscon.h> | 19 | #include <linux/mfd/syscon.h> |
| 20 | #include <linux/of_device.h> | ||
| 19 | #include <linux/regmap.h> | 21 | #include <linux/regmap.h> |
| 20 | 22 | ||
| 21 | #include <drm/drmP.h> | 23 | #include <drm/drmP.h> |
| 22 | #include <drm/exynos_drm.h> | 24 | #include <drm/exynos_drm.h> |
| 23 | #include "regs-gsc.h" | 25 | #include "regs-gsc.h" |
| 24 | #include "exynos_drm_drv.h" | 26 | #include "exynos_drm_drv.h" |
| 27 | #include "exynos_drm_iommu.h" | ||
| 25 | #include "exynos_drm_ipp.h" | 28 | #include "exynos_drm_ipp.h" |
| 26 | #include "exynos_drm_gsc.h" | ||
| 27 | 29 | ||
| 28 | /* | 30 | /* |
| 29 | * GSC stands for General SCaler and | 31 | * GSC stands for General SCaler and |
| @@ -31,26 +33,10 @@ · 1 unmodified line | |||
| 31 | * input DMA reads image data from the memory. | 33 | * input DMA reads image data from the memory. |
| 32 | * output DMA writes image data to memory. | 34 | * output DMA writes image data to memory. |
| 33 | * GSC supports image rotation and image effect functions. | 35 | * GSC supports image rotation and image effect functions. |
| 34 | * | ||
| 35 | * M2M operation : supports crop/scale/rotation/csc so on. | ||
| 36 | * Memory ----> GSC H/W ----> Memory. | ||
| 37 | * Writeback operation : supports cloned screen with FIMD. | ||
| 38 | * FIMD ----> GSC H/W ----> Memory. | ||
| 39 | * Output operation : supports direct display using local path. | ||
| 40 | * Memory ----> GSC H/W ----> FIMD, Mixer. | ||
| 41 | */ | 36 | */ |
| 42 | 37 | ||
| 43 | /* | ||
| 44 | * TODO | ||
| 45 | * 1. check suspend/resume api if needed. | ||
| 46 | * 2. need to check use case platform_device_id. | ||
| 47 | * 3. check src/dst size with, height. | ||
| 48 | * 4. added check_prepare api for right register. | ||
| 49 | * 5. need to add supported list in prop_list. | ||
| 50 | * 6. check prescaler/scaler optimization. | ||
| 51 | */ | ||
| 52 | 38 | ||
| 53 | #define GSC_MAX_DEVS 4 | 39 | #define GSC_MAX_CLOCKS 8 |
| 54 | #define GSC_MAX_SRC 4 | 40 | #define GSC_MAX_SRC 4 |
| 55 | #define GSC_MAX_DST 16 | 41 | #define GSC_MAX_DST 16 |
| 56 | #define GSC_RESET_TIMEOUT 50 | 42 | #define GSC_RESET_TIMEOUT 50 |
| @@ -65,8 +51,6 @@ · 8 unmodified lines | |||
| 65 | #define GSC_SC_DOWN_RATIO_4_8 131072 | 51 | #define GSC_SC_DOWN_RATIO_4_8 131072 |
| 66 | #define GSC_SC_DOWN_RATIO_3_8 174762 | 52 | #define GSC_SC_DOWN_RATIO_3_8 174762 |
| 67 | #define GSC_SC_DOWN_RATIO_2_8 262144 | 53 | #define GSC_SC_DOWN_RATIO_2_8 262144 |
| 68 | #define GSC_REFRESH_MIN 12 | ||
| 69 | #define GSC_REFRESH_MAX 60 | ||
| 70 | #define GSC_CROP_MAX 8192 | 54 | #define GSC_CROP_MAX 8192 |
| 71 | #define GSC_CROP_MIN 32 | 55 | #define GSC_CROP_MIN 32 |
| 72 | #define GSC_SCALE_MAX 4224 | 56 | #define GSC_SCALE_MAX 4224 |
| @@ -77,10 +61,9 @@ · 4 unmodified lines | |||
| 77 | #define GSC_COEF_H_8T 8 | 61 | #define GSC_COEF_H_8T 8 |
| 78 | #define GSC_COEF_V_4T 4 | 62 | #define GSC_COEF_V_4T 4 |
| 79 | #define GSC_COEF_DEPTH 3 | 63 | #define GSC_COEF_DEPTH 3 |
| 64 | #define GSC_AUTOSUSPEND_DELAY 2000 | ||
| 80 | 65 | ||
| 81 | #define get_gsc_context(dev) platform_get_drvdata(to_platform_device(dev)) | 66 | #define get_gsc_context(dev) platform_get_drvdata(to_platform_device(dev)) |
| 82 | #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ | ||
| 83 | struct gsc_context, ippdrv); | ||
| 84 | #define gsc_read(offset) readl(ctx->regs + (offset)) | 67 | #define gsc_read(offset) readl(ctx->regs + (offset)) |
| 85 | #define gsc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) | 68 | #define gsc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) |
| 86 | 69 | ||
| @@ -103,51 +86,48 @@ struct gsc_scaler { · 16 unmodified lines | |||
| 103 | unsigned long main_vratio; | 86 | unsigned long main_vratio; |
| 104 | }; | 87 | }; |
| 105 | 88 | ||
| 106 | /* | ||
| 107 | * A structure of scaler capability. | ||
| 108 | * | ||
| 109 | * find user manual 49.2 features. | ||
| 110 | * @tile_w: tile mode or rotation width. | ||
| 111 | * @tile_h: tile mode or rotation height. | ||
| 112 | * @w: other cases width. | ||
| 113 | * @h: other cases height. | ||
| 114 | */ | ||
| 115 | struct gsc_capability { | ||
| 116 | /* tile or rotation */ | ||
| 117 | u32 tile_w; | ||
| 118 | u32 tile_h; | ||
| 119 | /* other cases */ | ||
| 120 | u32 w; | ||
| 121 | u32 h; | ||
| 122 | }; | ||
| 123 | |||
| 124 | /* | 89 | /* |
| 125 | * A structure of gsc context. | 90 | * A structure of gsc context. |
| 126 | * | 91 | * |
| 127 | * @ippdrv: prepare initialization using ippdrv. | ||
| 128 | * @regs_res: register resources. | 92 | * @regs_res: register resources. |
| 129 | * @regs: memory mapped io registers. | 93 | * @regs: memory mapped io registers. |
| 130 | * @sysreg: handle to SYSREG block regmap. | ||
| 131 | * @lock: locking of operations. | ||
| 132 | * @gsc_clk: gsc gate clock. | 94 | * @gsc_clk: gsc gate clock. |
| 133 | * @sc: scaler infomations. | 95 | * @sc: scaler infomations. |
| 134 | * @id: gsc id. | 96 | * @id: gsc id. |
| 135 | * @irq: irq number. | 97 | * @irq: irq number. |
| 136 | * @rotation: supports rotation of src. | 98 | * @rotation: supports rotation of src. |
| 137 | * @suspended: qos operations. | ||
| 138 | */ | 99 | */ |
| 139 | struct gsc_context { | 100 | struct gsc_context { |
| 140 | struct exynos_drm_ippdrv ippdrv; | 101 | struct exynos_drm_ipp ipp; |
| 102 | struct drm_device *drm_dev; | ||
| 103 | struct device *dev; | ||
| 104 | struct exynos_drm_ipp_task *task; | ||
| 105 | struct exynos_drm_ipp_formats *formats; | ||
| 106 | unsigned int num_formats; | ||
| 107 | |||
| 141 | struct resource *regs_res; | 108 | struct resource *regs_res; |
| 142 | void __iomem *regs; | 109 | void __iomem *regs; |
| 143 | struct regmap *sysreg; | 110 | const char **clk_names; |
| 144 | struct mutex lock; | 111 | struct clk *clocks[GSC_MAX_CLOCKS]; |
| 145 | struct clk *gsc_clk; | 112 | int num_clocks; |
| 146 | struct gsc_scaler sc; | 113 | struct gsc_scaler sc; |
| 147 | int id; | 114 | int id; |
| 148 | int irq; | 115 | int irq; |
| 149 | bool rotation; | 116 | bool rotation; |
| 150 | bool suspended; | 117 | }; |
| 118 | |||
| 119 | /** | ||
| 120 | * struct gsc_driverdata - per device type driver data for init time. | ||
| 121 | * | ||
| 122 | * @limits: picture size limits array | ||
| 123 | * @clk_names: names of clocks needed by this variant | ||
| 124 | * @num_clocks: the number of clocks needed by this variant | ||
| 125 | */ | ||
| 126 | struct gsc_driverdata { | ||
| 127 | const struct drm_exynos_ipp_limit *limits; | ||
| 128 | int num_limits; | ||
| 129 | const char *clk_names[GSC_MAX_CLOCKS]; | ||
| 130 | int num_clocks; | ||
| 151 | }; | 131 | }; |
| 152 | 132 | ||
| 153 | /* 8-tap Filter Coefficient */ | 133 | /* 8-tap Filter Coefficient */ |
| @@ -438,25 +418,6 @@ static int gsc_sw_reset(struct gsc_context *ctx) · 284 unmodified lines | |||
| 438 | return 0; | 418 | return 0; |
| 439 | } | 419 | } |
| 440 | 420 | ||
| 441 | static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) | ||
| 442 | { | ||
| 443 | unsigned int gscblk_cfg; | ||
| 444 | |||
| 445 | if (!ctx->sysreg) | ||
| 446 | return; | ||
| 447 | |||
| 448 | regmap_read(ctx->sysreg, SYSREG_GSCBLK_CFG1, &gscblk_cfg); | ||
| 449 | |||
| 450 | if (enable) | ||
| 451 | gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) | | ||
| 452 | GSC_BLK_GSCL_WB_IN_SRC_SEL(ctx->id) | | ||
| 453 | GSC_BLK_SW_RESET_WB_DEST(ctx->id); | ||
| 454 | else | ||
| 455 | gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id); | ||
| 456 | |||
| 457 | regmap_write(ctx->sysreg, SYSREG_GSCBLK_CFG1, gscblk_cfg); | ||
| 458 | } | ||
| 459 | |||
| 460 | static void gsc_handle_irq(struct gsc_context *ctx, bool enable, | 421 | static void gsc_handle_irq(struct gsc_context *ctx, bool enable, |
| 461 | bool overflow, bool done) | 422 | bool overflow, bool done) |
| 462 | { | 423 | { |
| @@ -487,10 +448,8 @@ static void gsc_handle_irq(struct gsc_context *ctx, bool enable, · 24 unmodified lines | |||
| 487 | } | 448 | } |
| 488 | 449 | ||
| 489 | 450 | ||
| 490 | static int gsc_src_set_fmt(struct device *dev, u32 fmt) | 451 | static void gsc_src_set_fmt(struct gsc_context *ctx, u32 fmt) |
| 491 | { | 452 | { |
| 492 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 493 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 494 | u32 cfg; | 453 | u32 cfg; |
| 495 | 454 | ||
| 496 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); | 455 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); |
| @@ -506,6 +465,7 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt) · 9 unmodified lines | |||
| 506 | cfg |= GSC_IN_RGB565; | 465 | cfg |= GSC_IN_RGB565; |
| 507 | break; | 466 | break; |
| 508 | case DRM_FORMAT_XRGB8888: | 467 | case DRM_FORMAT_XRGB8888: |
| 468 | case DRM_FORMAT_ARGB8888: | ||
| 509 | cfg |= GSC_IN_XRGB8888; | 469 | cfg |= GSC_IN_XRGB8888; |
| 510 | break; | 470 | break; |
| 511 | case DRM_FORMAT_BGRX8888: | 471 | case DRM_FORMAT_BGRX8888: |
| @@ -548,115 +508,84 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt) · 36 unmodified lines | |||
| 548 | cfg |= (GSC_IN_CHROMA_ORDER_CBCR | | 508 | cfg |= (GSC_IN_CHROMA_ORDER_CBCR | |
| 549 | GSC_IN_YUV420_2P); | 509 | GSC_IN_YUV420_2P); |
| 550 | break; | 510 | break; |
| 551 | default: | ||
| 552 | dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt); | ||
| 553 | return -EINVAL; | ||
| 554 | } | 511 | } |
| 555 | 512 | ||
| 556 | gsc_write(cfg, GSC_IN_CON); | 513 | gsc_write(cfg, GSC_IN_CON); |
| 557 | |||
| 558 | return 0; | ||
| 559 | } | 514 | } |
| 560 | 515 | ||
| 561 | static int gsc_src_set_transf(struct device *dev, | 516 | static void gsc_src_set_transf(struct gsc_context *ctx, unsigned int rotation) |
| 562 | enum drm_exynos_degree degree, | ||
| 563 | enum drm_exynos_flip flip, bool *swap) | ||
| 564 | { | 517 | { |
| 565 | struct gsc_context *ctx = get_gsc_context(dev); | 518 | unsigned int degree = rotation & DRM_MODE_ROTATE_MASK; |
| 566 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 567 | u32 cfg; | 519 | u32 cfg; |
| 568 | 520 | ||
| 569 | DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); | ||
| 570 | |||
| 571 | cfg = gsc_read(GSC_IN_CON); | 521 | cfg = gsc_read(GSC_IN_CON); |
| 572 | cfg &= ~GSC_IN_ROT_MASK; | 522 | cfg &= ~GSC_IN_ROT_MASK; |
| 573 | 523 | ||
| 574 | switch (degree) { | 524 | switch (degree) { |
| 575 | case EXYNOS_DRM_DEGREE_0: | 525 | case DRM_MODE_ROTATE_0: |
| 576 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 526 | if (rotation & DRM_MODE_REFLECT_Y) |
| 577 | cfg |= GSC_IN_ROT_XFLIP; | 527 | cfg |= GSC_IN_ROT_XFLIP; |
| 578 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 528 | if (rotation & DRM_MODE_REFLECT_X) |
| 579 | cfg |= GSC_IN_ROT_YFLIP; | 529 | cfg |= GSC_IN_ROT_YFLIP; |
| 580 | break; | 530 | break; |
| 581 | case EXYNOS_DRM_DEGREE_90: | 531 | case DRM_MODE_ROTATE_90: |
| 582 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 532 | cfg |= GSC_IN_ROT_90; |
| 583 | cfg |= GSC_IN_ROT_90_XFLIP; | 533 | if (rotation & DRM_MODE_REFLECT_Y) |
| 584 | else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 534 | cfg |= GSC_IN_ROT_XFLIP; |
| 585 | cfg |= GSC_IN_ROT_90_YFLIP; | 535 | if (rotation & DRM_MODE_REFLECT_X) |
| 586 | else | 536 | cfg |= GSC_IN_ROT_YFLIP; |
| 587 | cfg |= GSC_IN_ROT_90; | ||
| 588 | break; | 537 | break; |
| 589 | case EXYNOS_DRM_DEGREE_180: | 538 | case DRM_MODE_ROTATE_180: |
| 590 | cfg |= GSC_IN_ROT_180; | 539 | cfg |= GSC_IN_ROT_180; |
| 591 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 540 | if (rotation & DRM_MODE_REFLECT_Y) |
| 592 | cfg &= ~GSC_IN_ROT_XFLIP; | 541 | cfg &= ~GSC_IN_ROT_XFLIP; |
| 593 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 542 | if (rotation & DRM_MODE_REFLECT_X) |
| 594 | cfg &= ~GSC_IN_ROT_YFLIP; | 543 | cfg &= ~GSC_IN_ROT_YFLIP; |
| 595 | break; | 544 | break; |
| 596 | case EXYNOS_DRM_DEGREE_270: | 545 | case DRM_MODE_ROTATE_270: |
| 597 | cfg |= GSC_IN_ROT_270; | 546 | cfg |= GSC_IN_ROT_270; |
| 598 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | 547 | if (rotation & DRM_MODE_REFLECT_Y) |
| 599 | cfg &= ~GSC_IN_ROT_XFLIP; | 548 | cfg &= ~GSC_IN_ROT_XFLIP; |
| 600 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | 549 | if (rotation & DRM_MODE_REFLECT_X) |
| 601 | cfg &= ~GSC_IN_ROT_YFLIP; | 550 | cfg &= ~GSC_IN_ROT_YFLIP; |
| 602 | break; | 551 | break; |
| 603 | default: | ||
| 604 | dev_err(ippdrv->dev, "invalid degree value %d.\n", degree); | ||
| 605 | return -EINVAL; | ||
| 606 | } | 552 | } |
| 607 | 553 | ||
| 608 | gsc_write(cfg, GSC_IN_CON); | 554 | gsc_write(cfg, GSC_IN_CON); |
| 609 | 555 | ||
| 610 | ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0; | 556 | ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0; |
| 611 | *swap = ctx->rotation; | ||
| 612 | |||
| 613 | return 0; | ||
| 614 | } | 557 | } |
| 615 | 558 | ||
| 616 | static int gsc_src_set_size(struct device *dev, int swap, | 559 | static void gsc_src_set_size(struct gsc_context *ctx, |
| 617 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | 560 | struct exynos_drm_ipp_buffer *buf) |
| 618 | { | 561 | { |
| 619 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 620 | struct drm_exynos_pos img_pos = *pos; | ||
| 621 | struct gsc_scaler *sc = &ctx->sc; | 562 | struct gsc_scaler *sc = &ctx->sc; |
| 622 | u32 cfg; | 563 | u32 cfg; |
| 623 | 564 | ||
| 624 | DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n", | ||
| 625 | swap, pos->x, pos->y, pos->w, pos->h); | ||
| 626 | |||
| 627 | if (swap) { | ||
| 628 | img_pos.w = pos->h; | ||
| 629 | img_pos.h = pos->w; | ||
| 630 | } | ||
| 631 | |||
| 632 | /* pixel offset */ | 565 | /* pixel offset */ |
| 633 | cfg = (GSC_SRCIMG_OFFSET_X(img_pos.x) | | 566 | cfg = (GSC_SRCIMG_OFFSET_X(buf->rect.x) | |
| 634 | GSC_SRCIMG_OFFSET_Y(img_pos.y)); | 567 | GSC_SRCIMG_OFFSET_Y(buf->rect.y)); |
| 635 | gsc_write(cfg, GSC_SRCIMG_OFFSET); | 568 | gsc_write(cfg, GSC_SRCIMG_OFFSET); |
| 636 | 569 | ||
| 637 | /* cropped size */ | 570 | /* cropped size */ |
| 638 | cfg = (GSC_CROPPED_WIDTH(img_pos.w) | | 571 | cfg = (GSC_CROPPED_WIDTH(buf->rect.w) | |
| 639 | GSC_CROPPED_HEIGHT(img_pos.h)); | 572 | GSC_CROPPED_HEIGHT(buf->rect.h)); |
| 640 | gsc_write(cfg, GSC_CROPPED_SIZE); | 573 | gsc_write(cfg, GSC_CROPPED_SIZE); |
| 641 | 574 | ||
| 642 | DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize); | ||
| 643 | |||
| 644 | /* original size */ | 575 | /* original size */ |
| 645 | cfg = gsc_read(GSC_SRCIMG_SIZE); | 576 | cfg = gsc_read(GSC_SRCIMG_SIZE); |
| 646 | cfg &= ~(GSC_SRCIMG_HEIGHT_MASK | | 577 | cfg &= ~(GSC_SRCIMG_HEIGHT_MASK | |
| 647 | GSC_SRCIMG_WIDTH_MASK); | 578 | GSC_SRCIMG_WIDTH_MASK); |
| 648 | 579 | ||
| 649 | cfg |= (GSC_SRCIMG_WIDTH(sz->hsize) | | 580 | cfg |= (GSC_SRCIMG_WIDTH(buf->buf.width) | |
| 650 | GSC_SRCIMG_HEIGHT(sz->vsize)); | 581 | GSC_SRCIMG_HEIGHT(buf->buf.height)); |
| 651 | 582 | ||
| 652 | gsc_write(cfg, GSC_SRCIMG_SIZE); | 583 | gsc_write(cfg, GSC_SRCIMG_SIZE); |
| 653 | 584 | ||
| 654 | cfg = gsc_read(GSC_IN_CON); | 585 | cfg = gsc_read(GSC_IN_CON); |
| 655 | cfg &= ~GSC_IN_RGB_TYPE_MASK; | 586 | cfg &= ~GSC_IN_RGB_TYPE_MASK; |
| 656 | 587 | ||
| 657 | DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range); | 588 | if (buf->rect.w >= GSC_WIDTH_ITU_709) |
| 658 | |||
| 659 | if (pos->w >= GSC_WIDTH_ITU_709) | ||
| 660 | if (sc->range) | 589 | if (sc->range) |
| 661 | cfg |= GSC_IN_RGB_HD_WIDE; | 590 | cfg |= GSC_IN_RGB_HD_WIDE; |
| 662 | else | 591 | else |
| @@ -668,103 +597,39 @@ static int gsc_src_set_size(struct device *dev, int swap, · 5 unmodified lines | |||
| 668 | cfg |= GSC_IN_RGB_SD_NARROW; | 597 | cfg |= GSC_IN_RGB_SD_NARROW; |
| 669 | 598 | ||
| 670 | gsc_write(cfg, GSC_IN_CON); | 599 | gsc_write(cfg, GSC_IN_CON); |
| 671 | |||
| 672 | return 0; | ||
| 673 | } | 600 | } |
| 674 | 601 | ||
| 675 | static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id, | 602 | static void gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id, |
| 676 | enum drm_exynos_ipp_buf_type buf_type) | 603 | bool enqueue) |
| 677 | { | 604 | { |
| 678 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | 605 | bool masked = !enqueue; |
| 679 | bool masked; | ||
| 680 | u32 cfg; | 606 | u32 cfg; |
| 681 | u32 mask = 0x00000001 << buf_id; | 607 | u32 mask = 0x00000001 << buf_id; |
| 682 | 608 | ||
| 683 | DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); | ||
| 684 | |||
| 685 | /* mask register set */ | 609 | /* mask register set */ |
| 686 | cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); | 610 | cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); |
| 687 | 611 | ||
| 688 | switch (buf_type) { | ||
| 689 | case IPP_BUF_ENQUEUE: | ||
| 690 | masked = false; | ||
| 691 | break; | ||
| 692 | case IPP_BUF_DEQUEUE: | ||
| 693 | masked = true; | ||
| 694 | break; | ||
| 695 | default: | ||
| 696 | dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); | ||
| 697 | return -EINVAL; | ||
| 698 | } | ||
| 699 | |||
| 700 | /* sequence id */ | 612 | /* sequence id */ |
| 701 | cfg &= ~mask; | 613 | cfg &= ~mask; |
| 702 | cfg |= masked << buf_id; | 614 | cfg |= masked << buf_id; |
| 703 | gsc_write(cfg, GSC_IN_BASE_ADDR_Y_MASK); | 615 | gsc_write(cfg, GSC_IN_BASE_ADDR_Y_MASK); |
| 704 | gsc_write(cfg, GSC_IN_BASE_ADDR_CB_MASK); | 616 | gsc_write(cfg, GSC_IN_BASE_ADDR_CB_MASK); |
| 705 | gsc_write(cfg, GSC_IN_BASE_ADDR_CR_MASK); | 617 | gsc_write(cfg, GSC_IN_BASE_ADDR_CR_MASK); |
| 706 | |||
| 707 | return 0; | ||
| 708 | } | 618 | } |
| 709 | 619 | ||
| 710 | static int gsc_src_set_addr(struct device *dev, | 620 | static void gsc_src_set_addr(struct gsc_context *ctx, u32 buf_id, |
| 711 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | 621 | struct exynos_drm_ipp_buffer *buf) |
| 712 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 713 | { | 622 | { |
| 714 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 715 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 716 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | ||
| 717 | struct drm_exynos_ipp_property *property; | ||
| 718 | |||
| 719 | if (!c_node) { | ||
| 720 | DRM_ERROR("failed to get c_node.\n"); | ||
| 721 | return -EFAULT; | ||
| 722 | } | ||
| 723 | |||
| 724 | property = &c_node->property; | ||
| 725 | |||
| 726 | DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", | ||
| 727 | property->prop_id, buf_id, buf_type); | ||
| 728 | |||
| 729 | if (buf_id > GSC_MAX_SRC) { | ||
| 730 | dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id); | ||
| 731 | return -EINVAL; | ||
| 732 | } | ||
| 733 | |||
| 734 | /* address register set */ | 623 | /* address register set */ |
| 735 | switch (buf_type) { | 624 | gsc_write(buf->dma_addr[0], GSC_IN_BASE_ADDR_Y(buf_id)); |
| 736 | case IPP_BUF_ENQUEUE: | 625 | gsc_write(buf->dma_addr[1], GSC_IN_BASE_ADDR_CB(buf_id)); |
| 737 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], | 626 | gsc_write(buf->dma_addr[2], GSC_IN_BASE_ADDR_CR(buf_id)); |
| 738 | GSC_IN_BASE_ADDR_Y(buf_id)); | ||
| 739 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 740 | GSC_IN_BASE_ADDR_CB(buf_id)); | ||
| 741 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 742 | GSC_IN_BASE_ADDR_CR(buf_id)); | ||
| 743 | break; | ||
| 744 | case IPP_BUF_DEQUEUE: | ||
| 745 | gsc_write(0x0, GSC_IN_BASE_ADDR_Y(buf_id)); | ||
| 746 | gsc_write(0x0, GSC_IN_BASE_ADDR_CB(buf_id)); | ||
| 747 | gsc_write(0x0, GSC_IN_BASE_ADDR_CR(buf_id)); | ||
| 748 | break; | ||
| 749 | default: | ||
| 750 | /* bypass */ | ||
| 751 | break; | ||
| 752 | } | ||
| 753 | 627 | ||
| 754 | return gsc_src_set_buf_seq(ctx, buf_id, buf_type); | 628 | gsc_src_set_buf_seq(ctx, buf_id, true); |
| 755 | } | 629 | } |
| 756 | 630 | ||
| 757 | static struct exynos_drm_ipp_ops gsc_src_ops = { | 631 | static void gsc_dst_set_fmt(struct gsc_context *ctx, u32 fmt) |
| 758 | .set_fmt = gsc_src_set_fmt, | ||
| 759 | .set_transf = gsc_src_set_transf, | ||
| 760 | .set_size = gsc_src_set_size, | ||
| 761 | .set_addr = gsc_src_set_addr, | ||
| 762 | }; | ||
| 763 | |||
| 764 | static int gsc_dst_set_fmt(struct device *dev, u32 fmt) | ||
| 765 | { | 632 | { |
| 766 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 767 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 768 | u32 cfg; | 633 | u32 cfg; |
| 769 | 634 | ||
| 770 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); | 635 | DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); |
| @@ -779,8 +644,9 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt) · 8 unmodified lines | |||
| 779 | case DRM_FORMAT_RGB565: | 644 | case DRM_FORMAT_RGB565: |
| 780 | cfg |= GSC_OUT_RGB565; | 645 | cfg |= GSC_OUT_RGB565; |
| 781 | break; | 646 | break; |
| 647 | case DRM_FORMAT_ARGB8888: | ||
| 782 | case DRM_FORMAT_XRGB8888: | 648 | case DRM_FORMAT_XRGB8888: |
| 783 | cfg |= GSC_OUT_XRGB8888; | 649 | cfg |= (GSC_OUT_XRGB8888 | GSC_OUT_GLOBAL_ALPHA(0xff)); |
| 784 | break; | 650 | break; |
| 785 | case DRM_FORMAT_BGRX8888: | 651 | case DRM_FORMAT_BGRX8888: |
| 786 | cfg |= (GSC_OUT_XRGB8888 | GSC_OUT_RB_SWAP); | 652 | cfg |= (GSC_OUT_XRGB8888 | GSC_OUT_RB_SWAP); |
| @@ -819,69 +685,9 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt) · 32 unmodified lines | |||
| 819 | cfg |= (GSC_OUT_CHROMA_ORDER_CBCR | | 685 | cfg |= (GSC_OUT_CHROMA_ORDER_CBCR | |
| 820 | GSC_OUT_YUV420_2P); | 686 | GSC_OUT_YUV420_2P); |
| 821 | break; | 687 | break; |
| 822 | default: | ||
| 823 | dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt); | ||
| 824 | return -EINVAL; | ||
| 825 | } | 688 | } |
| 826 | 689 | ||
| 827 | gsc_write(cfg, GSC_OUT_CON); | 690 | gsc_write(cfg, GSC_OUT_CON); |
| 828 | |||
| 829 | return 0; | ||
| 830 | } | ||
| 831 | |||
| 832 | static int gsc_dst_set_transf(struct device *dev, | ||
| 833 | enum drm_exynos_degree degree, | ||
| 834 | enum drm_exynos_flip flip, bool *swap) | ||
| 835 | { | ||
| 836 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 837 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 838 | u32 cfg; | ||
| 839 | |||
| 840 | DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); | ||
| 841 | |||
| 842 | cfg = gsc_read(GSC_IN_CON); | ||
| 843 | cfg &= ~GSC_IN_ROT_MASK; | ||
| 844 | |||
| 845 | switch (degree) { | ||
| 846 | case EXYNOS_DRM_DEGREE_0: | ||
| 847 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 848 | cfg |= GSC_IN_ROT_XFLIP; | ||
| 849 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 850 | cfg |= GSC_IN_ROT_YFLIP; | ||
| 851 | break; | ||
| 852 | case EXYNOS_DRM_DEGREE_90: | ||
| 853 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 854 | cfg |= GSC_IN_ROT_90_XFLIP; | ||
| 855 | else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 856 | cfg |= GSC_IN_ROT_90_YFLIP; | ||
| 857 | else | ||
| 858 | cfg |= GSC_IN_ROT_90; | ||
| 859 | break; | ||
| 860 | case EXYNOS_DRM_DEGREE_180: | ||
| 861 | cfg |= GSC_IN_ROT_180; | ||
| 862 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 863 | cfg &= ~GSC_IN_ROT_XFLIP; | ||
| 864 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 865 | cfg &= ~GSC_IN_ROT_YFLIP; | ||
| 866 | break; | ||
| 867 | case EXYNOS_DRM_DEGREE_270: | ||
| 868 | cfg |= GSC_IN_ROT_270; | ||
| 869 | if (flip & EXYNOS_DRM_FLIP_VERTICAL) | ||
| 870 | cfg &= ~GSC_IN_ROT_XFLIP; | ||
| 871 | if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) | ||
| 872 | cfg &= ~GSC_IN_ROT_YFLIP; | ||
| 873 | break; | ||
| 874 | default: | ||
| 875 | dev_err(ippdrv->dev, "invalid degree value %d.\n", degree); | ||
| 876 | return -EINVAL; | ||
| 877 | } | ||
| 878 | |||
| 879 | gsc_write(cfg, GSC_IN_CON); | ||
| 880 | |||
| 881 | ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0; | ||
| 882 | *swap = ctx->rotation; | ||
| 883 | |||
| 884 | return 0; | ||
| 885 | } | 691 | } |
| 886 | 692 | ||
| 887 | static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio) | 693 | static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio) |
| @@ -919,9 +725,9 @@ static void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *shfactor) · 31 unmodified lines | |||
| 919 | } | 725 | } |
| 920 | 726 | ||
| 921 | static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, | 727 | static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, |
| 922 | struct drm_exynos_pos *src, struct drm_exynos_pos *dst) | 728 | struct drm_exynos_ipp_task_rect *src, |
| 729 | struct drm_exynos_ipp_task_rect *dst) | ||
| 923 | { | 730 | { |
| 924 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 925 | u32 cfg; | 731 | u32 cfg; |
| 926 | u32 src_w, src_h, dst_w, dst_h; | 732 | u32 src_w, src_h, dst_w, dst_h; |
| 927 | int ret = 0; | 733 | int ret = 0; |
| @@ -939,13 +745,13 @@ static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, · 11 unmodified lines | |||
| 939 | 745 | ||
| 940 | ret = gsc_get_ratio_shift(src_w, dst_w, &sc->pre_hratio); | 746 | ret = gsc_get_ratio_shift(src_w, dst_w, &sc->pre_hratio); |
| 941 | if (ret) { | 747 | if (ret) { |
| 942 | dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); | 748 | dev_err(ctx->dev, "failed to get ratio horizontal.\n"); |
| 943 | return ret; | 749 | return ret; |
| 944 | } | 750 | } |
| 945 | 751 | ||
| 946 | ret = gsc_get_ratio_shift(src_h, dst_h, &sc->pre_vratio); | 752 | ret = gsc_get_ratio_shift(src_h, dst_h, &sc->pre_vratio); |
| 947 | if (ret) { | 753 | if (ret) { |
| 948 | dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); | 754 | dev_err(ctx->dev, "failed to get ratio vertical.\n"); |
| 949 | return ret; | 755 | return ret; |
| 950 | } | 756 | } |
| 951 | 757 | ||
| @@ -1039,47 +845,37 @@ static void gsc_set_scaler(struct gsc_context *ctx, struct gsc_scaler *sc) · 87 unmodified lines | |||
| 1039 | gsc_write(cfg, GSC_MAIN_V_RATIO); | 845 | gsc_write(cfg, GSC_MAIN_V_RATIO); |
| 1040 | } | 846 | } |
| 1041 | 847 | ||
| 1042 | static int gsc_dst_set_size(struct device *dev, int swap, | 848 | static void gsc_dst_set_size(struct gsc_context *ctx, |
| 1043 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) | 849 | struct exynos_drm_ipp_buffer *buf) |
| 1044 | { | 850 | { |
| 1045 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1046 | struct drm_exynos_pos img_pos = *pos; | ||
| 1047 | struct gsc_scaler *sc = &ctx->sc; | 851 | struct gsc_scaler *sc = &ctx->sc; |
| 1048 | u32 cfg; | 852 | u32 cfg; |
| 1049 | 853 | ||
| 1050 | DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n", | ||
| 1051 | swap, pos->x, pos->y, pos->w, pos->h); | ||
| 1052 | |||
| 1053 | if (swap) { | ||
| 1054 | img_pos.w = pos->h; | ||
| 1055 | img_pos.h = pos->w; | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | /* pixel offset */ | 854 | /* pixel offset */ |
| 1059 | cfg = (GSC_DSTIMG_OFFSET_X(pos->x) | | 855 | cfg = (GSC_DSTIMG_OFFSET_X(buf->rect.x) | |
| 1060 | GSC_DSTIMG_OFFSET_Y(pos->y)); | 856 | GSC_DSTIMG_OFFSET_Y(buf->rect.y)); |
| 1061 | gsc_write(cfg, GSC_DSTIMG_OFFSET); | 857 | gsc_write(cfg, GSC_DSTIMG_OFFSET); |
| 1062 | 858 | ||
| 1063 | /* scaled size */ | 859 | /* scaled size */ |
| 1064 | cfg = (GSC_SCALED_WIDTH(img_pos.w) | GSC_SCALED_HEIGHT(img_pos.h)); | 860 | if (ctx->rotation) |
| 861 | cfg = (GSC_SCALED_WIDTH(buf->rect.h) | | ||
| 862 | GSC_SCALED_HEIGHT(buf->rect.w)); | ||
| 863 | else | ||
| 864 | cfg = (GSC_SCALED_WIDTH(buf->rect.w) | | ||
| 865 | GSC_SCALED_HEIGHT(buf->rect.h)); | ||
| 1065 | gsc_write(cfg, GSC_SCALED_SIZE); | 866 | gsc_write(cfg, GSC_SCALED_SIZE); |
| 1066 | 867 | ||
| 1067 | DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize); | ||
| 1068 | |||
| 1069 | /* original size */ | 868 | /* original size */ |
| 1070 | cfg = gsc_read(GSC_DSTIMG_SIZE); | 869 | cfg = gsc_read(GSC_DSTIMG_SIZE); |
| 1071 | cfg &= ~(GSC_DSTIMG_HEIGHT_MASK | | 870 | cfg &= ~(GSC_DSTIMG_HEIGHT_MASK | GSC_DSTIMG_WIDTH_MASK); |
| 1072 | GSC_DSTIMG_WIDTH_MASK); | 871 | cfg |= GSC_DSTIMG_WIDTH(buf->buf.width) | |
| 1073 | cfg |= (GSC_DSTIMG_WIDTH(sz->hsize) | | 872 | GSC_DSTIMG_HEIGHT(buf->buf.height); |
| 1074 | GSC_DSTIMG_HEIGHT(sz->vsize)); | ||
| 1075 | gsc_write(cfg, GSC_DSTIMG_SIZE); | 873 | gsc_write(cfg, GSC_DSTIMG_SIZE); |
| 1076 | 874 | ||
| 1077 | cfg = gsc_read(GSC_OUT_CON); | 875 | cfg = gsc_read(GSC_OUT_CON); |
| 1078 | cfg &= ~GSC_OUT_RGB_TYPE_MASK; | 876 | cfg &= ~GSC_OUT_RGB_TYPE_MASK; |
| 1079 | 877 | ||
| 1080 | DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range); | 878 | if (buf->rect.w >= GSC_WIDTH_ITU_709) |
| 1081 | |||
| 1082 | if (pos->w >= GSC_WIDTH_ITU_709) | ||
| 1083 | if (sc->range) | 879 | if (sc->range) |
| 1084 | cfg |= GSC_OUT_RGB_HD_WIDE; | 880 | cfg |= GSC_OUT_RGB_HD_WIDE; |
| 1085 | else | 881 | else |
| @@ -1091,8 +887,6 @@ static int gsc_dst_set_size(struct device *dev, int swap, · 5 unmodified lines | |||
| 1091 | cfg |= GSC_OUT_RGB_SD_NARROW; | 887 | cfg |= GSC_OUT_RGB_SD_NARROW; |
| 1092 | 888 | ||
| 1093 | gsc_write(cfg, GSC_OUT_CON); | 889 | gsc_write(cfg, GSC_OUT_CON); |
| 1094 | |||
| 1095 | return 0; | ||
| 1096 | } | 890 | } |
| 1097 | 891 | ||
| 1098 | static int gsc_dst_get_buf_seq(struct gsc_context *ctx) | 892 | static int gsc_dst_get_buf_seq(struct gsc_context *ctx) |
| @@ -1111,35 +905,16 @@ static int gsc_dst_get_buf_seq(struct gsc_context *ctx) · 12 unmodified lines | |||
| 1111 | return buf_num; | 905 | return buf_num; |
| 1112 | } | 906 | } |
| 1113 | 907 | ||
| 1114 | static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, | 908 | static void gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, |
| 1115 | enum drm_exynos_ipp_buf_type buf_type) | 909 | bool enqueue) |
| 1116 | { | 910 | { |
| 1117 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | 911 | bool masked = !enqueue; |
| 1118 | bool masked; | ||
| 1119 | u32 cfg; | 912 | u32 cfg; |
| 1120 | u32 mask = 0x00000001 << buf_id; | 913 | u32 mask = 0x00000001 << buf_id; |
| 1121 | int ret = 0; | ||
| 1122 | |||
| 1123 | DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); | ||
| 1124 | |||
| 1125 | mutex_lock(&ctx->lock); | ||
| 1126 | 914 | ||
| 1127 | /* mask register set */ | 915 | /* mask register set */ |
| 1128 | cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); | 916 | cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); |
| 1129 | 917 | ||
| 1130 | switch (buf_type) { | ||
| 1131 | case IPP_BUF_ENQUEUE: | ||
| 1132 | masked = false; | ||
| 1133 | break; | ||
| 1134 | case IPP_BUF_DEQUEUE: | ||
| 1135 | masked = true; | ||
| 1136 | break; | ||
| 1137 | default: | ||
| 1138 | dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); | ||
| 1139 | ret = -EINVAL; | ||
| 1140 | goto err_unlock; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | /* sequence id */ | 918 | /* sequence id */ |
| 1144 | cfg &= ~mask; | 919 | cfg &= ~mask; |
| 1145 | cfg |= masked << buf_id; | 920 | cfg |= masked << buf_id; |
| @@ -1148,94 +923,29 @@ static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, · 2 unmodified lines | |||
| 1148 | gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK); | 923 | gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK); |
| 1149 | 924 | ||
| 1150 | /* interrupt enable */ | 925 | /* interrupt enable */ |
| 1151 | if (buf_type == IPP_BUF_ENQUEUE && | 926 | if (enqueue && gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START) |
| 1152 | gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START) | ||
| 1153 | gsc_handle_irq(ctx, true, false, true); | 927 | gsc_handle_irq(ctx, true, false, true); |
| 1154 | 928 | ||
| 1155 | /* interrupt disable */ | 929 | /* interrupt disable */ |
| 1156 | if (buf_type == IPP_BUF_DEQUEUE && | 930 | if (!enqueue && gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP) |
| 1157 | gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP) | ||
| 1158 | gsc_handle_irq(ctx, false, false, true); | 931 | gsc_handle_irq(ctx, false, false, true); |
| 1159 | |||
| 1160 | err_unlock: | ||
| 1161 | mutex_unlock(&ctx->lock); | ||
| 1162 | return ret; | ||
| 1163 | } | 932 | } |
| 1164 | 933 | ||
| 1165 | static int gsc_dst_set_addr(struct device *dev, | 934 | static void gsc_dst_set_addr(struct gsc_context *ctx, |
| 1166 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | 935 | u32 buf_id, struct exynos_drm_ipp_buffer *buf) |
| 1167 | enum drm_exynos_ipp_buf_type buf_type) | ||
| 1168 | { | 936 | { |
| 1169 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1170 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1171 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | ||
| 1172 | struct drm_exynos_ipp_property *property; | ||
| 1173 | |||
| 1174 | if (!c_node) { | ||
| 1175 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1176 | return -EFAULT; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | property = &c_node->property; | ||
| 1180 | |||
| 1181 | DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", | ||
| 1182 | property->prop_id, buf_id, buf_type); | ||
| 1183 | |||
| 1184 | if (buf_id > GSC_MAX_DST) { | ||
| 1185 | dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id); | ||
| 1186 | return -EINVAL; | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | /* address register set */ | 937 | /* address register set */ |
| 1190 | switch (buf_type) { | 938 | gsc_write(buf->dma_addr[0], GSC_OUT_BASE_ADDR_Y(buf_id)); |
| 1191 | case IPP_BUF_ENQUEUE: | 939 | gsc_write(buf->dma_addr[1], GSC_OUT_BASE_ADDR_CB(buf_id)); |
| 1192 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], | 940 | gsc_write(buf->dma_addr[2], GSC_OUT_BASE_ADDR_CR(buf_id)); |
| 1193 | GSC_OUT_BASE_ADDR_Y(buf_id)); | ||
| 1194 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], | ||
| 1195 | GSC_OUT_BASE_ADDR_CB(buf_id)); | ||
| 1196 | gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], | ||
| 1197 | GSC_OUT_BASE_ADDR_CR(buf_id)); | ||
| 1198 | break; | ||
| 1199 | case IPP_BUF_DEQUEUE: | ||
| 1200 | gsc_write(0x0, GSC_OUT_BASE_ADDR_Y(buf_id)); | ||
| 1201 | gsc_write(0x0, GSC_OUT_BASE_ADDR_CB(buf_id)); | ||
| 1202 | gsc_write(0x0, GSC_OUT_BASE_ADDR_CR(buf_id)); | ||
| 1203 | break; | ||
| 1204 | default: | ||
| 1205 | /* bypass */ | ||
| 1206 | break; | ||
| 1207 | } | ||
| 1208 | 941 | ||
| 1209 | return gsc_dst_set_buf_seq(ctx, buf_id, buf_type); | 942 | gsc_dst_set_buf_seq(ctx, buf_id, true); |
| 1210 | } | ||
| 1211 | |||
| 1212 | static struct exynos_drm_ipp_ops gsc_dst_ops = { | ||
| 1213 | .set_fmt = gsc_dst_set_fmt, | ||
| 1214 | .set_transf = gsc_dst_set_transf, | ||
| 1215 | .set_size = gsc_dst_set_size, | ||
| 1216 | .set_addr = gsc_dst_set_addr, | ||
| 1217 | }; | ||
| 1218 | |||
| 1219 | static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable) | ||
| 1220 | { | ||
| 1221 | DRM_DEBUG_KMS("enable[%d]\n", enable); | ||
| 1222 | |||
| 1223 | if (enable) { | ||
| 1224 | clk_prepare_enable(ctx->gsc_clk); | ||
| 1225 | ctx->suspended = false; | ||
| 1226 | } else { | ||
| 1227 | clk_disable_unprepare(ctx->gsc_clk); | ||
| 1228 | ctx->suspended = true; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | return 0; | ||
| 1232 | } | 943 | } |
| 1233 | 944 | ||
| 1234 | static int gsc_get_src_buf_index(struct gsc_context *ctx) | 945 | static int gsc_get_src_buf_index(struct gsc_context *ctx) |
| 1235 | { | 946 | { |
| 1236 | u32 cfg, curr_index, i; | 947 | u32 cfg, curr_index, i; |
| 1237 | u32 buf_id = GSC_MAX_SRC; | 948 | u32 buf_id = GSC_MAX_SRC; |
| 1238 | int ret; | ||
| 1239 | 949 | ||
| 1240 | DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); | 950 | DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); |
| 1241 | 951 | ||
| @@ -1249,19 +959,15 @@ static int gsc_get_src_buf_index(struct gsc_context *ctx) · 7 unmodified lines | |||
| 1249 | } | 959 | } |
| 1250 | } | 960 | } |
| 1251 | 961 | ||
| 962 | DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, | ||
| 963 | curr_index, buf_id); | ||
| 964 | |||
| 1252 | if (buf_id == GSC_MAX_SRC) { | 965 | if (buf_id == GSC_MAX_SRC) { |
| 1253 | DRM_ERROR("failed to get in buffer index.\n"); | 966 | DRM_ERROR("failed to get in buffer index.\n"); |
| 1254 | return -EINVAL; | 967 | return -EINVAL; |
| 1255 | } | 968 | } |
| 1256 | 969 | ||
| 1257 | ret = gsc_src_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); | 970 | gsc_src_set_buf_seq(ctx, buf_id, false); |
| 1258 | if (ret < 0) { | ||
| 1259 | DRM_ERROR("failed to dequeue.\n"); | ||
| 1260 | return ret; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, | ||
| 1264 | curr_index, buf_id); | ||
| 1265 | 971 | ||
| 1266 | return buf_id; | 972 | return buf_id; |
| 1267 | } | 973 | } |
| @@ -1270,7 +976,6 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx) · 2 unmodified lines | |||
| 1270 | { | 976 | { |
| 1271 | u32 cfg, curr_index, i; | 977 | u32 cfg, curr_index, i; |
| 1272 | u32 buf_id = GSC_MAX_DST; | 978 | u32 buf_id = GSC_MAX_DST; |
| 1273 | int ret; | ||
| 1274 | 979 | ||
| 1275 | DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); | 980 | DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); |
| 1276 | 981 | ||
| @@ -1289,11 +994,7 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx) · 12 unmodified lines | |||
| 1289 | return -EINVAL; | 994 | return -EINVAL; |
| 1290 | } | 995 | } |
| 1291 | 996 | ||
| 1292 | ret = gsc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); | 997 | gsc_dst_set_buf_seq(ctx, buf_id, false); |
| 1293 | if (ret < 0) { | ||
| 1294 | DRM_ERROR("failed to dequeue.\n"); | ||
| 1295 | return ret; | ||
| 1296 | } | ||
| 1297 | 998 | ||
| 1298 | DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, | 999 | DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, |
| 1299 | curr_index, buf_id); | 1000 | curr_index, buf_id); |
| @@ -1304,215 +1005,55 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx) · 4 unmodified lines | |||
| 1304 | static irqreturn_t gsc_irq_handler(int irq, void *dev_id) | 1005 | static irqreturn_t gsc_irq_handler(int irq, void *dev_id) |
| 1305 | { | 1006 | { |
| 1306 | struct gsc_context *ctx = dev_id; | 1007 | struct gsc_context *ctx = dev_id; |
| 1307 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1308 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | ||
| 1309 | struct drm_exynos_ipp_event_work *event_work = | ||
| 1310 | c_node->event_work; | ||
| 1311 | u32 status; | 1008 | u32 status; |
| 1312 | int buf_id[EXYNOS_DRM_OPS_MAX]; | 1009 | int err = 0; |
| 1313 | 1010 | ||
| 1314 | DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); | 1011 | DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); |
| 1315 | 1012 | ||
| 1316 | status = gsc_read(GSC_IRQ); | 1013 | status = gsc_read(GSC_IRQ); |
| 1317 | if (status & GSC_IRQ_STATUS_OR_IRQ) { | 1014 | if (status & GSC_IRQ_STATUS_OR_IRQ) { |
| 1318 | dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n", | 1015 | dev_err(ctx->dev, "occurred overflow at %d, status 0x%x.\n", |
| 1319 | ctx->id, status); | 1016 | ctx->id, status); |
| 1320 | return IRQ_NONE; | 1017 | err = -EINVAL; |
| 1321 | } | 1018 | } |
| 1322 | 1019 | ||
| 1323 | if (status & GSC_IRQ_STATUS_OR_FRM_DONE) { | 1020 | if (status & GSC_IRQ_STATUS_OR_FRM_DONE) { |
| 1324 | dev_dbg(ippdrv->dev, "occurred frame done at %d, status 0x%x.\n", | 1021 | int src_buf_id, dst_buf_id; |
| 1325 | ctx->id, status); | ||
| 1326 | |||
| 1327 | buf_id[EXYNOS_DRM_OPS_SRC] = gsc_get_src_buf_index(ctx); | ||
| 1328 | if (buf_id[EXYNOS_DRM_OPS_SRC] < 0) | ||
| 1329 | return IRQ_HANDLED; | ||
| 1330 | |||
| 1331 | buf_id[EXYNOS_DRM_OPS_DST] = gsc_get_dst_buf_index(ctx); | ||
| 1332 | if (buf_id[EXYNOS_DRM_OPS_DST] < 0) | ||
| 1333 | return IRQ_HANDLED; | ||
| 1334 | |||
| 1335 | DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n", | ||
| 1336 | buf_id[EXYNOS_DRM_OPS_SRC], buf_id[EXYNOS_DRM_OPS_DST]); | ||
| 1337 | |||
| 1338 | event_work->ippdrv = ippdrv; | ||
| 1339 | event_work->buf_id[EXYNOS_DRM_OPS_SRC] = | ||
| 1340 | buf_id[EXYNOS_DRM_OPS_SRC]; | ||
| 1341 | event_work->buf_id[EXYNOS_DRM_OPS_DST] = | ||
| 1342 | buf_id[EXYNOS_DRM_OPS_DST]; | ||
| 1343 | queue_work(ippdrv->event_workq, &event_work->work); | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | return IRQ_HANDLED; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) | ||
| 1350 | { | ||
| 1351 | struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list; | ||
| 1352 | |||
| 1353 | prop_list->version = 1; | ||
| 1354 | prop_list->writeback = 1; | ||
| 1355 | prop_list->refresh_min = GSC_REFRESH_MIN; | ||
| 1356 | prop_list->refresh_max = GSC_REFRESH_MAX; | ||
| 1357 | prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) | | ||
| 1358 | (1 << EXYNOS_DRM_FLIP_HORIZONTAL); | ||
| 1359 | prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | | ||
| 1360 | (1 << EXYNOS_DRM_DEGREE_90) | | ||
| 1361 | (1 << EXYNOS_DRM_DEGREE_180) | | ||
| 1362 | (1 << EXYNOS_DRM_DEGREE_270); | ||
| 1363 | prop_list->csc = 1; | ||
| 1364 | prop_list->crop = 1; | ||
| 1365 | prop_list->crop_max.hsize = GSC_CROP_MAX; | ||
| 1366 | prop_list->crop_max.vsize = GSC_CROP_MAX; | ||
| 1367 | prop_list->crop_min.hsize = GSC_CROP_MIN; | ||
| 1368 | prop_list->crop_min.vsize = GSC_CROP_MIN; | ||
| 1369 | prop_list->scale = 1; | ||
| 1370 | prop_list->scale_max.hsize = GSC_SCALE_MAX; | ||
| 1371 | prop_list->scale_max.vsize = GSC_SCALE_MAX; | ||
| 1372 | prop_list->scale_min.hsize = GSC_SCALE_MIN; | ||
| 1373 | prop_list->scale_min.vsize = GSC_SCALE_MIN; | ||
| 1374 | |||
| 1375 | return 0; | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | static inline bool gsc_check_drm_flip(enum drm_exynos_flip flip) | ||
| 1379 | { | ||
| 1380 | switch (flip) { | ||
| 1381 | case EXYNOS_DRM_FLIP_NONE: | ||
| 1382 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 1383 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 1384 | case EXYNOS_DRM_FLIP_BOTH: | ||
| 1385 | return true; | ||
| 1386 | default: | ||
| 1387 | DRM_DEBUG_KMS("invalid flip\n"); | ||
| 1388 | return false; | ||
| 1389 | } | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | static int gsc_ippdrv_check_property(struct device *dev, | ||
| 1393 | struct drm_exynos_ipp_property *property) | ||
| 1394 | { | ||
| 1395 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1396 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1397 | struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list; | ||
| 1398 | struct drm_exynos_ipp_config *config; | ||
| 1399 | struct drm_exynos_pos *pos; | ||
| 1400 | struct drm_exynos_sz *sz; | ||
| 1401 | bool swap; | ||
| 1402 | int i; | ||
| 1403 | |||
| 1404 | for_each_ipp_ops(i) { | ||
| 1405 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1406 | (property->cmd == IPP_CMD_WB)) | ||
| 1407 | continue; | ||
| 1408 | 1022 | ||
| 1409 | config = &property->config[i]; | 1023 | dev_dbg(ctx->dev, "occurred frame done at %d, status 0x%x.\n", |
| 1410 | pos = &config->pos; | 1024 | ctx->id, status); |
| 1411 | sz = &config->sz; | ||
| 1412 | |||
| 1413 | /* check for flip */ | ||
| 1414 | if (!gsc_check_drm_flip(config->flip)) { | ||
| 1415 | DRM_ERROR("invalid flip.\n"); | ||
| 1416 | goto err_property; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | /* check for degree */ | ||
| 1420 | switch (config->degree) { | ||
| 1421 | case EXYNOS_DRM_DEGREE_90: | ||
| 1422 | case EXYNOS_DRM_DEGREE_270: | ||
| 1423 | swap = true; | ||
| 1424 | break; | ||
| 1425 | case EXYNOS_DRM_DEGREE_0: | ||
| 1426 | case EXYNOS_DRM_DEGREE_180: | ||
| 1427 | swap = false; | ||
| 1428 | break; | ||
| 1429 | default: | ||
| 1430 | DRM_ERROR("invalid degree.\n"); | ||
| 1431 | goto err_property; | ||
| 1432 | } | ||
| 1433 | 1025 | ||
| 1434 | /* check for buffer bound */ | 1026 | src_buf_id = gsc_get_src_buf_index(ctx); |
| 1435 | if ((pos->x + pos->w > sz->hsize) || | 1027 | dst_buf_id = gsc_get_dst_buf_index(ctx); |
| 1436 | (pos->y + pos->h > sz->vsize)) { | ||
| 1437 | DRM_ERROR("out of buf bound.\n"); | ||
| 1438 | goto err_property; | ||
| 1439 | } | ||
| 1440 | 1028 | ||
| 1441 | /* check for crop */ | 1029 | DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n", src_buf_id, |
| 1442 | if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { | 1030 | dst_buf_id); |
| 1443 | if (swap) { | ||
| 1444 | if ((pos->h < pp->crop_min.hsize) || | ||
| 1445 | (sz->vsize > pp->crop_max.hsize) || | ||
| 1446 | (pos->w < pp->crop_min.vsize) || | ||
| 1447 | (sz->hsize > pp->crop_max.vsize)) { | ||
| 1448 | DRM_ERROR("out of crop size.\n"); | ||
| 1449 | goto err_property; | ||
| 1450 | } | ||
| 1451 | } else { | ||
| 1452 | if ((pos->w < pp->crop_min.hsize) || | ||
| 1453 | (sz->hsize > pp->crop_max.hsize) || | ||
| 1454 | (pos->h < pp->crop_min.vsize) || | ||
| 1455 | (sz->vsize > pp->crop_max.vsize)) { | ||
| 1456 | DRM_ERROR("out of crop size.\n"); | ||
| 1457 | goto err_property; | ||
| 1458 | } | ||
| 1459 | } | ||
| 1460 | } | ||
| 1461 | 1031 | ||
| 1462 | /* check for scale */ | 1032 | if (src_buf_id < 0 || dst_buf_id < 0) |
| 1463 | if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { | 1033 | err = -EINVAL; |
| 1464 | if (swap) { | ||
| 1465 | if ((pos->h < pp->scale_min.hsize) || | ||
| 1466 | (sz->vsize > pp->scale_max.hsize) || | ||
| 1467 | (pos->w < pp->scale_min.vsize) || | ||
| 1468 | (sz->hsize > pp->scale_max.vsize)) { | ||
| 1469 | DRM_ERROR("out of scale size.\n"); | ||
| 1470 | goto err_property; | ||
| 1471 | } | ||
| 1472 | } else { | ||
| 1473 | if ((pos->w < pp->scale_min.hsize) || | ||
| 1474 | (sz->hsize > pp->scale_max.hsize) || | ||
| 1475 | (pos->h < pp->scale_min.vsize) || | ||
| 1476 | (sz->vsize > pp->scale_max.vsize)) { | ||
| 1477 | DRM_ERROR("out of scale size.\n"); | ||
| 1478 | goto err_property; | ||
| 1479 | } | ||
| 1480 | } | ||
| 1481 | } | ||
| 1482 | } | 1034 | } |
| 1483 | 1035 | ||
| 1484 | return 0; | 1036 | if (ctx->task) { |
| 1485 | 1037 | struct exynos_drm_ipp_task *task = ctx->task; | |
| 1486 | err_property: | ||
| 1487 | for_each_ipp_ops(i) { | ||
| 1488 | if ((i == EXYNOS_DRM_OPS_SRC) && | ||
| 1489 | (property->cmd == IPP_CMD_WB)) | ||
| 1490 | continue; | ||
| 1491 | 1038 | ||
| 1492 | config = &property->config[i]; | 1039 | ctx->task = NULL; |
| 1493 | pos = &config->pos; | 1040 | pm_runtime_mark_last_busy(ctx->dev); |
| 1494 | sz = &config->sz; | 1041 | pm_runtime_put_autosuspend(ctx->dev); |
| 1495 | 1042 | exynos_drm_ipp_task_done(task, err); | |
| 1496 | DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", | ||
| 1497 | i ? "dst" : "src", config->flip, config->degree, | ||
| 1498 | pos->x, pos->y, pos->w, pos->h, | ||
| 1499 | sz->hsize, sz->vsize); | ||
| 1500 | } | 1043 | } |
| 1501 | 1044 | ||
| 1502 | return -EINVAL; | 1045 | return IRQ_HANDLED; |
| 1503 | } | 1046 | } |
| 1504 | 1047 | ||
| 1505 | 1048 | static int gsc_reset(struct gsc_context *ctx) | |
| 1506 | static int gsc_ippdrv_reset(struct device *dev) | ||
| 1507 | { | 1049 | { |
| 1508 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1509 | struct gsc_scaler *sc = &ctx->sc; | 1050 | struct gsc_scaler *sc = &ctx->sc; |
| 1510 | int ret; | 1051 | int ret; |
| 1511 | 1052 | ||
| 1512 | /* reset h/w block */ | 1053 | /* reset h/w block */ |
| 1513 | ret = gsc_sw_reset(ctx); | 1054 | ret = gsc_sw_reset(ctx); |
| 1514 | if (ret < 0) { | 1055 | if (ret < 0) { |
| 1515 | dev_err(dev, "failed to reset hardware.\n"); | 1056 | dev_err(ctx->dev, "failed to reset hardware.\n"); |
| 1516 | return ret; | 1057 | return ret; |
| 1517 | } | 1058 | } |
| 1518 | 1059 | ||
| @@ -1523,166 +1064,172 @@ static int gsc_ippdrv_reset(struct device *dev) · 4 unmodified lines | |||
| 1523 | return 0; | 1064 | return 0; |
| 1524 | } | 1065 | } |
| 1525 | 1066 | ||
| 1526 | static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) | 1067 | static void gsc_start(struct gsc_context *ctx) |
| 1527 | { | 1068 | { |
| 1528 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1529 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1530 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | ||
| 1531 | struct drm_exynos_ipp_property *property; | ||
| 1532 | struct drm_exynos_ipp_config *config; | ||
| 1533 | struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; | ||
| 1534 | struct drm_exynos_ipp_set_wb set_wb; | ||
| 1535 | u32 cfg; | 1069 | u32 cfg; |
| 1536 | int ret, i; | ||
| 1537 | |||
| 1538 | DRM_DEBUG_KMS("cmd[%d]\n", cmd); | ||
| 1539 | |||
| 1540 | if (!c_node) { | ||
| 1541 | DRM_ERROR("failed to get c_node.\n"); | ||
| 1542 | return -EINVAL; | ||
| 1543 | } | ||
| 1544 | |||
| 1545 | property = &c_node->property; | ||
| 1546 | 1070 | ||
| 1547 | gsc_handle_irq(ctx, true, false, true); | 1071 | gsc_handle_irq(ctx, true, false, true); |
| 1548 | 1072 | ||
| 1549 | for_each_ipp_ops(i) { | 1073 | /* enable one shot */ |
| 1550 | config = &property->config[i]; | 1074 | cfg = gsc_read(GSC_ENABLE); |
| 1551 | img_pos[i] = config->pos; | 1075 | cfg &= ~(GSC_ENABLE_ON_CLEAR_MASK | |
| 1552 | } | 1076 | GSC_ENABLE_CLK_GATE_MODE_MASK); |
| 1077 | cfg |= GSC_ENABLE_ON_CLEAR_ONESHOT; | ||
| 1078 | gsc_write(cfg, GSC_ENABLE); | ||
| 1553 | 1079 | ||
| 1554 | switch (cmd) { | 1080 | /* src dma memory */ |
| 1555 | case IPP_CMD_M2M: | 1081 | cfg = gsc_read(GSC_IN_CON); |
| 1556 | /* enable one shot */ | 1082 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); |
| 1557 | cfg = gsc_read(GSC_ENABLE); | 1083 | cfg |= GSC_IN_PATH_MEMORY; |
| 1558 | cfg &= ~(GSC_ENABLE_ON_CLEAR_MASK | | 1084 | gsc_write(cfg, GSC_IN_CON); |
| 1559 | GSC_ENABLE_CLK_GATE_MODE_MASK); | ||
| 1560 | cfg |= GSC_ENABLE_ON_CLEAR_ONESHOT; | ||
| 1561 | gsc_write(cfg, GSC_ENABLE); | ||
| 1562 | |||
| 1563 | /* src dma memory */ | ||
| 1564 | cfg = gsc_read(GSC_IN_CON); | ||
| 1565 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); | ||
| 1566 | cfg |= GSC_IN_PATH_MEMORY; | ||
| 1567 | gsc_write(cfg, GSC_IN_CON); | ||
| 1568 | |||
| 1569 | /* dst dma memory */ | ||
| 1570 | cfg = gsc_read(GSC_OUT_CON); | ||
| 1571 | cfg |= GSC_OUT_PATH_MEMORY; | ||
| 1572 | gsc_write(cfg, GSC_OUT_CON); | ||
| 1573 | break; | ||
| 1574 | case IPP_CMD_WB: | ||
| 1575 | set_wb.enable = 1; | ||
| 1576 | set_wb.refresh = property->refresh_rate; | ||
| 1577 | gsc_set_gscblk_fimd_wb(ctx, set_wb.enable); | ||
| 1578 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | ||
| 1579 | |||
| 1580 | /* src local path */ | ||
| 1581 | cfg = gsc_read(GSC_IN_CON); | ||
| 1582 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); | ||
| 1583 | cfg |= (GSC_IN_PATH_LOCAL | GSC_IN_LOCAL_FIMD_WB); | ||
| 1584 | gsc_write(cfg, GSC_IN_CON); | ||
| 1585 | |||
| 1586 | /* dst dma memory */ | ||
| 1587 | cfg = gsc_read(GSC_OUT_CON); | ||
| 1588 | cfg |= GSC_OUT_PATH_MEMORY; | ||
| 1589 | gsc_write(cfg, GSC_OUT_CON); | ||
| 1590 | break; | ||
| 1591 | case IPP_CMD_OUTPUT: | ||
| 1592 | /* src dma memory */ | ||
| 1593 | cfg = gsc_read(GSC_IN_CON); | ||
| 1594 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); | ||
| 1595 | cfg |= GSC_IN_PATH_MEMORY; | ||
| 1596 | gsc_write(cfg, GSC_IN_CON); | ||
| 1597 | |||
| 1598 | /* dst local path */ | ||
| 1599 | cfg = gsc_read(GSC_OUT_CON); | ||
| 1600 | cfg |= GSC_OUT_PATH_MEMORY; | ||
| 1601 | gsc_write(cfg, GSC_OUT_CON); | ||
| 1602 | break; | ||
| 1603 | default: | ||
| 1604 | ret = -EINVAL; | ||
| 1605 | dev_err(dev, "invalid operations.\n"); | ||
| 1606 | return ret; | ||
| 1607 | } | ||
| 1608 | 1085 | ||
| 1609 | ret = gsc_set_prescaler(ctx, &ctx->sc, | 1086 | /* dst dma memory */ |
| 1610 | &img_pos[EXYNOS_DRM_OPS_SRC], | 1087 | cfg = gsc_read(GSC_OUT_CON); |
| 1611 | &img_pos[EXYNOS_DRM_OPS_DST]); | 1088 | cfg |= GSC_OUT_PATH_MEMORY; |
| 1612 | if (ret) { | 1089 | gsc_write(cfg, GSC_OUT_CON); |
| 1613 | dev_err(dev, "failed to set prescaler.\n"); | ||
| 1614 | return ret; | ||
| 1615 | } | ||
| 1616 | 1090 | ||
| 1617 | gsc_set_scaler(ctx, &ctx->sc); | 1091 | gsc_set_scaler(ctx, &ctx->sc); |
| 1618 | 1092 | ||
| 1619 | cfg = gsc_read(GSC_ENABLE); | 1093 | cfg = gsc_read(GSC_ENABLE); |
| 1620 | cfg |= GSC_ENABLE_ON; | 1094 | cfg |= GSC_ENABLE_ON; |
| 1621 | gsc_write(cfg, GSC_ENABLE); | 1095 | gsc_write(cfg, GSC_ENABLE); |
| 1096 | } | ||
| 1097 | |||
| 1098 | static int gsc_commit(struct exynos_drm_ipp *ipp, | ||
| 1099 | struct exynos_drm_ipp_task *task) | ||
| 1100 | { | ||
| 1101 | struct gsc_context *ctx = container_of(ipp, struct gsc_context, ipp); | ||
| 1102 | int ret; | ||
| 1103 | |||
| 1104 | pm_runtime_get_sync(ctx->dev); | ||
| 1105 | ctx->task = task; | ||
| 1106 | |||
| 1107 | ret = gsc_reset(ctx); | ||
| 1108 | if (ret) { | ||
| 1109 | pm_runtime_put_autosuspend(ctx->dev); | ||
| 1110 | ctx->task = NULL; | ||
| 1111 | return ret; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | gsc_src_set_fmt(ctx, task->src.buf.fourcc); | ||
| 1115 | gsc_src_set_transf(ctx, task->transform.rotation); | ||
| 1116 | gsc_src_set_size(ctx, &task->src); | ||
| 1117 | gsc_src_set_addr(ctx, 0, &task->src); | ||
| 1118 | gsc_dst_set_fmt(ctx, task->dst.buf.fourcc); | ||
| 1119 | gsc_dst_set_size(ctx, &task->dst); | ||
| 1120 | gsc_dst_set_addr(ctx, 0, &task->dst); | ||
| 1121 | gsc_set_prescaler(ctx, &ctx->sc, &task->src.rect, &task->dst.rect); | ||
| 1122 | gsc_start(ctx); | ||
| 1622 | 1123 | ||
| 1623 | return 0; | 1124 | return 0; |
| 1624 | } | 1125 | } |
| 1625 | 1126 | ||
| 1626 | static void gsc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) | 1127 | static void gsc_abort(struct exynos_drm_ipp *ipp, |
| 1128 | struct exynos_drm_ipp_task *task) | ||
| 1627 | { | 1129 | { |
| 1628 | struct gsc_context *ctx = get_gsc_context(dev); | 1130 | struct gsc_context *ctx = |
| 1629 | struct drm_exynos_ipp_set_wb set_wb = {0, 0}; | 1131 | container_of(ipp, struct gsc_context, ipp); |
| 1630 | u32 cfg; | ||
| 1631 | 1132 | ||
| 1632 | DRM_DEBUG_KMS("cmd[%d]\n", cmd); | 1133 | gsc_reset(ctx); |
| 1134 | if (ctx->task) { | ||
| 1135 | struct exynos_drm_ipp_task *task = ctx->task; | ||
| 1633 | 1136 | ||
| 1634 | switch (cmd) { | 1137 | ctx->task = NULL; |
| 1635 | case IPP_CMD_M2M: | 1138 | pm_runtime_mark_last_busy(ctx->dev); |
| 1636 | /* bypass */ | 1139 | pm_runtime_put_autosuspend(ctx->dev); |
| 1637 | break; | 1140 | exynos_drm_ipp_task_done(task, -EIO); |
| 1638 | case IPP_CMD_WB: | ||
| 1639 | gsc_set_gscblk_fimd_wb(ctx, set_wb.enable); | ||
| 1640 | exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); | ||
| 1641 | break; | ||
| 1642 | case IPP_CMD_OUTPUT: | ||
| 1643 | default: | ||
| 1644 | dev_err(dev, "invalid operations.\n"); | ||
| 1645 | break; | ||
| 1646 | } | 1141 | } |
| 1142 | } | ||
| 1647 | 1143 | ||
| 1648 | gsc_handle_irq(ctx, false, false, true); | 1144 | static struct exynos_drm_ipp_funcs ipp_funcs = { |
| 1145 | .commit = gsc_commit, | ||
| 1146 | .abort = gsc_abort, | ||
| 1147 | }; | ||
| 1649 | 1148 | ||
| 1650 | /* reset sequence */ | 1149 | static int gsc_bind(struct device *dev, struct device *master, void *data) |
| 1651 | gsc_write(0xff, GSC_OUT_BASE_ADDR_Y_MASK); | 1150 | { |
| 1652 | gsc_write(0xff, GSC_OUT_BASE_ADDR_CB_MASK); | 1151 | struct gsc_context *ctx = dev_get_drvdata(dev); |
| 1653 | gsc_write(0xff, GSC_OUT_BASE_ADDR_CR_MASK); | 1152 | struct drm_device *drm_dev = data; |
| 1153 | struct exynos_drm_ipp *ipp = &ctx->ipp; | ||
| 1654 | 1154 | ||
| 1655 | cfg = gsc_read(GSC_ENABLE); | 1155 | ctx->drm_dev = drm_dev; |
| 1656 | cfg &= ~GSC_ENABLE_ON; | 1156 | drm_iommu_attach_device(drm_dev, dev); |
| 1657 | gsc_write(cfg, GSC_ENABLE); | 1157 | |
| 1158 | exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs, | ||
| 1159 | DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE | | ||
| 1160 | DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT, | ||
| 1161 | ctx->formats, ctx->num_formats, "gsc"); | ||
| 1162 | |||
| 1163 | dev_info(dev, "The exynos gscaler has been probed successfully\n"); | ||
| 1164 | |||
| 1165 | return 0; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | static void gsc_unbind(struct device *dev, struct device *master, | ||
| 1169 | void *data) | ||
| 1170 | { | ||
| 1171 | struct gsc_context *ctx = dev_get_drvdata(dev); | ||
| 1172 | struct drm_device *drm_dev = data; | ||
| 1173 | struct exynos_drm_ipp *ipp = &ctx->ipp; | ||
| 1174 | |||
| 1175 | exynos_drm_ipp_unregister(drm_dev, ipp); | ||
| 1176 | drm_iommu_detach_device(drm_dev, dev); | ||
| 1658 | } | 1177 | } |
| 1659 | 1178 | ||
| 1179 | static const struct component_ops gsc_component_ops = { | ||
| 1180 | .bind = gsc_bind, | ||
| 1181 | .unbind = gsc_unbind, | ||
| 1182 | }; | ||
| 1183 | |||
| 1184 | static const unsigned int gsc_formats[] = { | ||
| 1185 | DRM_FORMAT_ARGB8888, | ||
| 1186 | DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_BGRX8888, | ||
| 1187 | DRM_FORMAT_NV12, DRM_FORMAT_NV16, DRM_FORMAT_NV21, DRM_FORMAT_NV61, | ||
| 1188 | DRM_FORMAT_UYVY, DRM_FORMAT_VYUY, DRM_FORMAT_YUYV, DRM_FORMAT_YVYU, | ||
| 1189 | DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, DRM_FORMAT_YUV422, | ||
| 1190 | }; | ||
| 1191 | |||
| 1660 | static int gsc_probe(struct platform_device *pdev) | 1192 | static int gsc_probe(struct platform_device *pdev) |
| 1661 | { | 1193 | { |
| 1662 | struct device *dev = &pdev->dev; | 1194 | struct device *dev = &pdev->dev; |
| 1195 | struct gsc_driverdata *driver_data; | ||
| 1196 | struct exynos_drm_ipp_formats *formats; | ||
| 1663 | struct gsc_context *ctx; | 1197 | struct gsc_context *ctx; |
| 1664 | struct resource *res; | 1198 | struct resource *res; |
| 1665 | struct exynos_drm_ippdrv *ippdrv; | 1199 | int ret, i; |
| 1666 | int ret; | ||
| 1667 | 1200 | ||
| 1668 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | 1201 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); |
| 1669 | if (!ctx) | 1202 | if (!ctx) |
| 1670 | return -ENOMEM; | 1203 | return -ENOMEM; |
| 1671 | 1204 | ||
| 1672 | if (dev->of_node) { | 1205 | formats = devm_kzalloc(dev, sizeof(*formats) * |
| 1673 | ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, | 1206 | (ARRAY_SIZE(gsc_formats)), GFP_KERNEL); |
| 1674 | "samsung,sysreg"); | 1207 | if (!formats) |
| 1675 | if (IS_ERR(ctx->sysreg)) { | 1208 | return -ENOMEM; |
| 1676 | dev_warn(dev, "failed to get system register.\n"); | 1209 | |
| 1677 | ctx->sysreg = NULL; | 1210 | driver_data = (struct gsc_driverdata *)of_device_get_match_data(dev); |
| 1678 | } | 1211 | ctx->dev = dev; |
| 1212 | ctx->num_clocks = driver_data->num_clocks; | ||
| 1213 | ctx->clk_names = driver_data->clk_names; | ||
| 1214 | |||
| 1215 | for (i = 0; i < ARRAY_SIZE(gsc_formats); i++) { | ||
| 1216 | formats[i].fourcc = gsc_formats[i]; | ||
| 1217 | formats[i].type = DRM_EXYNOS_IPP_FORMAT_SOURCE | | ||
| 1218 | DRM_EXYNOS_IPP_FORMAT_DESTINATION; | ||
| 1219 | formats[i].limits = driver_data->limits; | ||
| 1220 | formats[i].num_limits = driver_data->num_limits; | ||
| 1679 | } | 1221 | } |
| 1222 | ctx->formats = formats; | ||
| 1223 | ctx->num_formats = ARRAY_SIZE(gsc_formats); | ||
| 1680 | 1224 | ||
| 1681 | /* clock control */ | 1225 | /* clock control */ |
| 1682 | ctx->gsc_clk = devm_clk_get(dev, "gscl"); | 1226 | for (i = 0; i < ctx->num_clocks; i++) { |
| 1683 | if (IS_ERR(ctx->gsc_clk)) { | 1227 | ctx->clocks[i] = devm_clk_get(dev, ctx->clk_names[i]); |
| 1684 | dev_err(dev, "failed to get gsc clock.\n"); | 1228 | if (IS_ERR(ctx->clocks[i])) { |
| 1685 | return PTR_ERR(ctx->gsc_clk); | 1229 | dev_err(dev, "failed to get clock: %s\n", |
| 1230 | ctx->clk_names[i]); | ||
| 1231 | return PTR_ERR(ctx->clocks[i]); | ||
| 1232 | } | ||
| 1686 | } | 1233 | } |
| 1687 | 1234 | ||
| 1688 | /* resource memory */ | 1235 | /* resource memory */ |
| @@ -1699,8 +1246,8 @@ static int gsc_probe(struct platform_device *pdev) · 10 unmodified lines | |||
| 1699 | } | 1246 | } |
| 1700 | 1247 | ||
| 1701 | ctx->irq = res->start; | 1248 | ctx->irq = res->start; |
| 1702 | ret = devm_request_threaded_irq(dev, ctx->irq, NULL, gsc_irq_handler, | 1249 | ret = devm_request_irq(dev, ctx->irq, gsc_irq_handler, 0, |
| 1703 | IRQF_ONESHOT, "drm_gsc", ctx); | 1250 | dev_name(dev), ctx); |
| 1704 | if (ret < 0) { | 1251 | if (ret < 0) { |
| 1705 | dev_err(dev, "failed to request irq.\n"); | 1252 | dev_err(dev, "failed to request irq.\n"); |
| 1706 | return ret; | 1253 | return ret; |
| @@ -1709,38 +1256,22 @@ static int gsc_probe(struct platform_device *pdev) · 2 unmodified lines | |||
| 1709 | /* context initailization */ | 1256 | /* context initailization */ |
| 1710 | ctx->id = pdev->id; | 1257 | ctx->id = pdev->id; |
| 1711 | 1258 | ||
| 1712 | ippdrv = &ctx->ippdrv; | ||
| 1713 | ippdrv->dev = dev; | ||
| 1714 | ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &gsc_src_ops; | ||
| 1715 | ippdrv->ops[EXYNOS_DRM_OPS_DST] = &gsc_dst_ops; | ||
| 1716 | ippdrv->check_property = gsc_ippdrv_check_property; | ||
| 1717 | ippdrv->reset = gsc_ippdrv_reset; | ||
| 1718 | ippdrv->start = gsc_ippdrv_start; | ||
| 1719 | ippdrv->stop = gsc_ippdrv_stop; | ||
| 1720 | ret = gsc_init_prop_list(ippdrv); | ||
| 1721 | if (ret < 0) { | ||
| 1722 | dev_err(dev, "failed to init property list.\n"); | ||
| 1723 | return ret; | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | DRM_DEBUG_KMS("id[%d]ippdrv[%pK]\n", ctx->id, ippdrv); | ||
| 1727 | |||
| 1728 | mutex_init(&ctx->lock); | ||
| 1729 | platform_set_drvdata(pdev, ctx); | 1259 | platform_set_drvdata(pdev, ctx); |
| 1730 | 1260 | ||
| 1261 | pm_runtime_use_autosuspend(dev); | ||
| 1262 | pm_runtime_set_autosuspend_delay(dev, GSC_AUTOSUSPEND_DELAY); | ||
| 1731 | pm_runtime_enable(dev); | 1263 | pm_runtime_enable(dev); |
| 1732 | 1264 | ||
| 1733 | ret = exynos_drm_ippdrv_register(ippdrv); | 1265 | ret = component_add(dev, &gsc_component_ops); |
| 1734 | if (ret < 0) { | 1266 | if (ret) |
| 1735 | dev_err(dev, "failed to register drm gsc device.\n"); | 1267 | goto err_pm_dis; |
| 1736 | goto err_ippdrv_register; | ||
| 1737 | } | ||
| 1738 | 1268 | ||
| 1739 | dev_info(dev, "drm gsc registered successfully.\n"); | 1269 | dev_info(dev, "drm gsc registered successfully.\n"); |
| 1740 | 1270 | ||
| 1741 | return 0; | 1271 | return 0; |
| 1742 | 1272 | ||
| 1743 | err_ippdrv_register: | 1273 | err_pm_dis: |
| 1274 | pm_runtime_dont_use_autosuspend(dev); | ||
| 1744 | pm_runtime_disable(dev); | 1275 | pm_runtime_disable(dev); |
| 1745 | return ret; | 1276 | return ret; |
| 1746 | } | 1277 | } |
| @@ -1748,13 +1279,8 @@ err_ippdrv_register: · 1 unmodified line | |||
| 1748 | static int gsc_remove(struct platform_device *pdev) | 1279 | static int gsc_remove(struct platform_device *pdev) |
| 1749 | { | 1280 | { |
| 1750 | struct device *dev = &pdev->dev; | 1281 | struct device *dev = &pdev->dev; |
| 1751 | struct gsc_context *ctx = get_gsc_context(dev); | ||
| 1752 | struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; | ||
| 1753 | 1282 | ||
| 1754 | exynos_drm_ippdrv_unregister(ippdrv); | 1283 | pm_runtime_dont_use_autosuspend(dev); |
| 1755 | mutex_destroy(&ctx->lock); | ||
| 1756 | |||
| 1757 | pm_runtime_set_suspended(dev); | ||
| 1758 | pm_runtime_disable(dev); | 1284 | pm_runtime_disable(dev); |
| 1759 | 1285 | ||
| 1760 | return 0; | 1286 | return 0; |
| @@ -1763,19 +1289,32 @@ static int gsc_remove(struct platform_device *pdev) · 2 unmodified lines | |||
| 1763 | static int __maybe_unused gsc_runtime_suspend(struct device *dev) | 1289 | static int __maybe_unused gsc_runtime_suspend(struct device *dev) |
| 1764 | { | 1290 | { |
| 1765 | struct gsc_context *ctx = get_gsc_context(dev); | 1291 | struct gsc_context *ctx = get_gsc_context(dev); |
| 1292 | int i; | ||
| 1766 | 1293 | ||
| 1767 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); | 1294 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); |
| 1768 | 1295 | ||
| 1769 | return gsc_clk_ctrl(ctx, false); | 1296 | for (i = ctx->num_clocks - 1; i >= 0; i--) |
| 1297 | clk_disable_unprepare(ctx->clocks[i]); | ||
| 1298 | |||
| 1299 | return 0; | ||
| 1770 | } | 1300 | } |
| 1771 | 1301 | ||
| 1772 | static int __maybe_unused gsc_runtime_resume(struct device *dev) | 1302 | static int __maybe_unused gsc_runtime_resume(struct device *dev) |
| 1773 | { | 1303 | { |
| 1774 | struct gsc_context *ctx = get_gsc_context(dev); | 1304 | struct gsc_context *ctx = get_gsc_context(dev); |
| 1305 | int i, ret; | ||
| 1775 | 1306 | ||
| 1776 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); | 1307 | DRM_DEBUG_KMS("id[%d]\n", ctx->id); |
| 1777 | 1308 | ||
| 1778 | return gsc_clk_ctrl(ctx, true); | 1309 | for (i = 0; i < ctx->num_clocks; i++) { |
| 1310 | ret = clk_prepare_enable(ctx->clocks[i]); | ||
| 1311 | if (ret) { | ||
| 1312 | while (--i > 0) | ||
| 1313 | clk_disable_unprepare(ctx->clocks[i]); | ||
| 1314 | return ret; | ||
| 1315 | } | ||
| 1316 | } | ||
| 1317 | return 0; | ||
| 1779 | } | 1318 | } |
| 1780 | 1319 | ||
| 1781 | static const struct dev_pm_ops gsc_pm_ops = { | 1320 | static const struct dev_pm_ops gsc_pm_ops = { |
| @@ -1784,9 +1323,66 @@ static const struct dev_pm_ops gsc_pm_ops = { · 2 unmodified lines | |||
| 1784 | SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) | 1323 | SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) |
| 1785 | }; | 1324 | }; |
| 1786 | 1325 | ||
| 1326 | static const struct drm_exynos_ipp_limit gsc_5250_limits[] = { | ||
| 1327 | { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 4800, 8 }, .v = { 16, 3344, 8 }) }, | ||
| 1328 | { IPP_SIZE_LIMIT(AREA, .h = { 16, 4800, 2 }, .v = { 8, 3344, 2 }) }, | ||
| 1329 | { IPP_SIZE_LIMIT(ROTATED, .h = { 32, 2048 }, .v = { 16, 2048 }) }, | ||
| 1330 | { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8 }, | ||
| 1331 | .v = { (1 << 16) / 16, (1 << 16) * 8 }) }, | ||
| 1332 | }; | ||
| 1333 | |||
| 1334 | static const struct drm_exynos_ipp_limit gsc_5420_limits[] = { | ||
| 1335 | { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 4800, 8 }, .v = { 16, 3344, 8 }) }, | ||
| 1336 | { IPP_SIZE_LIMIT(AREA, .h = { 16, 4800, 2 }, .v = { 8, 3344, 2 }) }, | ||
| 1337 | { IPP_SIZE_LIMIT(ROTATED, .h = { 16, 2016 }, .v = { 8, 2016 }) }, | ||
| 1338 | { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8 }, | ||
| 1339 | .v = { (1 << 16) / 16, (1 << 16) * 8 }) }, | ||
| 1340 | }; | ||
| 1341 | |||
| 1342 | static const struct drm_exynos_ipp_limit gsc_5433_limits[] = { | ||
| 1343 | { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 8191, 2 }, .v = { 16, 8191, 2 }) }, | ||
| 1344 | { IPP_SIZE_LIMIT(AREA, .h = { 16, 4800, 1 }, .v = { 8, 3344, 1 }) }, | ||
| 1345 | { IPP_SIZE_LIMIT(ROTATED, .h = { 32, 2047 }, .v = { 8, 8191 }) }, | ||
| 1346 | { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8 }, | ||
| 1347 | .v = { (1 << 16) / 16, (1 << 16) * 8 }) }, | ||
| 1348 | }; | ||
| 1349 | |||
| 1350 | static struct gsc_driverdata gsc_exynos5250_drvdata = { | ||
| 1351 | .clk_names = {"gscl"}, | ||
| 1352 | .num_clocks = 1, | ||
| 1353 | .limits = gsc_5250_limits, | ||
| 1354 | .num_limits = ARRAY_SIZE(gsc_5250_limits), | ||
| 1355 | }; | ||
| 1356 | |||
| 1357 | static struct gsc_driverdata gsc_exynos5420_drvdata = { | ||
| 1358 | .clk_names = {"gscl"}, | ||
| 1359 | .num_clocks = 1, | ||
| 1360 | .limits = gsc_5420_limits, | ||
| 1361 | .num_limits = ARRAY_SIZE(gsc_5420_limits), | ||
| 1362 | }; | ||
| 1363 | |||
| 1364 | static struct gsc_driverdata gsc_exynos5433_drvdata = { | ||
| 1365 | .clk_names = {"pclk", "aclk", "aclk_xiu", "aclk_gsclbend"}, | ||
| 1366 | .num_clocks = 4, | ||
| 1367 | .limits = gsc_5433_limits, | ||
| 1368 | .num_limits = ARRAY_SIZE(gsc_5433_limits), | ||
| 1369 | }; | ||
| 1370 | |||
| 1787 | static const struct of_device_id exynos_drm_gsc_of_match[] = { | 1371 | static const struct of_device_id exynos_drm_gsc_of_match[] = { |
| 1788 | { .compatible = "samsung,exynos5-gsc" }, | 1372 | { |
| 1789 | { }, | 1373 | .compatible = "samsung,exynos5-gsc", |
| 1374 | .data = &gsc_exynos5250_drvdata, | ||
| 1375 | }, { | ||
| 1376 | .compatible = "samsung,exynos5250-gsc", | ||
| 1377 | .data = &gsc_exynos5250_drvdata, | ||
| 1378 | }, { | ||
| 1379 | .compatible = "samsung,exynos5420-gsc", | ||
| 1380 | .data = &gsc_exynos5420_drvdata, | ||
| 1381 | }, { | ||
| 1382 | .compatible = "samsung,exynos5433-gsc", | ||
| 1383 | .data = &gsc_exynos5433_drvdata, | ||
| 1384 | }, { | ||
| 1385 | }, | ||
| 1790 | }; | 1386 | }; |
| 1791 | MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match); | 1387 | MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match); |
| 1792 | 1388 | ||
| @@ -1800,4 +1396,3 @@ struct platform_driver gsc_driver = { · 7 unmodified lines | |||
| 1800 | .of_match_table = of_match_ptr(exynos_drm_gsc_of_match), | 1396 | .of_match_table = of_match_ptr(exynos_drm_gsc_of_match), |
| 1801 | }, | 1397 | }, |
| 1802 | }; | 1398 | }; |
| 1803 | |||
| @@ -1,24 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
| 6 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
| 7 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License as published by the | ||
| 11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 12 | * option) any later version. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef _EXYNOS_DRM_GSC_H_ | ||
| 16 | #define _EXYNOS_DRM_GSC_H_ | ||
| 17 | |||
| 18 | /* | ||
| 19 | * TODO | ||
| 20 | * FIMD output interface notifier callback. | ||
| 21 | * Mixer output interface notifier callback. | ||
| 22 | */ | ||
| 23 | |||
| 24 | #endif /* _EXYNOS_DRM_GSC_H_ */ | ||
| @@ -0,0 +1,916 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2017 Samsung Electronics Co.Ltd | ||
| 3 | * Authors: | ||
| 4 | * Marek Szyprowski <m.szyprowski@samsung.com> | ||
| 5 | * | ||
| 6 | * Exynos DRM Image Post Processing (IPP) related functions | ||
| 7 | * | ||
| 8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 9 | * copy of this software and associated documentation files (the "Software"), | ||
| 10 | * to deal in the Software without restriction, including without limitation | ||
| 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
| 12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
| 13 | * Software is furnished to do so, subject to the following conditions: | ||
| 14 | * | ||
| 15 | * The above copyright notice and this permission notice shall be included in | ||
| 16 | * all copies or substantial portions of the Software. | ||
| 17 | */ | ||
| 18 | |||
| 19 | |||
| 20 | #include <drm/drmP.h> | ||
| 21 | #include <drm/drm_mode.h> | ||
| 22 | #include <uapi/drm/exynos_drm.h> | ||
| 23 | |||
| 24 | #include "exynos_drm_drv.h" | ||
| 25 | #include "exynos_drm_gem.h" | ||
| 26 | #include "exynos_drm_ipp.h" | ||
| 27 | |||
| 28 | static int num_ipp; | ||
| 29 | static LIST_HEAD(ipp_list); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * exynos_drm_ipp_register - Register a new picture processor hardware module | ||
| 33 | * @dev: DRM device | ||
| 34 | * @ipp: ipp module to init | ||
| 35 | * @funcs: callbacks for the new ipp object | ||
| 36 | * @caps: bitmask of ipp capabilities (%DRM_EXYNOS_IPP_CAP_*) | ||
| 37 | * @formats: array of supported formats | ||
| 38 | * @num_formats: size of the supported formats array | ||
| 39 | * @name: name (for debugging purposes) | ||
| 40 | * | ||
| 41 | * Initializes a ipp module. | ||
| 42 | * | ||
| 43 | * Returns: | ||
| 44 | * Zero on success, error code on failure. | ||
| 45 | */ | ||
| 46 | int exynos_drm_ipp_register(struct drm_device *dev, struct exynos_drm_ipp *ipp, | ||
| 47 | const struct exynos_drm_ipp_funcs *funcs, unsigned int caps, | ||
| 48 | const struct exynos_drm_ipp_formats *formats, | ||
| 49 | unsigned int num_formats, const char *name) | ||
| 50 | { | ||
| 51 | WARN_ON(!ipp); | ||
| 52 | WARN_ON(!funcs); | ||
| 53 | WARN_ON(!formats); | ||
| 54 | WARN_ON(!num_formats); | ||
| 55 | |||
| 56 | spin_lock_init(&ipp->lock); | ||
| 57 | INIT_LIST_HEAD(&ipp->todo_list); | ||
| 58 | init_waitqueue_head(&ipp->done_wq); | ||
| 59 | ipp->dev = dev; | ||
| 60 | ipp->funcs = funcs; | ||
| 61 | ipp->capabilities = caps; | ||
| 62 | ipp->name = name; | ||
| 63 | ipp->formats = formats; | ||
| 64 | ipp->num_formats = num_formats; | ||
| 65 | |||
| 66 | /* ipp_list modification is serialized by component framework */ | ||
| 67 | list_add_tail(&ipp->head, &ipp_list); | ||
| 68 | ipp->id = num_ipp++; | ||
| 69 | |||
| 70 | DRM_DEBUG_DRIVER("Registered ipp %d\n", ipp->id); | ||
| 71 | |||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * exynos_drm_ipp_unregister - Unregister the picture processor module | ||
| 77 | * @dev: DRM device | ||
| 78 | * @ipp: ipp module | ||
| 79 | */ | ||
| 80 | void exynos_drm_ipp_unregister(struct drm_device *dev, | ||
| 81 | struct exynos_drm_ipp *ipp) | ||
| 82 | { | ||
| 83 | WARN_ON(ipp->task); | ||
| 84 | WARN_ON(!list_empty(&ipp->todo_list)); | ||
| 85 | list_del(&ipp->head); | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * exynos_drm_ipp_ioctl_get_res_ioctl - enumerate all ipp modules | ||
| 90 | * @dev: DRM device | ||
| 91 | * @data: ioctl data | ||
| 92 | * @file_priv: DRM file info | ||
| 93 | * | ||
| 94 | * Construct a list of ipp ids. | ||
| 95 | * | ||
| 96 | * Called by the user via ioctl. | ||
| 97 | * | ||
| 98 | * Returns: | ||
| 99 | * Zero on success, negative errno on failure. | ||
| 100 | */ | ||
| 101 | int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data, | ||
| 102 | struct drm_file *file_priv) | ||
| 103 | { | ||
| 104 | struct drm_exynos_ioctl_ipp_get_res *resp = data; | ||
| 105 | struct exynos_drm_ipp *ipp; | ||
| 106 | uint32_t __user *ipp_ptr = (uint32_t __user *) | ||
| 107 | (unsigned long)resp->ipp_id_ptr; | ||
| 108 | unsigned int count = num_ipp, copied = 0; | ||
| 109 | |||
| 110 | /* | ||
| 111 | * This ioctl is called twice, once to determine how much space is | ||
| 112 | * needed, and the 2nd time to fill it. | ||
| 113 | */ | ||
| 114 | if (count && resp->count_ipps >= count) { | ||
| 115 | list_for_each_entry(ipp, &ipp_list, head) { | ||
| 116 | if (put_user(ipp->id, ipp_ptr + copied)) | ||
| 117 | return -EFAULT; | ||
| 118 | copied++; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | resp->count_ipps = count; | ||
| 122 | |||
| 123 | return 0; | ||
| 124 | } | ||
| 125 | |||
| 126 | static inline struct exynos_drm_ipp *__ipp_get(uint32_t id) | ||
| 127 | { | ||
| 128 | struct exynos_drm_ipp *ipp; | ||
| 129 | |||
| 130 | list_for_each_entry(ipp, &ipp_list, head) | ||
| 131 | if (ipp->id == id) | ||
| 132 | return ipp; | ||
| 133 | return NULL; | ||
| 134 | } | ||
| 135 | |||
| 136 | /** | ||
| 137 | * exynos_drm_ipp_ioctl_get_caps - get ipp module capabilities and formats | ||
| 138 | * @dev: DRM device | ||
| 139 | * @data: ioctl data | ||
| 140 | * @file_priv: DRM file info | ||
| 141 | * | ||
| 142 | * Construct a structure describing ipp module capabilities. | ||
| 143 | * | ||
| 144 | * Called by the user via ioctl. | ||
| 145 | * | ||
| 146 | * Returns: | ||
| 147 | * Zero on success, negative errno on failure. | ||
| 148 | */ | ||
| 149 | int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data, | ||
| 150 | struct drm_file *file_priv) | ||
| 151 | { | ||
| 152 | struct drm_exynos_ioctl_ipp_get_caps *resp = data; | ||
| 153 | void __user *ptr = (void __user *)(unsigned long)resp->formats_ptr; | ||
| 154 | struct exynos_drm_ipp *ipp; | ||
| 155 | int i; | ||
| 156 | |||
| 157 | ipp = __ipp_get(resp->ipp_id); | ||
| 158 | if (!ipp) | ||
| 159 | return -ENOENT; | ||
| 160 | |||
| 161 | resp->ipp_id = ipp->id; | ||
| 162 | resp->capabilities = ipp->capabilities; | ||
| 163 | |||
| 164 | /* | ||
| 165 | * This ioctl is called twice, once to determine how much space is | ||
| 166 | * needed, and the 2nd time to fill it. | ||
| 167 | */ | ||
| 168 | if (resp->formats_count >= ipp->num_formats) { | ||
| 169 | for (i = 0; i < ipp->num_formats; i++) { | ||
| 170 | struct drm_exynos_ipp_format tmp = { | ||
| 171 | .fourcc = ipp->formats[i].fourcc, | ||
| 172 | .type = ipp->formats[i].type, | ||
| 173 | .modifier = ipp->formats[i].modifier, | ||
| 174 | }; | ||
| 175 | |||
| 176 | if (copy_to_user(ptr, &tmp, sizeof(tmp))) | ||
| 177 | return -EFAULT; | ||
| 178 | ptr += sizeof(tmp); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | resp->formats_count = ipp->num_formats; | ||
| 182 | |||
| 183 | return 0; | ||
| 184 | } | ||
| 185 | |||
| 186 | static inline const struct exynos_drm_ipp_formats *__ipp_format_get( | ||
| 187 | struct exynos_drm_ipp *ipp, uint32_t fourcc, | ||
| 188 | uint64_t mod, unsigned int type) | ||
| 189 | { | ||
| 190 | int i; | ||
| 191 | |||
| 192 | for (i = 0; i < ipp->num_formats; i++) { | ||
| 193 | if ((ipp->formats[i].type & type) && | ||
| 194 | ipp->formats[i].fourcc == fourcc && | ||
| 195 | ipp->formats[i].modifier == mod) | ||
| 196 | return &ipp->formats[i]; | ||
| 197 | } | ||
| 198 | return NULL; | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * exynos_drm_ipp_get_limits_ioctl - get ipp module limits | ||
| 203 | * @dev: DRM device | ||
| 204 | * @data: ioctl data | ||
| 205 | * @file_priv: DRM file info | ||
| 206 | * | ||
| 207 | * Construct a structure describing ipp module limitations for provided | ||
| 208 | * picture format. | ||
| 209 | * | ||
| 210 | * Called by the user via ioctl. | ||
| 211 | * | ||
| 212 | * Returns: | ||
| 213 | * Zero on success, negative errno on failure. | ||
| 214 | */ | ||
| 215 | int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data, | ||
| 216 | struct drm_file *file_priv) | ||
| 217 | { | ||
| 218 | struct drm_exynos_ioctl_ipp_get_limits *resp = data; | ||
| 219 | void __user *ptr = (void __user *)(unsigned long)resp->limits_ptr; | ||
| 220 | const struct exynos_drm_ipp_formats *format; | ||
| 221 | struct exynos_drm_ipp *ipp; | ||
| 222 | |||
| 223 | if (resp->type != DRM_EXYNOS_IPP_FORMAT_SOURCE && | ||
| 224 | resp->type != DRM_EXYNOS_IPP_FORMAT_DESTINATION) | ||
| 225 | return -EINVAL; | ||
| 226 | |||
| 227 | ipp = __ipp_get(resp->ipp_id); | ||
| 228 | if (!ipp) | ||
| 229 | return -ENOENT; | ||
| 230 | |||
| 231 | format = __ipp_format_get(ipp, resp->fourcc, resp->modifier, | ||
| 232 | resp->type); | ||
| 233 | if (!format) | ||
| 234 | return -EINVAL; | ||
| 235 | |||
| 236 | /* | ||
| 237 | * This ioctl is called twice, once to determine how much space is | ||
| 238 | * needed, and the 2nd time to fill it. | ||
| 239 | */ | ||
| 240 | if (format->num_limits && resp->limits_count >= format->num_limits) | ||
| 241 | if (copy_to_user((void __user *)ptr, format->limits, | ||
| 242 | sizeof(*format->limits) * format->num_limits)) | ||
| 243 | return -EFAULT; | ||
| 244 | resp->limits_count = format->num_limits; | ||
| 245 | |||
| 246 | return 0; | ||
| 247 | } | ||
| 248 | |||
| 249 | struct drm_pending_exynos_ipp_event { | ||
| 250 | struct drm_pending_event base; | ||
| 251 | struct drm_exynos_ipp_event event; | ||
| 252 | }; | ||
| 253 | |||
| 254 | static inline struct exynos_drm_ipp_task * | ||
| 255 | exynos_drm_ipp_task_alloc(struct exynos_drm_ipp *ipp) | ||
| 256 | { | ||
| 257 | struct exynos_drm_ipp_task *task; | ||
| 258 | |||
| 259 | task = kzalloc(sizeof(*task), GFP_KERNEL); | ||
| 260 | if (!task) | ||
| 261 | return NULL; | ||
| 262 | |||
| 263 | task->dev = ipp->dev; | ||
| 264 | task->ipp = ipp; | ||
| 265 | |||
| 266 | /* some defaults */ | ||
| 267 | task->src.rect.w = task->dst.rect.w = UINT_MAX; | ||
| 268 | task->src.rect.h = task->dst.rect.h = UINT_MAX; | ||
| 269 | task->transform.rotation = DRM_MODE_ROTATE_0; | ||
| 270 | |||
| 271 | DRM_DEBUG_DRIVER("Allocated task %pK\n", task); | ||
| 272 | |||
| 273 | return task; | ||
| 274 | } | ||
| 275 | |||
| 276 | static const struct exynos_drm_param_map { | ||
| 277 | unsigned int id; | ||
| 278 | unsigned int size; | ||
| 279 | unsigned int offset; | ||
| 280 | } exynos_drm_ipp_params_maps[] = { | ||
| 281 | { | ||
| 282 | DRM_EXYNOS_IPP_TASK_BUFFER | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE, | ||
| 283 | sizeof(struct drm_exynos_ipp_task_buffer), | ||
| 284 | offsetof(struct exynos_drm_ipp_task, src.buf), | ||
| 285 | }, { | ||
| 286 | DRM_EXYNOS_IPP_TASK_BUFFER | | ||
| 287 | DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION, | ||
| 288 | sizeof(struct drm_exynos_ipp_task_buffer), | ||
| 289 | offsetof(struct exynos_drm_ipp_task, dst.buf), | ||
| 290 | }, { | ||
| 291 | DRM_EXYNOS_IPP_TASK_RECTANGLE | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE, | ||
| 292 | sizeof(struct drm_exynos_ipp_task_rect), | ||
| 293 | offsetof(struct exynos_drm_ipp_task, src.rect), | ||
| 294 | }, { | ||
| 295 | DRM_EXYNOS_IPP_TASK_RECTANGLE | | ||
| 296 | DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION, | ||
| 297 | sizeof(struct drm_exynos_ipp_task_rect), | ||
| 298 | offsetof(struct exynos_drm_ipp_task, dst.rect), | ||
| 299 | }, { | ||
| 300 | DRM_EXYNOS_IPP_TASK_TRANSFORM, | ||
| 301 | sizeof(struct drm_exynos_ipp_task_transform), | ||
| 302 | offsetof(struct exynos_drm_ipp_task, transform), | ||
| 303 | }, { | ||
| 304 | DRM_EXYNOS_IPP_TASK_ALPHA, | ||
| 305 | sizeof(struct drm_exynos_ipp_task_alpha), | ||
| 306 | offsetof(struct exynos_drm_ipp_task, alpha), | ||
| 307 | }, | ||
| 308 | }; | ||
| 309 | |||
| 310 | static int exynos_drm_ipp_task_set(struct exynos_drm_ipp_task *task, | ||
| 311 | struct drm_exynos_ioctl_ipp_commit *arg) | ||
| 312 | { | ||
| 313 | const struct exynos_drm_param_map *map = exynos_drm_ipp_params_maps; | ||
| 314 | void __user *params = (void __user *)(unsigned long)arg->params_ptr; | ||
| 315 | unsigned int size = arg->params_size; | ||
| 316 | uint32_t id; | ||
| 317 | int i; | ||
| 318 | |||
| 319 | while (size) { | ||
| 320 | if (get_user(id, (uint32_t __user *)params)) | ||
| 321 | return -EFAULT; | ||
| 322 | |||
| 323 | for (i = 0; i < ARRAY_SIZE(exynos_drm_ipp_params_maps); i++) | ||
| 324 | if (map[i].id == id) | ||
| 325 | break; | ||
| 326 | if (i == ARRAY_SIZE(exynos_drm_ipp_params_maps) || | ||
| 327 | map[i].size > size) | ||
| 328 | return -EINVAL; | ||
| 329 | |||
| 330 | if (copy_from_user((void *)task + map[i].offset, params, | ||
| 331 | map[i].size)) | ||
| 332 | return -EFAULT; | ||
| 333 | |||
| 334 | params += map[i].size; | ||
| 335 | size -= map[i].size; | ||
| 336 | } | ||
| 337 | |||
| 338 | DRM_DEBUG_DRIVER("Got task %pK configuration from userspace\n", task); | ||
| 339 | return 0; | ||
| 340 | } | ||
| 341 | |||
| 342 | static int exynos_drm_ipp_task_setup_buffer(struct exynos_drm_ipp_buffer *buf, | ||
| 343 | struct drm_file *filp) | ||
| 344 | { | ||
| 345 | int ret = 0; | ||
| 346 | int i; | ||
| 347 | |||
| 348 | /* basic checks */ | ||
| 349 | if (buf->buf.width == 0 || buf->buf.height == 0) | ||
| 350 | return -EINVAL; | ||
| 351 | buf->format = drm_format_info(buf->buf.fourcc); | ||
| 352 | for (i = 0; i < buf->format->num_planes; i++) { | ||
| 353 | unsigned int width = (i == 0) ? buf->buf.width : | ||
| 354 | DIV_ROUND_UP(buf->buf.width, buf->format->hsub); | ||
| 355 | |||
| 356 | if (buf->buf.pitch[i] == 0) | ||
| 357 | buf->buf.pitch[i] = width * buf->format->cpp[i]; | ||
| 358 | if (buf->buf.pitch[i] < width * buf->format->cpp[i]) | ||
| 359 | return -EINVAL; | ||
| 360 | if (!buf->buf.gem_id[i]) | ||
| 361 | return -ENOENT; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* pitch for additional planes must match */ | ||
| 365 | if (buf->format->num_planes > 2 && | ||
| 366 | buf->buf.pitch[1] != buf->buf.pitch[2]) | ||
| 367 | return -EINVAL; | ||
| 368 | |||
| 369 | /* get GEM buffers and check their size */ | ||
| 370 | for (i = 0; i < buf->format->num_planes; i++) { | ||
| 371 | unsigned int height = (i == 0) ? buf->buf.height : | ||
| 372 | DIV_ROUND_UP(buf->buf.height, buf->format->vsub); | ||
| 373 | unsigned long size = height * buf->buf.pitch[i]; | ||
| 374 | struct drm_gem_object *obj = drm_gem_object_lookup(filp, | ||
| 375 | buf->buf.gem_id[i]); | ||
| 376 | if (!obj) { | ||
| 377 | ret = -ENOENT; | ||
| 378 | goto gem_free; | ||
| 379 | } | ||
| 380 | buf->exynos_gem[i] = to_exynos_gem(obj); | ||
| 381 | |||
| 382 | if (size + buf->buf.offset[i] > buf->exynos_gem[i]->size) { | ||
| 383 | i++; | ||
| 384 | ret = -EINVAL; | ||
| 385 | goto gem_free; | ||
| 386 | } | ||
| 387 | buf->dma_addr[i] = buf->exynos_gem[i]->dma_addr + | ||
| 388 | buf->buf.offset[i]; | ||
| 389 | } | ||
| 390 | |||
| 391 | return 0; | ||
| 392 | gem_free: | ||
| 393 | while (i--) { | ||
| 394 | drm_gem_object_put_unlocked(&buf->exynos_gem[i]->base); | ||
| 395 | buf->exynos_gem[i] = NULL; | ||
| 396 | } | ||
| 397 | return ret; | ||
| 398 | } | ||
| 399 | |||
| 400 | static void exynos_drm_ipp_task_release_buf(struct exynos_drm_ipp_buffer *buf) | ||
| 401 | { | ||
| 402 | int i; | ||
| 403 | |||
| 404 | if (!buf->exynos_gem[0]) | ||
| 405 | return; | ||
| 406 | for (i = 0; i < buf->format->num_planes; i++) | ||
| 407 | drm_gem_object_put_unlocked(&buf->exynos_gem[i]->base); | ||
| 408 | } | ||
| 409 | |||
| 410 | static void exynos_drm_ipp_task_free(struct exynos_drm_ipp *ipp, | ||
| 411 | struct exynos_drm_ipp_task *task) | ||
| 412 | { | ||
| 413 | DRM_DEBUG_DRIVER("Freeing task %pK\n", task); | ||
| 414 | |||
| 415 | exynos_drm_ipp_task_release_buf(&task->src); | ||
| 416 | exynos_drm_ipp_task_release_buf(&task->dst); | ||
| 417 | if (task->event) | ||
| 418 | drm_event_cancel_free(ipp->dev, &task->event->base); | ||
| 419 | kfree(task); | ||
| 420 | } | ||
| 421 | |||
| 422 | struct drm_ipp_limit { | ||
| 423 | struct drm_exynos_ipp_limit_val h; | ||
| 424 | struct drm_exynos_ipp_limit_val v; | ||
| 425 | }; | ||
| 426 | |||
| 427 | enum drm_ipp_size_id { | ||
| 428 | IPP_LIMIT_BUFFER, IPP_LIMIT_AREA, IPP_LIMIT_ROTATED, IPP_LIMIT_MAX | ||
| 429 | }; | ||
| 430 | |||
| 431 | static const enum drm_ipp_size_id limit_id_fallback[IPP_LIMIT_MAX][4] = { | ||
| 432 | [IPP_LIMIT_BUFFER] = { DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER }, | ||
| 433 | [IPP_LIMIT_AREA] = { DRM_EXYNOS_IPP_LIMIT_SIZE_AREA, | ||
| 434 | DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER }, | ||
| 435 | [IPP_LIMIT_ROTATED] = { DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED, | ||
| 436 | DRM_EXYNOS_IPP_LIMIT_SIZE_AREA, | ||
| 437 | DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER }, | ||
| 438 | }; | ||
| 439 | |||
| 440 | static inline void __limit_set_val(unsigned int *ptr, unsigned int val) | ||
| 441 | { | ||
| 442 | if (!*ptr) | ||
| 443 | *ptr = val; | ||
| 444 | } | ||
| 445 | |||
| 446 | static void __get_size_limit(const struct drm_exynos_ipp_limit *limits, | ||
| 447 | unsigned int num_limits, enum drm_ipp_size_id id, | ||
| 448 | struct drm_ipp_limit *res) | ||
| 449 | { | ||
| 450 | const struct drm_exynos_ipp_limit *l = limits; | ||
| 451 | int i = 0; | ||
| 452 | |||
| 453 | memset(res, 0, sizeof(*res)); | ||
| 454 | for (i = 0; limit_id_fallback[id][i]; i++) | ||
| 455 | for (l = limits; l - limits < num_limits; l++) { | ||
| 456 | if (((l->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) != | ||
| 457 | DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE) || | ||
| 458 | ((l->type & DRM_EXYNOS_IPP_LIMIT_SIZE_MASK) != | ||
| 459 | limit_id_fallback[id][i])) | ||
| 460 | continue; | ||
| 461 | __limit_set_val(&res->h.min, l->h.min); | ||
| 462 | __limit_set_val(&res->h.max, l->h.max); | ||
| 463 | __limit_set_val(&res->h.align, l->h.align); | ||
| 464 | __limit_set_val(&res->v.min, l->v.min); | ||
| 465 | __limit_set_val(&res->v.max, l->v.max); | ||
| 466 | __limit_set_val(&res->v.align, l->v.align); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | static inline bool __align_check(unsigned int val, unsigned int align) | ||
| 471 | { | ||
| 472 | if (align && (val & (align - 1))) { | ||
| 473 | DRM_DEBUG_DRIVER("Value %d exceeds HW limits (align %d)\n", | ||
| 474 | val, align); | ||
| 475 | return false; | ||
| 476 | } | ||
| 477 | return true; | ||
| 478 | } | ||
| 479 | |||
| 480 | static inline bool __size_limit_check(unsigned int val, | ||
| 481 | struct drm_exynos_ipp_limit_val *l) | ||
| 482 | { | ||
| 483 | if ((l->min && val < l->min) || (l->max && val > l->max)) { | ||
| 484 | DRM_DEBUG_DRIVER("Value %d exceeds HW limits (min %d, max %d)\n", | ||
| 485 | val, l->min, l->max); | ||
| 486 | return false; | ||
| 487 | } | ||
| 488 | return __align_check(val, l->align); | ||
| 489 | } | ||
| 490 | |||
| 491 | static int exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer *buf, | ||
| 492 | const struct drm_exynos_ipp_limit *limits, unsigned int num_limits, | ||
| 493 | bool rotate, bool swap) | ||
| 494 | { | ||
| 495 | enum drm_ipp_size_id id = rotate ? IPP_LIMIT_ROTATED : IPP_LIMIT_AREA; | ||
| 496 | struct drm_ipp_limit l; | ||
| 497 | struct drm_exynos_ipp_limit_val *lh = &l.h, *lv = &l.v; | ||
| 498 | |||
| 499 | if (!limits) | ||
| 500 | return 0; | ||
| 501 | |||
| 502 | __get_size_limit(limits, num_limits, IPP_LIMIT_BUFFER, &l); | ||
| 503 | if (!__size_limit_check(buf->buf.width, &l.h) || | ||
| 504 | !__size_limit_check(buf->buf.height, &l.v)) | ||
| 505 | return -EINVAL; | ||
| 506 | |||
| 507 | if (swap) { | ||
| 508 | lv = &l.h; | ||
| 509 | lh = &l.v; | ||
| 510 | } | ||
| 511 | __get_size_limit(limits, num_limits, id, &l); | ||
| 512 | if (!__size_limit_check(buf->rect.w, lh) || | ||
| 513 | !__align_check(buf->rect.x, lh->align) || | ||
| 514 | !__size_limit_check(buf->rect.h, lv) || | ||
| 515 | !__align_check(buf->rect.y, lv->align)) | ||
| 516 | return -EINVAL; | ||
| 517 | |||
| 518 | return 0; | ||
| 519 | } | ||
| 520 | |||
| 521 | static inline bool __scale_limit_check(unsigned int src, unsigned int dst, | ||
| 522 | unsigned int min, unsigned int max) | ||
| 523 | { | ||
| 524 | if ((max && (dst << 16) > src * max) || | ||
| 525 | (min && (dst << 16) < src * min)) { | ||
| 526 | DRM_DEBUG_DRIVER("Scale from %d to %d exceeds HW limits (ratio min %d.%05d, max %d.%05d)\n", | ||
| 527 | src, dst, | ||
| 528 | min >> 16, 100000 * (min & 0xffff) / (1 << 16), | ||
| 529 | max >> 16, 100000 * (max & 0xffff) / (1 << 16)); | ||
| 530 | return false; | ||
| 531 | } | ||
| 532 | return true; | ||
| 533 | } | ||
| 534 | |||
| 535 | static int exynos_drm_ipp_check_scale_limits( | ||
| 536 | struct drm_exynos_ipp_task_rect *src, | ||
| 537 | struct drm_exynos_ipp_task_rect *dst, | ||
| 538 | const struct drm_exynos_ipp_limit *limits, | ||
| 539 | unsigned int num_limits, bool swap) | ||
| 540 | { | ||
| 541 | const struct drm_exynos_ipp_limit_val *lh, *lv; | ||
| 542 | int dw, dh; | ||
| 543 | |||
| 544 | for (; num_limits; limits++, num_limits--) | ||
| 545 | if ((limits->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) == | ||
| 546 | DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE) | ||
| 547 | break; | ||
| 548 | if (!num_limits) | ||
| 549 | return 0; | ||
| 550 | |||
| 551 | lh = (!swap) ? &limits->h : &limits->v; | ||
| 552 | lv = (!swap) ? &limits->v : &limits->h; | ||
| 553 | dw = (!swap) ? dst->w : dst->h; | ||
| 554 | dh = (!swap) ? dst->h : dst->w; | ||
| 555 | |||
| 556 | if (!__scale_limit_check(src->w, dw, lh->min, lh->max) || | ||
| 557 | !__scale_limit_check(src->h, dh, lv->min, lv->max)) | ||
| 558 | return -EINVAL; | ||
| 559 | |||
| 560 | return 0; | ||
| 561 | } | ||
| 562 | |||
| 563 | static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task) | ||
| 564 | { | ||
| 565 | struct exynos_drm_ipp *ipp = task->ipp; | ||
| 566 | const struct exynos_drm_ipp_formats *src_fmt, *dst_fmt; | ||
| 567 | struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst; | ||
| 568 | unsigned int rotation = task->transform.rotation; | ||
| 569 | int ret = 0; | ||
| 570 | bool swap = drm_rotation_90_or_270(rotation); | ||
| 571 | bool rotate = (rotation != DRM_MODE_ROTATE_0); | ||
| 572 | bool scale = false; | ||
| 573 | |||
| 574 | DRM_DEBUG_DRIVER("Checking task %pK\n", task); | ||
| 575 | |||
| 576 | if (src->rect.w == UINT_MAX) | ||
| 577 | src->rect.w = src->buf.width; | ||
| 578 | if (src->rect.h == UINT_MAX) | ||
| 579 | src->rect.h = src->buf.height; | ||
| 580 | if (dst->rect.w == UINT_MAX) | ||
| 581 | dst->rect.w = dst->buf.width; | ||
| 582 | if (dst->rect.h == UINT_MAX) | ||
| 583 | dst->rect.h = dst->buf.height; | ||
| 584 | |||
| 585 | if (src->rect.x + src->rect.w > (src->buf.width) || | ||
| 586 | src->rect.y + src->rect.h > (src->buf.height) || | ||
| 587 | dst->rect.x + dst->rect.w > (dst->buf.width) || | ||
| 588 | dst->rect.y + dst->rect.h > (dst->buf.height)) { | ||
| 589 | DRM_DEBUG_DRIVER("Task %pK: defined area is outside provided buffers\n", | ||
| 590 | task); | ||
| 591 | return -EINVAL; | ||
| 592 | } | ||
| 593 | |||
| 594 | if ((!swap && (src->rect.w != dst->rect.w || | ||
| 595 | src->rect.h != dst->rect.h)) || | ||
| 596 | (swap && (src->rect.w != dst->rect.h || | ||
| 597 | src->rect.h != dst->rect.w))) | ||
| 598 | scale = true; | ||
| 599 | |||
| 600 | if ((!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CROP) && | ||
| 601 | (src->rect.x || src->rect.y || dst->rect.x || dst->rect.y)) || | ||
| 602 | (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_ROTATE) && rotate) || | ||
| 603 | (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_SCALE) && scale) || | ||
| 604 | (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CONVERT) && | ||
| 605 | src->buf.fourcc != dst->buf.fourcc)) { | ||
| 606 | DRM_DEBUG_DRIVER("Task %pK: hw capabilities exceeded\n", task); | ||
| 607 | return -EINVAL; | ||
| 608 | } | ||
| 609 | |||
| 610 | src_fmt = __ipp_format_get(ipp, src->buf.fourcc, src->buf.modifier, | ||
| 611 | DRM_EXYNOS_IPP_FORMAT_SOURCE); | ||
| 612 | if (!src_fmt) { | ||
| 613 | DRM_DEBUG_DRIVER("Task %pK: src format not supported\n", task); | ||
| 614 | return -EINVAL; | ||
| 615 | } | ||
| 616 | ret = exynos_drm_ipp_check_size_limits(src, src_fmt->limits, | ||
| 617 | src_fmt->num_limits, | ||
| 618 | rotate, false); | ||
| 619 | if (ret) | ||
| 620 | return ret; | ||
| 621 | ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect, | ||
| 622 | src_fmt->limits, | ||
| 623 | src_fmt->num_limits, swap); | ||
| 624 | if (ret) | ||
| 625 | return ret; | ||
| 626 | |||
| 627 | dst_fmt = __ipp_format_get(ipp, dst->buf.fourcc, dst->buf.modifier, | ||
| 628 | DRM_EXYNOS_IPP_FORMAT_DESTINATION); | ||
| 629 | if (!dst_fmt) { | ||
| 630 | DRM_DEBUG_DRIVER("Task %pK: dst format not supported\n", task); | ||
| 631 | return -EINVAL; | ||
| 632 | } | ||
| 633 | ret = exynos_drm_ipp_check_size_limits(dst, dst_fmt->limits, | ||
| 634 | dst_fmt->num_limits, | ||
| 635 | false, swap); | ||
| 636 | if (ret) | ||
| 637 | return ret; | ||
| 638 | ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect, | ||
| 639 | dst_fmt->limits, | ||
| 640 | dst_fmt->num_limits, swap); | ||
| 641 | if (ret) | ||
| 642 | return ret; | ||
| 643 | |||
| 644 | DRM_DEBUG_DRIVER("Task %pK: all checks done.\n", task); | ||
| 645 | |||
| 646 | return ret; | ||
| 647 | } | ||
| 648 | |||
| 649 | static int exynos_drm_ipp_task_setup_buffers(struct exynos_drm_ipp_task *task, | ||
| 650 | struct drm_file *filp) | ||
| 651 | { | ||
| 652 | struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst; | ||
| 653 | int ret = 0; | ||
| 654 | |||
| 655 | DRM_DEBUG_DRIVER("Setting buffer for task %pK\n", task); | ||
| 656 | |||
| 657 | ret = exynos_drm_ipp_task_setup_buffer(src, filp); | ||
| 658 | if (ret) { | ||
| 659 | DRM_DEBUG_DRIVER("Task %pK: src buffer setup failed\n", task); | ||
| 660 | return ret; | ||
| 661 | } | ||
| 662 | ret = exynos_drm_ipp_task_setup_buffer(dst, filp); | ||
| 663 | if (ret) { | ||
| 664 | DRM_DEBUG_DRIVER("Task %pK: dst buffer setup failed\n", task); | ||
| 665 | return ret; | ||
| 666 | } | ||
| 667 | |||
| 668 | DRM_DEBUG_DRIVER("Task %pK: buffers prepared.\n", task); | ||
| 669 | |||
| 670 | return ret; | ||
| 671 | } | ||
| 672 | |||
| 673 | |||
| 674 | static int exynos_drm_ipp_event_create(struct exynos_drm_ipp_task *task, | ||
| 675 | struct drm_file *file_priv, uint64_t user_data) | ||
| 676 | { | ||
| 677 | struct drm_pending_exynos_ipp_event *e = NULL; | ||
| 678 | int ret; | ||
| 679 | |||
| 680 | e = kzalloc(sizeof(*e), GFP_KERNEL); | ||
| 681 | if (!e) | ||
| 682 | return -ENOMEM; | ||
| 683 | |||
| 684 | e->event.base.type = DRM_EXYNOS_IPP_EVENT; | ||
| 685 | e->event.base.length = sizeof(e->event); | ||
| 686 | e->event.user_data = user_data; | ||
| 687 | |||
| 688 | ret = drm_event_reserve_init(task->dev, file_priv, &e->base, | ||
| 689 | &e->event.base); | ||
| 690 | if (ret) | ||
| 691 | goto free; | ||
| 692 | |||
| 693 | task->event = e; | ||
| 694 | return 0; | ||
| 695 | free: | ||
| 696 | kfree(e); | ||
| 697 | return ret; | ||
| 698 | } | ||
| 699 | |||
| 700 | static void exynos_drm_ipp_event_send(struct exynos_drm_ipp_task *task) | ||
| 701 | { | ||
| 702 | struct timespec64 now; | ||
| 703 | |||
| 704 | ktime_get_ts64(&now); | ||
| 705 | task->event->event.tv_sec = now.tv_sec; | ||
| 706 | task->event->event.tv_usec = now.tv_nsec / NSEC_PER_USEC; | ||
| 707 | task->event->event.sequence = atomic_inc_return(&task->ipp->sequence); | ||
| 708 | |||
| 709 | drm_send_event(task->dev, &task->event->base); | ||
| 710 | } | ||
| 711 | |||
| 712 | static int exynos_drm_ipp_task_cleanup(struct exynos_drm_ipp_task *task) | ||
| 713 | { | ||
| 714 | int ret = task->ret; | ||
| 715 | |||
| 716 | if (ret == 0 && task->event) { | ||
| 717 | exynos_drm_ipp_event_send(task); | ||
| 718 | /* ensure event won't be canceled on task free */ | ||
| 719 | task->event = NULL; | ||
| 720 | } | ||
| 721 | |||
| 722 | exynos_drm_ipp_task_free(task->ipp, task); | ||
| 723 | return ret; | ||
| 724 | } | ||
| 725 | |||
| 726 | static void exynos_drm_ipp_cleanup_work(struct work_struct *work) | ||
| 727 | { | ||
| 728 | struct exynos_drm_ipp_task *task = container_of(work, | ||
| 729 | struct exynos_drm_ipp_task, cleanup_work); | ||
| 730 | |||
| 731 | exynos_drm_ipp_task_cleanup(task); | ||
| 732 | } | ||
| 733 | |||
| 734 | static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp); | ||
| 735 | |||
| 736 | /** | ||
| 737 | * exynos_drm_ipp_task_done - finish given task and set return code | ||
| 738 | * @task: ipp task to finish | ||
| 739 | * @ret: error code or 0 if operation has been performed successfully | ||
| 740 | */ | ||
| 741 | void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret) | ||
| 742 | { | ||
| 743 | struct exynos_drm_ipp *ipp = task->ipp; | ||
| 744 | unsigned long flags; | ||
| 745 | |||
| 746 | DRM_DEBUG_DRIVER("ipp: %d, task %pK done: %d\n", ipp->id, task, ret); | ||
| 747 | |||
| 748 | spin_lock_irqsave(&ipp->lock, flags); | ||
| 749 | if (ipp->task == task) | ||
| 750 | ipp->task = NULL; | ||
| 751 | task->flags |= DRM_EXYNOS_IPP_TASK_DONE; | ||
| 752 | task->ret = ret; | ||
| 753 | spin_unlock_irqrestore(&ipp->lock, flags); | ||
| 754 | |||
| 755 | exynos_drm_ipp_next_task(ipp); | ||
| 756 | wake_up(&ipp->done_wq); | ||
| 757 | |||
| 758 | if (task->flags & DRM_EXYNOS_IPP_TASK_ASYNC) { | ||
| 759 | INIT_WORK(&task->cleanup_work, exynos_drm_ipp_cleanup_work); | ||
| 760 | schedule_work(&task->cleanup_work); | ||
| 761 | } | ||
| 762 | } | ||
| 763 | |||
| 764 | static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp) | ||
| 765 | { | ||
| 766 | struct exynos_drm_ipp_task *task; | ||
| 767 | unsigned long flags; | ||
| 768 | int ret; | ||
| 769 | |||
| 770 | DRM_DEBUG_DRIVER("ipp: %d, try to run new task\n", ipp->id); | ||
| 771 | |||
| 772 | spin_lock_irqsave(&ipp->lock, flags); | ||
| 773 | |||
| 774 | if (ipp->task || list_empty(&ipp->todo_list)) { | ||
| 775 | spin_unlock_irqrestore(&ipp->lock, flags); | ||
| 776 | return; | ||
| 777 | } | ||
| 778 | |||
| 779 | task = list_first_entry(&ipp->todo_list, struct exynos_drm_ipp_task, | ||
| 780 | head); | ||
| 781 | list_del_init(&task->head); | ||
| 782 | ipp->task = task; | ||
| 783 | |||
| 784 | spin_unlock_irqrestore(&ipp->lock, flags); | ||
| 785 | |||
| 786 | DRM_DEBUG_DRIVER("ipp: %d, selected task %pK to run\n", ipp->id, task); | ||
| 787 | |||
| 788 | ret = ipp->funcs->commit(ipp, task); | ||
| 789 | if (ret) | ||
| 790 | exynos_drm_ipp_task_done(task, ret); | ||
| 791 | } | ||
| 792 | |||
| 793 | static void exynos_drm_ipp_schedule_task(struct exynos_drm_ipp *ipp, | ||
| 794 | struct exynos_drm_ipp_task *task) | ||
| 795 | { | ||
| 796 | unsigned long flags; | ||
| 797 | |||
| 798 | spin_lock_irqsave(&ipp->lock, flags); | ||
| 799 | list_add(&task->head, &ipp->todo_list); | ||
| 800 | spin_unlock_irqrestore(&ipp->lock, flags); | ||
| 801 | |||
| 802 | exynos_drm_ipp_next_task(ipp); | ||
| 803 | } | ||
| 804 | |||
| 805 | static void exynos_drm_ipp_task_abort(struct exynos_drm_ipp *ipp, | ||
| 806 | struct exynos_drm_ipp_task *task) | ||
| 807 | { | ||
| 808 | unsigned long flags; | ||
| 809 | |||
| 810 | spin_lock_irqsave(&ipp->lock, flags); | ||
| 811 | if (task->flags & DRM_EXYNOS_IPP_TASK_DONE) { | ||
| 812 | /* already completed task */ | ||
| 813 | exynos_drm_ipp_task_cleanup(task); | ||
| 814 | } else if (ipp->task != task) { | ||
| 815 | /* task has not been scheduled for execution yet */ | ||
| 816 | list_del_init(&task->head); | ||
| 817 | exynos_drm_ipp_task_cleanup(task); | ||
| 818 | } else { | ||
| 819 | /* | ||
| 820 | * currently processed task, call abort() and perform | ||
| 821 | * cleanup with async worker | ||
| 822 | */ | ||
| 823 | task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC; | ||
| 824 | spin_unlock_irqrestore(&ipp->lock, flags); | ||
| 825 | if (ipp->funcs->abort) | ||
| 826 | ipp->funcs->abort(ipp, task); | ||
| 827 | return; | ||
| 828 | } | ||
| 829 | spin_unlock_irqrestore(&ipp->lock, flags); | ||
| 830 | } | ||
| 831 | |||
| 832 | /** | ||
| 833 | * exynos_drm_ipp_commit_ioctl - perform image processing operation | ||
| 834 | * @dev: DRM device | ||
| 835 | * @data: ioctl data | ||
| 836 | * @file_priv: DRM file info | ||
| 837 | * | ||
| 838 | * Construct a ipp task from the set of properties provided from the user | ||
| 839 | * and try to schedule it to framebuffer processor hardware. | ||
| 840 | * | ||
| 841 | * Called by the user via ioctl. | ||
| 842 | * | ||
| 843 | * Returns: | ||
| 844 | * Zero on success, negative errno on failure. | ||
| 845 | */ | ||
| 846 | int exynos_drm_ipp_commit_ioctl(struct drm_device *dev, void *data, | ||
| 847 | struct drm_file *file_priv) | ||
| 848 | { | ||
| 849 | struct drm_exynos_ioctl_ipp_commit *arg = data; | ||
| 850 | struct exynos_drm_ipp *ipp; | ||
| 851 | struct exynos_drm_ipp_task *task; | ||
| 852 | int ret = 0; | ||
| 853 | |||
| 854 | if ((arg->flags & ~DRM_EXYNOS_IPP_FLAGS) || arg->reserved) | ||
| 855 | return -EINVAL; | ||
| 856 | |||
| 857 | /* can't test and expect an event at the same time */ | ||
| 858 | if ((arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY) && | ||
| 859 | (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT)) | ||
| 860 | return -EINVAL; | ||
| 861 | |||
| 862 | ipp = __ipp_get(arg->ipp_id); | ||
| 863 | if (!ipp) | ||
| 864 | return -ENOENT; | ||
| 865 | |||
| 866 | task = exynos_drm_ipp_task_alloc(ipp); | ||
| 867 | if (!task) | ||
| 868 | return -ENOMEM; | ||
| 869 | |||
| 870 | ret = exynos_drm_ipp_task_set(task, arg); | ||
| 871 | if (ret) | ||
| 872 | goto free; | ||
| 873 | |||
| 874 | ret = exynos_drm_ipp_task_check(task); | ||
| 875 | if (ret) | ||
| 876 | goto free; | ||
| 877 | |||
| 878 | ret = exynos_drm_ipp_task_setup_buffers(task, file_priv); | ||
| 879 | if (ret || arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY) | ||
| 880 | goto free; | ||
| 881 | |||
| 882 | if (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT) { | ||
| 883 | ret = exynos_drm_ipp_event_create(task, file_priv, | ||
| 884 | arg->user_data); | ||
| 885 | if (ret) | ||
| 886 | goto free; | ||
| 887 | } | ||
| 888 | |||
| 889 | /* | ||
| 890 | * Queue task for processing on the hardware. task object will be | ||
| 891 | * then freed after exynos_drm_ipp_task_done() | ||
| 892 | */ | ||
| 893 | if (arg->flags & DRM_EXYNOS_IPP_FLAG_NONBLOCK) { | ||
| 894 | DRM_DEBUG_DRIVER("ipp: %d, nonblocking processing task %pK\n", | ||
| 895 | ipp->id, task); | ||
| 896 | |||
| 897 | task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC; | ||
| 898 | exynos_drm_ipp_schedule_task(task->ipp, task); | ||
| 899 | ret = 0; | ||
| 900 | } else { | ||
| 901 | DRM_DEBUG_DRIVER("ipp: %d, processing task %pK\n", ipp->id, | ||
| 902 | task); | ||
| 903 | exynos_drm_ipp_schedule_task(ipp, task); | ||
| 904 | ret = wait_event_interruptible(ipp->done_wq, | ||
| 905 | task->flags & DRM_EXYNOS_IPP_TASK_DONE); | ||
| 906 | if (ret) | ||
| 907 | exynos_drm_ipp_task_abort(ipp, task); | ||
| 908 | else | ||
| 909 | ret = exynos_drm_ipp_task_cleanup(task); | ||
| 910 | } | ||
| 911 | return ret; | ||
| 912 | free: | ||
| 913 | exynos_drm_ipp_task_free(ipp, task); | ||
| 914 | |||
| 915 | return ret; | ||
| 916 | } | ||
| @@ -0,0 +1,175 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2017 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License as published by the | ||
| 6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 7 | * option) any later version. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef _EXYNOS_DRM_IPP_H_ | ||
| 11 | #define _EXYNOS_DRM_IPP_H_ | ||
| 12 | |||
| 13 | #include <drm/drmP.h> | ||
| 14 | |||
| 15 | struct exynos_drm_ipp; | ||
| 16 | struct exynos_drm_ipp_task; | ||
| 17 | |||
| 18 | /** | ||
| 19 | * struct exynos_drm_ipp_funcs - exynos_drm_ipp control functions | ||
| 20 | */ | ||
| 21 | struct exynos_drm_ipp_funcs { | ||
| 22 | /** | ||
| 23 | * @commit: | ||
| 24 | * | ||
| 25 | * This is the main entry point to start framebuffer processing | ||
| 26 | * in the hardware. The exynos_drm_ipp_task has been already validated. | ||
| 27 | * This function must not wait until the device finishes processing. | ||
| 28 | * When the driver finishes processing, it has to call | ||
| 29 | * exynos_exynos_drm_ipp_task_done() function. | ||
| 30 | * | ||
| 31 | * RETURNS: | ||
| 32 | * | ||
| 33 | * 0 on success or negative error codes in case of failure. | ||
| 34 | */ | ||
| 35 | int (*commit)(struct exynos_drm_ipp *ipp, | ||
| 36 | struct exynos_drm_ipp_task *task); | ||
| 37 | |||
| 38 | /** | ||
| 39 | * @abort: | ||
| 40 | * | ||
| 41 | * Informs the driver that it has to abort the currently running | ||
| 42 | * task as soon as possible (i.e. as soon as it can stop the device | ||
| 43 | * safely), even if the task would not have been finished by then. | ||
| 44 | * After the driver performs the necessary steps, it has to call | ||
| 45 | * exynos_drm_ipp_task_done() (as if the task ended normally). | ||
| 46 | * This function does not have to (and will usually not) wait | ||
| 47 | * until the device enters a state when it can be stopped. | ||
| 48 | */ | ||
| 49 | void (*abort)(struct exynos_drm_ipp *ipp, | ||
| 50 | struct exynos_drm_ipp_task *task); | ||
| 51 | }; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * struct exynos_drm_ipp - central picture processor module structure | ||
| 55 | */ | ||
| 56 | struct exynos_drm_ipp { | ||
| 57 | struct drm_device *dev; | ||
| 58 | struct list_head head; | ||
| 59 | unsigned int id; | ||
| 60 | |||
| 61 | const char *name; | ||
| 62 | const struct exynos_drm_ipp_funcs *funcs; | ||
| 63 | unsigned int capabilities; | ||
| 64 | const struct exynos_drm_ipp_formats *formats; | ||
| 65 | unsigned int num_formats; | ||
| 66 | atomic_t sequence; | ||
| 67 | |||
| 68 | spinlock_t lock; | ||
| 69 | struct exynos_drm_ipp_task *task; | ||
| 70 | struct list_head todo_list; | ||
| 71 | wait_queue_head_t done_wq; | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct exynos_drm_ipp_buffer { | ||
| 75 | struct drm_exynos_ipp_task_buffer buf; | ||
| 76 | struct drm_exynos_ipp_task_rect rect; | ||
| 77 | |||
| 78 | struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER]; | ||
| 79 | const struct drm_format_info *format; | ||
| 80 | dma_addr_t dma_addr[MAX_FB_BUFFER]; | ||
| 81 | }; | ||
| 82 | |||
| 83 | /** | ||
| 84 | * struct exynos_drm_ipp_task - a structure describing transformation that | ||
| 85 | * has to be performed by the picture processor hardware module | ||
| 86 | */ | ||
| 87 | struct exynos_drm_ipp_task { | ||
| 88 | struct drm_device *dev; | ||
| 89 | struct exynos_drm_ipp *ipp; | ||
| 90 | struct list_head head; | ||
| 91 | |||
| 92 | struct exynos_drm_ipp_buffer src; | ||
| 93 | struct exynos_drm_ipp_buffer dst; | ||
| 94 | |||
| 95 | struct drm_exynos_ipp_task_transform transform; | ||
| 96 | struct drm_exynos_ipp_task_alpha alpha; | ||
| 97 | |||
| 98 | struct work_struct cleanup_work; | ||
| 99 | unsigned int flags; | ||
| 100 | int ret; | ||
| 101 | |||
| 102 | struct drm_pending_exynos_ipp_event *event; | ||
| 103 | }; | ||
| 104 | |||
| 105 | #define DRM_EXYNOS_IPP_TASK_DONE (1 << 0) | ||
| 106 | #define DRM_EXYNOS_IPP_TASK_ASYNC (1 << 1) | ||
| 107 | |||
| 108 | struct exynos_drm_ipp_formats { | ||
| 109 | uint32_t fourcc; | ||
| 110 | uint32_t type; | ||
| 111 | uint64_t modifier; | ||
| 112 | const struct drm_exynos_ipp_limit *limits; | ||
| 113 | unsigned int num_limits; | ||
| 114 | }; | ||
| 115 | |||
| 116 | /* helper macros to set exynos_drm_ipp_formats structure and limits*/ | ||
| 117 | #define IPP_SRCDST_MFORMAT(f, m, l) \ | ||
| 118 | .fourcc = DRM_FORMAT_##f, .modifier = m, .limits = l, \ | ||
| 119 | .num_limits = ARRAY_SIZE(l), \ | ||
| 120 | .type = (DRM_EXYNOS_IPP_FORMAT_SOURCE | \ | ||
| 121 | DRM_EXYNOS_IPP_FORMAT_DESTINATION) | ||
| 122 | |||
| 123 | #define IPP_SRCDST_FORMAT(f, l) IPP_SRCDST_MFORMAT(f, 0, l) | ||
| 124 | |||
| 125 | #define IPP_SIZE_LIMIT(l, val...) \ | ||
| 126 | .type = (DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE | \ | ||
| 127 | DRM_EXYNOS_IPP_LIMIT_SIZE_##l), val | ||
| 128 | |||
| 129 | #define IPP_SCALE_LIMIT(val...) \ | ||
| 130 | .type = (DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE), val | ||
| 131 | |||
| 132 | int exynos_drm_ipp_register(struct drm_device *dev, struct exynos_drm_ipp *ipp, | ||
| 133 | const struct exynos_drm_ipp_funcs *funcs, unsigned int caps, | ||
| 134 | const struct exynos_drm_ipp_formats *formats, | ||
| 135 | unsigned int num_formats, const char *name); | ||
| 136 | void exynos_drm_ipp_unregister(struct drm_device *dev, | ||
| 137 | struct exynos_drm_ipp *ipp); | ||
| 138 | |||
| 139 | void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret); | ||
| 140 | |||
| 141 | #ifdef CONFIG_DRM_EXYNOS_IPP | ||
| 142 | int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data, | ||
| 143 | struct drm_file *file_priv); | ||
| 144 | int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data, | ||
| 145 | struct drm_file *file_priv); | ||
| 146 | int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data, | ||
| 147 | struct drm_file *file_priv); | ||
| 148 | int exynos_drm_ipp_commit_ioctl(struct drm_device *dev, | ||
| 149 | void *data, struct drm_file *file_priv); | ||
| 150 | #else | ||
| 151 | static inline int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, | ||
| 152 | void *data, struct drm_file *file_priv) | ||
| 153 | { | ||
| 154 | struct drm_exynos_ioctl_ipp_get_res *resp = data; | ||
| 155 | |||
| 156 | resp->count_ipps = 0; | ||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | static inline int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, | ||
| 160 | void *data, struct drm_file *file_priv) | ||
| 161 | { | ||
| 162 | return -ENODEV; | ||
| 163 | } | ||
| 164 | static inline int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, | ||
| 165 | void *data, struct drm_file *file_priv) | ||
| 166 | { | ||
| 167 | return -ENODEV; | ||
| 168 | } | ||
| 169 | static inline int exynos_drm_ipp_commit_ioctl(struct drm_device *dev, | ||
| 170 | void *data, struct drm_file *file_priv) | ||
| 171 | { | ||
| 172 | return -ENODEV; | ||
| 173 | } | ||
| 174 | #endif | ||
| 175 | #endif | ||
| @@ -10,6 +10,7 @@ · 9 unmodified lines | |||
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
| 13 | #include <linux/component.h> | ||
| 13 | #include <linux/err.h> | 14 | #include <linux/err.h> |
| 14 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
| 15 | #include <linux/io.h> | 16 | #include <linux/io.h> |
| @@ -22,29 +23,18 @@ · 6 unmodified lines | |||
| 22 | #include <drm/exynos_drm.h> | 23 | #include <drm/exynos_drm.h> |
| 23 | #include "regs-rotator.h" | 24 | #include "regs-rotator.h" |
| 24 | #include "exynos_drm_drv.h" | 25 | #include "exynos_drm_drv.h" |
| 26 | #include "exynos_drm_iommu.h" | ||
| 25 | #include "exynos_drm_ipp.h" | 27 | #include "exynos_drm_ipp.h" |
| 26 | 28 | ||
| 27 | /* | 29 | /* |
| 28 | * Rotator supports image crop/rotator and input/output DMA operations. | 30 | * Rotator supports image crop/rotator and input/output DMA operations. |
| 29 | * input DMA reads image data from the memory. | 31 | * input DMA reads image data from the memory. |
| 30 | * output DMA writes image data to memory. | 32 | * output DMA writes image data to memory. |
| 31 | * | ||
| 32 | * M2M operation : supports crop/scale/rotation/csc so on. | ||
| 33 | * Memory ----> Rotator H/W ----> Memory. | ||
| 34 | */ | 33 | */ |
| 35 | 34 | ||
| 36 | /* | 35 | #define ROTATOR_AUTOSUSPEND_DELAY 2000 |
| 37 | * TODO | ||
| 38 | * 1. check suspend/resume api if needed. | ||
| 39 | * 2. need to check use case platform_device_id. | ||
| 40 | * 3. check src/dst size with, height. | ||
| 41 | * 4. need to add supported list in prop_list. | ||
| 42 | */ | ||
| 43 | 36 | ||
| 44 | #define get_rot_context(dev) platform_get_drvdata(to_platform_device(dev)) | 37 | #define rot_read(offset) readl(rot->regs + (offset)) |
| 45 | #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ | ||
| 46 | struct rot_context, ippdrv); | ||
| 47 | #define rot_read(offset) readl(rot->regs + (offset)) | ||
| 48 | #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset)) | 38 | #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset)) |
| 49 | 39 | ||
| 50 | enum rot_irq_status { | 40 | enum rot_irq_status { |
| @@ -52,54 +42,28 @@ enum rot_irq_status { · 1 unmodified line | |||
| 52 | ROT_IRQ_STATUS_ILLEGAL = 9, | 42 | ROT_IRQ_STATUS_ILLEGAL = 9, |
| 53 | }; | 43 | }; |
| 54 | 44 | ||
| 55 | /* | 45 | struct rot_variant { |
| 56 | * A structure of limitation. | 46 | const struct exynos_drm_ipp_formats *formats; |
| 57 | * | 47 | unsigned int num_formats; |
| 58 | * @min_w: minimum width. | ||
| 59 | * @min_h: minimum height. | ||
| 60 | * @max_w: maximum width. | ||
| 61 | * @max_h: maximum height. | ||
| 62 | * @align: align size. | ||
| 63 | */ | ||
| 64 | struct rot_limit { | ||
| 65 | u32 min_w; | ||
| 66 | u32 min_h; | ||
| 67 | u32 max_w; | ||
| 68 | u32 max_h; | ||
| 69 | u32 align; | ||
| 70 | }; | ||
| 71 | |||
| 72 | /* | ||
| 73 | * A structure of limitation table. | ||
| 74 | * | ||
| 75 | * @ycbcr420_2p: case of YUV. | ||
| 76 | * @rgb888: case of RGB. | ||
| 77 | */ | ||
| 78 | struct rot_limit_table { | ||
| 79 | struct rot_limit ycbcr420_2p; | ||
| 80 | struct rot_limit rgb888; | ||
| 81 | }; | 48 | }; |
| 82 | 49 | ||
| 83 | /* | 50 | /* |
| 84 | * A structure of rotator context. | 51 | * A structure of rotator context. |
| 85 | * @ippdrv: prepare initialization using ippdrv. | 52 | * @ippdrv: prepare initialization using ippdrv. |
| 86 | * @regs_res: register resources. | ||
| 87 | * @regs: memory mapped io registers. | 53 | * @regs: memory mapped io registers. |
| 88 | * @clock: rotator gate clock. | 54 | * @clock: rotator gate clock. |
| 89 | * @limit_tbl: limitation of rotator. | 55 | * @limit_tbl: limitation of rotator. |
| 90 | * @irq: irq number. | 56 | * @irq: irq number. |
| 91 | * @cur_buf_id: current operation buffer id. | ||
| 92 | * @suspended: suspended state. | ||
| 93 | */ | 57 | */ |
| 94 | struct rot_context { | 58 | struct rot_context { |
| 95 | struct exynos_drm_ippdrv ippdrv; | 59 | struct exynos_drm_ipp ipp; |
| 96 | struct resource *regs_res; | 60 | struct drm_device *drm_dev; |
| 61 | struct device *dev; | ||
| 97 | void __iomem *regs; | 62 | void __iomem *regs; |
| 98 | struct clk *clock; | 63 | struct clk *clock; |
| 99 | struct rot_limit_table *limit_tbl; | 64 | const struct exynos_drm_ipp_formats *formats; |
| 100 | int irq; | 65 | unsigned int num_formats; |
| 101 | int cur_buf_id[EXYNOS_DRM_OPS_MAX]; | 66 | struct exynos_drm_ipp_task *task; |
| 102 | bool suspended; | ||
| 103 | }; | 67 | }; |
| 104 | 68 | ||
| 105 | static void rotator_reg_set_irq(struct rot_context *rot, bool enable) | 69 | static void rotator_reg_set_irq(struct rot_context *rot, bool enable) |
| @@ -114,15 +78,6 @@ static void rotator_reg_set_irq(struct rot_context *rot, bool enable) · 8 unmodified lines | |||
| 114 | rot_write(val, ROT_CONFIG); | 78 | rot_write(val, ROT_CONFIG); |
| 115 | } | 79 | } |
| 116 | 80 | ||
| 117 | static u32 rotator_reg_get_fmt(struct rot_context *rot) | ||
| 118 | { | ||
| 119 | u32 val = rot_read(ROT_CONTROL); | ||
| 120 | |||
| 121 | val &= ROT_CONTROL_FMT_MASK; | ||
| 122 | |||
| 123 | return val; | ||
| 124 | } | ||
| 125 | |||
| 126 | static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) | 81 | static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) |
| 127 | { | 82 | { |
| 128 | u32 val = rot_read(ROT_STATUS); | 83 | u32 val = rot_read(ROT_STATUS); |
| @@ -138,9 +93,6 @@ static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) · 9 unmodified lines | |||
| 138 | static irqreturn_t rotator_irq_handler(int irq, void *arg) | 93 | static irqreturn_t rotator_irq_handler(int irq, void *arg) |
| 139 | { | 94 | { |
| 140 | struct rot_context *rot = arg; | 95 | struct rot_context *rot = arg; |
| 141 | struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; | ||
| 142 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; | ||
| 143 | struct drm_exynos_ipp_event_work *event_work = c_node->event_work; | ||
| 144 | enum rot_irq_status irq_status; | 96 | enum rot_irq_status irq_status; |
| 145 | u32 val; | 97 | u32 val; |
| 146 | 98 | ||
| @@ -152,56 +104,21 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg) · 5 unmodified lines | |||
| 152 | val |= ROT_STATUS_IRQ_PENDING((u32)irq_status); | 104 | val |= ROT_STATUS_IRQ_PENDING((u32)irq_status); |
| 153 | rot_write(val, ROT_STATUS); | 105 | rot_write(val, ROT_STATUS); |
| 154 | 106 | ||
| 155 | if (irq_status == ROT_IRQ_STATUS_COMPLETE) { | 107 | if (rot->task) { |
| 156 | event_work->ippdrv = ippdrv; | 108 | struct exynos_drm_ipp_task *task = rot->task; |
| 157 | event_work->buf_id[EXYNOS_DRM_OPS_DST] = | 109 | |
| 158 | rot->cur_buf_id[EXYNOS_DRM_OPS_DST]; | 110 | rot->task = NULL; |
| 159 | queue_work(ippdrv->event_workq, &event_work->work); | 111 | pm_runtime_mark_last_busy(rot->dev); |
| 160 | } else { | 112 | pm_runtime_put_autosuspend(rot->dev); |
| 161 | DRM_ERROR("the SFR is set illegally\n"); | 113 | exynos_drm_ipp_task_done(task, |
| 114 | irq_status == ROT_IRQ_STATUS_COMPLETE ? 0 : -EINVAL); | ||
| 162 | } | 115 | } |
| 163 | 116 | ||
| 164 | return IRQ_HANDLED; | 117 | return IRQ_HANDLED; |
| 165 | } | 118 | } |
| 166 | 119 | ||
| 167 | static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize, | 120 | static void rotator_src_set_fmt(struct rot_context *rot, u32 fmt) |
| 168 | u32 *vsize) | ||
| 169 | { | 121 | { |
| 170 | struct rot_limit_table *limit_tbl = rot->limit_tbl; | ||
| 171 | struct rot_limit *limit; | ||
| 172 | u32 mask, val; | ||
| 173 | |||
| 174 | /* Get size limit */ | ||
| 175 | if (fmt == ROT_CONTROL_FMT_RGB888) | ||
| 176 | limit = &limit_tbl->rgb888; | ||
| 177 | else | ||
| 178 | limit = &limit_tbl->ycbcr420_2p; | ||
| 179 | |||
| 180 | /* Get mask for rounding to nearest aligned val */ | ||
| 181 | mask = ~((1 << limit->align) - 1); | ||
| 182 | |||
| 183 | /* Set aligned width */ | ||
| 184 | val = ROT_ALIGN(*hsize, limit->align, mask); | ||
| 185 | if (val < limit->min_w) | ||
| 186 | *hsize = ROT_MIN(limit->min_w, mask); | ||
| 187 | else if (val > limit->max_w) | ||
| 188 | *hsize = ROT_MAX(limit->max_w, mask); | ||
| 189 | else | ||
| 190 | *hsize = val; | ||
| 191 | |||
| 192 | /* Set aligned height */ | ||
| 193 | val = ROT_ALIGN(*vsize, limit->align, mask); | ||
| 194 | if (val < limit->min_h) | ||
| 195 | *vsize = ROT_MIN(limit->min_h, mask); | ||
| 196 | else if (val > limit->max_h) | ||
| 197 | *vsize = ROT_MAX(limit->max_h, mask); | ||
| 198 | else | ||
| 199 | *vsize = val; | ||
| 200 | } | ||
| 201 | |||
| 202 | static int rotator_src_set_fmt(struct device *dev, u32 fmt) | ||
| 203 | { | ||
| 204 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 205 | u32 val; | 122 | u32 val; |
| 206 | 123 | ||
| 207 | val = rot_read(ROT_CONTROL); | 124 | val = rot_read(ROT_CONTROL); |
| @@ -214,515 +131,176 @@ static int rotator_src_set_fmt(struct device *dev, u32 fmt) · 6 unmodified lines | |||
| 214 | case DRM_FORMAT_XRGB8888: | 131 | case DRM_FORMAT_XRGB8888: |
| 215 | val |= ROT_CONTROL_FMT_RGB888; | 132 | val |= ROT_CONTROL_FMT_RGB888; |
| 216 | break; | 133 | break; |
| 217 | default: | ||
| 218 | DRM_ERROR("invalid image format\n"); | ||
| 219 | return -EINVAL; | ||
| 220 | } | 134 | } |
| 221 | 135 | ||
| 222 | rot_write(val, ROT_CONTROL); | 136 | rot_write(val, ROT_CONTROL); |
| 223 | |||
| 224 | return 0; | ||
| 225 | } | 137 | } |
| 226 | 138 | ||
| 227 | static inline bool rotator_check_reg_fmt(u32 fmt) | 139 | static void rotator_src_set_buf(struct rot_context *rot, |
| 140 | struct exynos_drm_ipp_buffer *buf) | ||
| 228 | { | 141 | { |
| 229 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) || | ||
| 230 | (fmt == ROT_CONTROL_FMT_RGB888)) | ||
| 231 | return true; | ||
| 232 | |||
| 233 | return false; | ||
| 234 | } | ||
| 235 | |||
| 236 | static int rotator_src_set_size(struct device *dev, int swap, | ||
| 237 | struct drm_exynos_pos *pos, | ||
| 238 | struct drm_exynos_sz *sz) | ||
| 239 | { | ||
| 240 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 241 | u32 fmt, hsize, vsize; | ||
| 242 | u32 val; | 142 | u32 val; |
| 243 | 143 | ||
| 244 | /* Get format */ | ||
| 245 | fmt = rotator_reg_get_fmt(rot); | ||
| 246 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 247 | DRM_ERROR("invalid format.\n"); | ||
| 248 | return -EINVAL; | ||
| 249 | } | ||
| 250 | |||
| 251 | /* Align buffer size */ | ||
| 252 | hsize = sz->hsize; | ||
| 253 | vsize = sz->vsize; | ||
| 254 | rotator_align_size(rot, fmt, &hsize, &vsize); | ||
| 255 | |||
| 256 | /* Set buffer size configuration */ | 144 | /* Set buffer size configuration */ |
| 257 | val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize); | 145 | val = ROT_SET_BUF_SIZE_H(buf->buf.height) | |
| 146 | ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]); | ||
| 258 | rot_write(val, ROT_SRC_BUF_SIZE); | 147 | rot_write(val, ROT_SRC_BUF_SIZE); |
| 259 | 148 | ||
| 260 | /* Set crop image position configuration */ | 149 | /* Set crop image position configuration */ |
| 261 | val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); | 150 | val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x); |
| 262 | rot_write(val, ROT_SRC_CROP_POS); | 151 | rot_write(val, ROT_SRC_CROP_POS); |
| 263 | val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w); | 152 | val = ROT_SRC_CROP_SIZE_H(buf->rect.h) | |
| 153 | ROT_SRC_CROP_SIZE_W(buf->rect.w); | ||
| 264 | rot_write(val, ROT_SRC_CROP_SIZE); | 154 | rot_write(val, ROT_SRC_CROP_SIZE); |
| 265 | 155 | ||
| 266 | return 0; | 156 | /* Set buffer DMA address */ |
| 157 | rot_write(buf->dma_addr[0], ROT_SRC_BUF_ADDR(0)); | ||
| 158 | rot_write(buf->dma_addr[1], ROT_SRC_BUF_ADDR(1)); | ||
| 267 | } | 159 | } |
| 268 | 160 | ||
| 269 | static int rotator_src_set_addr(struct device *dev, | 161 | static void rotator_dst_set_transf(struct rot_context *rot, |
| 270 | struct drm_exynos_ipp_buf_info *buf_info, | 162 | unsigned int rotation) |
| 271 | u32 buf_id, enum drm_exynos_ipp_buf_type buf_type) | ||
| 272 | { | 163 | { |
| 273 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 274 | dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; | ||
| 275 | u32 val, fmt, hsize, vsize; | ||
| 276 | int i; | ||
| 277 | |||
| 278 | /* Set current buf_id */ | ||
| 279 | rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id; | ||
| 280 | |||
| 281 | switch (buf_type) { | ||
| 282 | case IPP_BUF_ENQUEUE: | ||
| 283 | /* Set address configuration */ | ||
| 284 | for_each_ipp_planar(i) | ||
| 285 | addr[i] = buf_info->base[i]; | ||
| 286 | |||
| 287 | /* Get format */ | ||
| 288 | fmt = rotator_reg_get_fmt(rot); | ||
| 289 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 290 | DRM_ERROR("invalid format.\n"); | ||
| 291 | return -EINVAL; | ||
| 292 | } | ||
| 293 | |||
| 294 | /* Re-set cb planar for NV12 format */ | ||
| 295 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && | ||
| 296 | !addr[EXYNOS_DRM_PLANAR_CB]) { | ||
| 297 | |||
| 298 | val = rot_read(ROT_SRC_BUF_SIZE); | ||
| 299 | hsize = ROT_GET_BUF_SIZE_W(val); | ||
| 300 | vsize = ROT_GET_BUF_SIZE_H(val); | ||
| 301 | |||
| 302 | /* Set cb planar */ | ||
| 303 | addr[EXYNOS_DRM_PLANAR_CB] = | ||
| 304 | addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; | ||
| 305 | } | ||
| 306 | |||
| 307 | for_each_ipp_planar(i) | ||
| 308 | rot_write(addr[i], ROT_SRC_BUF_ADDR(i)); | ||
| 309 | break; | ||
| 310 | case IPP_BUF_DEQUEUE: | ||
| 311 | for_each_ipp_planar(i) | ||
| 312 | rot_write(0x0, ROT_SRC_BUF_ADDR(i)); | ||
| 313 | break; | ||
| 314 | default: | ||
| 315 | /* Nothing to do */ | ||
| 316 | break; | ||
| 317 | } | ||
| 318 | |||
| 319 | return 0; | ||
| 320 | } | ||
| 321 | |||
| 322 | static int rotator_dst_set_transf(struct device *dev, | ||
| 323 | enum drm_exynos_degree degree, | ||
| 324 | enum drm_exynos_flip flip, bool *swap) | ||
| 325 | { | ||
| 326 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 327 | u32 val; | 164 | u32 val; |
| 328 | 165 | ||
| 329 | /* Set transform configuration */ | 166 | /* Set transform configuration */ |
| 330 | val = rot_read(ROT_CONTROL); | 167 | val = rot_read(ROT_CONTROL); |
| 331 | val &= ~ROT_CONTROL_FLIP_MASK; | 168 | val &= ~ROT_CONTROL_FLIP_MASK; |
| 332 | 169 | ||
| 333 | switch (flip) { | 170 | if (rotation & DRM_MODE_REFLECT_X) |
| 334 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 335 | val |= ROT_CONTROL_FLIP_VERTICAL; | ||
| 336 | break; | ||
| 337 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 338 | val |= ROT_CONTROL_FLIP_HORIZONTAL; | 171 | val |= ROT_CONTROL_FLIP_HORIZONTAL; |
| 339 | break; | 172 | if (rotation & DRM_MODE_REFLECT_Y) |
| 340 | default: | 173 | val |= ROT_CONTROL_FLIP_VERTICAL; |
| 341 | /* Flip None */ | ||
| 342 | break; | ||
| 343 | } | ||
| 344 | 174 | ||
| 345 | val &= ~ROT_CONTROL_ROT_MASK; | 175 | val &= ~ROT_CONTROL_ROT_MASK; |
| 346 | 176 | ||
| 347 | switch (degree) { | 177 | if (rotation & DRM_MODE_ROTATE_90) |
| 348 | case EXYNOS_DRM_DEGREE_90: | ||
| 349 | val |= ROT_CONTROL_ROT_90; | 178 | val |= ROT_CONTROL_ROT_90; |
| 350 | break; | 179 | else if (rotation & DRM_MODE_ROTATE_180) |
| 351 | case EXYNOS_DRM_DEGREE_180: | ||
| 352 | val |= ROT_CONTROL_ROT_180; | 180 | val |= ROT_CONTROL_ROT_180; |
| 353 | break; | 181 | else if (rotation & DRM_MODE_ROTATE_270) |
| 354 | case EXYNOS_DRM_DEGREE_270: | ||
| 355 | val |= ROT_CONTROL_ROT_270; | 182 | val |= ROT_CONTROL_ROT_270; |
| 356 | break; | ||
| 357 | default: | ||
| 358 | /* Rotation 0 Degree */ | ||
| 359 | break; | ||
| 360 | } | ||
| 361 | 183 | ||
| 362 | rot_write(val, ROT_CONTROL); | 184 | rot_write(val, ROT_CONTROL); |
| 363 | |||
| 364 | /* Check degree for setting buffer size swap */ | ||
| 365 | if ((degree == EXYNOS_DRM_DEGREE_90) || | ||
| 366 | (degree == EXYNOS_DRM_DEGREE_270)) | ||
| 367 | *swap = true; | ||
| 368 | else | ||
| 369 | *swap = false; | ||
| 370 | |||
| 371 | return 0; | ||
| 372 | } | 185 | } |
| 373 | 186 | ||
| 374 | static int rotator_dst_set_size(struct device *dev, int swap, | 187 | static void rotator_dst_set_buf(struct rot_context *rot, |
| 375 | struct drm_exynos_pos *pos, | 188 | struct exynos_drm_ipp_buffer *buf) |
| 376 | struct drm_exynos_sz *sz) | ||
| 377 | { | 189 | { |
| 378 | struct rot_context *rot = dev_get_drvdata(dev); | 190 | u32 val; |
| 379 | u32 val, fmt, hsize, vsize; | ||
| 380 | |||
| 381 | /* Get format */ | ||
| 382 | fmt = rotator_reg_get_fmt(rot); | ||
| 383 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 384 | DRM_ERROR("invalid format.\n"); | ||
| 385 | return -EINVAL; | ||
| 386 | } | ||
| 387 | |||
| 388 | /* Align buffer size */ | ||
| 389 | hsize = sz->hsize; | ||
| 390 | vsize = sz->vsize; | ||
| 391 | rotator_align_size(rot, fmt, &hsize, &vsize); | ||
| 392 | 191 | ||
| 393 | /* Set buffer size configuration */ | 192 | /* Set buffer size configuration */ |
| 394 | val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize); | 193 | val = ROT_SET_BUF_SIZE_H(buf->buf.height) | |
| 194 | ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]); | ||
| 395 | rot_write(val, ROT_DST_BUF_SIZE); | 195 | rot_write(val, ROT_DST_BUF_SIZE); |
| 396 | 196 | ||
| 397 | /* Set crop image position configuration */ | 197 | /* Set crop image position configuration */ |
| 398 | val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); | 198 | val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x); |
| 399 | rot_write(val, ROT_DST_CROP_POS); | 199 | rot_write(val, ROT_DST_CROP_POS); |
| 400 | 200 | ||
| 401 | return 0; | 201 | /* Set buffer DMA address */ |
| 202 | rot_write(buf->dma_addr[0], ROT_DST_BUF_ADDR(0)); | ||
| 203 | rot_write(buf->dma_addr[1], ROT_DST_BUF_ADDR(1)); | ||
| 402 | } | 204 | } |
| 403 | 205 | ||
| 404 | static int rotator_dst_set_addr(struct device *dev, | 206 | static void rotator_start(struct rot_context *rot) |
| 405 | struct drm_exynos_ipp_buf_info *buf_info, | ||
| 406 | u32 buf_id, enum drm_exynos_ipp_buf_type buf_type) | ||
| 407 | { | 207 | { |
| 408 | struct rot_context *rot = dev_get_drvdata(dev); | 208 | u32 val; |
| 409 | dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; | ||
| 410 | u32 val, fmt, hsize, vsize; | ||
| 411 | int i; | ||
| 412 | |||
| 413 | /* Set current buf_id */ | ||
| 414 | rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id; | ||
| 415 | |||
| 416 | switch (buf_type) { | ||
| 417 | case IPP_BUF_ENQUEUE: | ||
| 418 | /* Set address configuration */ | ||
| 419 | for_each_ipp_planar(i) | ||
| 420 | addr[i] = buf_info->base[i]; | ||
| 421 | |||
| 422 | /* Get format */ | ||
| 423 | fmt = rotator_reg_get_fmt(rot); | ||
| 424 | if (!rotator_check_reg_fmt(fmt)) { | ||
| 425 | DRM_ERROR("invalid format.\n"); | ||
| 426 | return -EINVAL; | ||
| 427 | } | ||
| 428 | |||
| 429 | /* Re-set cb planar for NV12 format */ | ||
| 430 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && | ||
| 431 | !addr[EXYNOS_DRM_PLANAR_CB]) { | ||
| 432 | /* Get buf size */ | ||
| 433 | val = rot_read(ROT_DST_BUF_SIZE); | ||
| 434 | |||
| 435 | hsize = ROT_GET_BUF_SIZE_W(val); | ||
| 436 | vsize = ROT_GET_BUF_SIZE_H(val); | ||
| 437 | |||
| 438 | /* Set cb planar */ | ||
| 439 | addr[EXYNOS_DRM_PLANAR_CB] = | ||
| 440 | addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; | ||
| 441 | } | ||
| 442 | |||
| 443 | for_each_ipp_planar(i) | ||
| 444 | rot_write(addr[i], ROT_DST_BUF_ADDR(i)); | ||
| 445 | break; | ||
| 446 | case IPP_BUF_DEQUEUE: | ||
| 447 | for_each_ipp_planar(i) | ||
| 448 | rot_write(0x0, ROT_DST_BUF_ADDR(i)); | ||
| 449 | break; | ||
| 450 | default: | ||
| 451 | /* Nothing to do */ | ||
| 452 | break; | ||
| 453 | } | ||
| 454 | 209 | ||
| 455 | return 0; | 210 | /* Set interrupt enable */ |
| 211 | rotator_reg_set_irq(rot, true); | ||
| 212 | |||
| 213 | val = rot_read(ROT_CONTROL); | ||
| 214 | val |= ROT_CONTROL_START; | ||
| 215 | rot_write(val, ROT_CONTROL); | ||
| 456 | } | 216 | } |
| 457 | 217 | ||
| 458 | static struct exynos_drm_ipp_ops rot_src_ops = { | 218 | static int rotator_commit(struct exynos_drm_ipp *ipp, |
| 459 | .set_fmt = rotator_src_set_fmt, | 219 | struct exynos_drm_ipp_task *task) |
| 460 | .set_size = rotator_src_set_size, | 220 | { |
| 461 | .set_addr = rotator_src_set_addr, | 221 | struct rot_context *rot = |
| 462 | }; | 222 | container_of(ipp, struct rot_context, ipp); |
| 463 | 223 | ||
| 464 | static struct exynos_drm_ipp_ops rot_dst_ops = { | 224 | pm_runtime_get_sync(rot->dev); |
| 465 | .set_transf = rotator_dst_set_transf, | 225 | rot->task = task; |
| 466 | .set_size = rotator_dst_set_size, | ||
| 467 | .set_addr = rotator_dst_set_addr, | ||
| 468 | }; | ||
| 469 | 226 | ||
| 470 | static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv) | 227 | rotator_src_set_fmt(rot, task->src.buf.fourcc); |
| 471 | { | 228 | rotator_src_set_buf(rot, &task->src); |
| 472 | struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list; | 229 | rotator_dst_set_transf(rot, task->transform.rotation); |
| 473 | 230 | rotator_dst_set_buf(rot, &task->dst); | |
| 474 | prop_list->version = 1; | 231 | rotator_start(rot); |
| 475 | prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) | | ||
| 476 | (1 << EXYNOS_DRM_FLIP_HORIZONTAL); | ||
| 477 | prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | | ||
| 478 | (1 << EXYNOS_DRM_DEGREE_90) | | ||
| 479 | (1 << EXYNOS_DRM_DEGREE_180) | | ||
| 480 | (1 << EXYNOS_DRM_DEGREE_270); | ||
| 481 | prop_list->csc = 0; | ||
| 482 | prop_list->crop = 0; | ||
| 483 | prop_list->scale = 0; | ||
| 484 | 232 | ||
| 485 | return 0; | 233 | return 0; |
| 486 | } | 234 | } |
| 487 | 235 | ||
| 488 | static inline bool rotator_check_drm_fmt(u32 fmt) | 236 | static const struct exynos_drm_ipp_funcs ipp_funcs = { |
| 489 | { | 237 | .commit = rotator_commit, |
| 490 | switch (fmt) { | 238 | }; |
| 491 | case DRM_FORMAT_XRGB8888: | ||
| 492 | case DRM_FORMAT_NV12: | ||
| 493 | return true; | ||
| 494 | default: | ||
| 495 | DRM_DEBUG_KMS("not support format\n"); | ||
| 496 | return false; | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip) | ||
| 501 | { | ||
| 502 | switch (flip) { | ||
| 503 | case EXYNOS_DRM_FLIP_NONE: | ||
| 504 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
| 505 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
| 506 | case EXYNOS_DRM_FLIP_BOTH: | ||
| 507 | return true; | ||
| 508 | default: | ||
| 509 | DRM_DEBUG_KMS("invalid flip\n"); | ||
| 510 | return false; | ||
| 511 | } | ||
| 512 | } | ||
| 513 | 239 | ||
| 514 | static int rotator_ippdrv_check_property(struct device *dev, | 240 | static int rotator_bind(struct device *dev, struct device *master, void *data) |
| 515 | struct drm_exynos_ipp_property *property) | ||
| 516 | { | 241 | { |
| 517 | struct drm_exynos_ipp_config *src_config = | 242 | struct rot_context *rot = dev_get_drvdata(dev); |
| 518 | &property->config[EXYNOS_DRM_OPS_SRC]; | 243 | struct drm_device *drm_dev = data; |
| 519 | struct drm_exynos_ipp_config *dst_config = | 244 | struct exynos_drm_ipp *ipp = &rot->ipp; |
| 520 | &property->config[EXYNOS_DRM_OPS_DST]; | ||
| 521 | struct drm_exynos_pos *src_pos = &src_config->pos; | ||
| 522 | struct drm_exynos_pos *dst_pos = &dst_config->pos; | ||
| 523 | struct drm_exynos_sz *src_sz = &src_config->sz; | ||
| 524 | struct drm_exynos_sz *dst_sz = &dst_config->sz; | ||
| 525 | bool swap = false; | ||
| 526 | |||
| 527 | /* Check format configuration */ | ||
| 528 | if (src_config->fmt != dst_config->fmt) { | ||
| 529 | DRM_DEBUG_KMS("not support csc feature\n"); | ||
| 530 | return -EINVAL; | ||
| 531 | } | ||
| 532 | |||
| 533 | if (!rotator_check_drm_fmt(dst_config->fmt)) { | ||
| 534 | DRM_DEBUG_KMS("invalid format\n"); | ||
| 535 | return -EINVAL; | ||
| 536 | } | ||
| 537 | |||
| 538 | /* Check transform configuration */ | ||
| 539 | if (src_config->degree != EXYNOS_DRM_DEGREE_0) { | ||
| 540 | DRM_DEBUG_KMS("not support source-side rotation\n"); | ||
| 541 | return -EINVAL; | ||
| 542 | } | ||
| 543 | |||
| 544 | switch (dst_config->degree) { | ||
| 545 | case EXYNOS_DRM_DEGREE_90: | ||
| 546 | case EXYNOS_DRM_DEGREE_270: | ||
| 547 | swap = true; | ||
| 548 | case EXYNOS_DRM_DEGREE_0: | ||
| 549 | case EXYNOS_DRM_DEGREE_180: | ||
| 550 | /* No problem */ | ||
| 551 | break; | ||
| 552 | default: | ||
| 553 | DRM_DEBUG_KMS("invalid degree\n"); | ||
| 554 | return -EINVAL; | ||
| 555 | } | ||
| 556 | |||
| 557 | if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { | ||
| 558 | DRM_DEBUG_KMS("not support source-side flip\n"); | ||
| 559 | return -EINVAL; | ||
| 560 | } | ||
| 561 | 245 | ||
| 562 | if (!rotator_check_drm_flip(dst_config->flip)) { | 246 | rot->drm_dev = drm_dev; |
| 563 | DRM_DEBUG_KMS("invalid flip\n"); | 247 | drm_iommu_attach_device(drm_dev, dev); |
| 564 | return -EINVAL; | ||
| 565 | } | ||
| 566 | 248 | ||
| 567 | /* Check size configuration */ | 249 | exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs, |
| 568 | if ((src_pos->x + src_pos->w > src_sz->hsize) || | 250 | DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE, |
| 569 | (src_pos->y + src_pos->h > src_sz->vsize)) { | 251 | rot->formats, rot->num_formats, "rotator"); |
| 570 | DRM_DEBUG_KMS("out of source buffer bound\n"); | ||
| 571 | return -EINVAL; | ||
| 572 | } | ||
| 573 | 252 | ||
| 574 | if (swap) { | 253 | dev_info(dev, "The exynos rotator has been probed successfully\n"); |
| 575 | if ((dst_pos->x + dst_pos->h > dst_sz->vsize) || | ||
| 576 | (dst_pos->y + dst_pos->w > dst_sz->hsize)) { | ||
| 577 | DRM_DEBUG_KMS("out of destination buffer bound\n"); | ||
| 578 | return -EINVAL; | ||
| 579 | } | ||
| 580 | |||
| 581 | if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { | ||
| 582 | DRM_DEBUG_KMS("not support scale feature\n"); | ||
| 583 | return -EINVAL; | ||
| 584 | } | ||
| 585 | } else { | ||
| 586 | if ((dst_pos->x + dst_pos->w > dst_sz->hsize) || | ||
| 587 | (dst_pos->y + dst_pos->h > dst_sz->vsize)) { | ||
| 588 | DRM_DEBUG_KMS("out of destination buffer bound\n"); | ||
| 589 | return -EINVAL; | ||
| 590 | } | ||
| 591 | |||
| 592 | if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { | ||
| 593 | DRM_DEBUG_KMS("not support scale feature\n"); | ||
| 594 | return -EINVAL; | ||
| 595 | } | ||
| 596 | } | ||
| 597 | 254 | ||
| 598 | return 0; | 255 | return 0; |
| 599 | } | 256 | } |
| 600 | 257 | ||
| 601 | static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) | 258 | static void rotator_unbind(struct device *dev, struct device *master, |
| 259 | void *data) | ||
| 602 | { | 260 | { |
| 603 | struct rot_context *rot = dev_get_drvdata(dev); | 261 | struct rot_context *rot = dev_get_drvdata(dev); |
| 604 | u32 val; | 262 | struct drm_device *drm_dev = data; |
| 605 | 263 | struct exynos_drm_ipp *ipp = &rot->ipp; | |
| 606 | if (rot->suspended) { | ||
| 607 | DRM_ERROR("suspended state\n"); | ||
| 608 | return -EPERM; | ||
| 609 | } | ||
| 610 | |||
| 611 | if (cmd != IPP_CMD_M2M) { | ||
| 612 | DRM_ERROR("not support cmd: %d\n", cmd); | ||
| 613 | return -EINVAL; | ||
| 614 | } | ||
| 615 | |||
| 616 | /* Set interrupt enable */ | ||
| 617 | rotator_reg_set_irq(rot, true); | ||
| 618 | |||
| 619 | val = rot_read(ROT_CONTROL); | ||
| 620 | val |= ROT_CONTROL_START; | ||
| 621 | |||
| 622 | rot_write(val, ROT_CONTROL); | ||
| 623 | 264 | ||
| 624 | return 0; | 265 | exynos_drm_ipp_unregister(drm_dev, ipp); |
| 266 | drm_iommu_detach_device(rot->drm_dev, rot->dev); | ||
| 625 | } | 267 | } |
| 626 | 268 | ||
| 627 | static struct rot_limit_table rot_limit_tbl_4210 = { | 269 | static const struct component_ops rotator_component_ops = { |
| 628 | .ycbcr420_2p = { | 270 | .bind = rotator_bind, |
| 629 | .min_w = 32, | 271 | .unbind = rotator_unbind, |
| 630 | .min_h = 32, | ||
| 631 | .max_w = SZ_64K, | ||
| 632 | .max_h = SZ_64K, | ||
| 633 | .align = 3, | ||
| 634 | }, | ||
| 635 | .rgb888 = { | ||
| 636 | .min_w = 8, | ||
| 637 | .min_h = 8, | ||
| 638 | .max_w = SZ_16K, | ||
| 639 | .max_h = SZ_16K, | ||
| 640 | .align = 2, | ||
| 641 | }, | ||
| 642 | }; | ||
| 643 | |||
| 644 | static struct rot_limit_table rot_limit_tbl_4x12 = { | ||
| 645 | .ycbcr420_2p = { | ||
| 646 | .min_w = 32, | ||
| 647 | .min_h = 32, | ||
| 648 | .max_w = SZ_32K, | ||
| 649 | .max_h = SZ_32K, | ||
| 650 | .align = 3, | ||
| 651 | }, | ||
| 652 | .rgb888 = { | ||
| 653 | .min_w = 8, | ||
| 654 | .min_h = 8, | ||
| 655 | .max_w = SZ_8K, | ||
| 656 | .max_h = SZ_8K, | ||
| 657 | .align = 2, | ||
| 658 | }, | ||
| 659 | }; | 272 | }; |
| 660 | 273 | ||
| 661 | static struct rot_limit_table rot_limit_tbl_5250 = { | ||
| 662 | .ycbcr420_2p = { | ||
| 663 | .min_w = 32, | ||
| 664 | .min_h = 32, | ||
| 665 | .max_w = SZ_32K, | ||
| 666 | .max_h = SZ_32K, | ||
| 667 | .align = 3, | ||
| 668 | }, | ||
| 669 | .rgb888 = { | ||
| 670 | .min_w = 8, | ||
| 671 | .min_h = 8, | ||
| 672 | .max_w = SZ_8K, | ||
| 673 | .max_h = SZ_8K, | ||
| 674 | .align = 1, | ||
| 675 | }, | ||
| 676 | }; | ||
| 677 | |||
| 678 | static const struct of_device_id exynos_rotator_match[] = { | ||
| 679 | { | ||
| 680 | .compatible = "samsung,exynos4210-rotator", | ||
| 681 | .data = &rot_limit_tbl_4210, | ||
| 682 | }, | ||
| 683 | { | ||
| 684 | .compatible = "samsung,exynos4212-rotator", | ||
| 685 | .data = &rot_limit_tbl_4x12, | ||
| 686 | }, | ||
| 687 | { | ||
| 688 | .compatible = "samsung,exynos5250-rotator", | ||
| 689 | .data = &rot_limit_tbl_5250, | ||
| 690 | }, | ||
| 691 | {}, | ||
| 692 | }; | ||
| 693 | MODULE_DEVICE_TABLE(of, exynos_rotator_match); | ||
| 694 | |||
| 695 | static int rotator_probe(struct platform_device *pdev) | 274 | static int rotator_probe(struct platform_device *pdev) |
| 696 | { | 275 | { |
| 697 | struct device *dev = &pdev->dev; | 276 | struct device *dev = &pdev->dev; |
| 277 | struct resource *regs_res; | ||
| 698 | struct rot_context *rot; | 278 | struct rot_context *rot; |
| 699 | struct exynos_drm_ippdrv *ippdrv; | 279 | const struct rot_variant *variant; |
| 280 | int irq; | ||
| 700 | int ret; | 281 | int ret; |
| 701 | 282 | ||
| 702 | if (!dev->of_node) { | ||
| 703 | dev_err(dev, "cannot find of_node.\n"); | ||
| 704 | return -ENODEV; | ||
| 705 | } | ||
| 706 | |||
| 707 | rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL); | 283 | rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL); |
| 708 | if (!rot) | 284 | if (!rot) |
| 709 | return -ENOMEM; | 285 | return -ENOMEM; |
| 710 | 286 | ||
| 711 | rot->limit_tbl = (struct rot_limit_table *) | 287 | variant = of_device_get_match_data(dev); |
| 712 | of_device_get_match_data(dev); | 288 | rot->formats = variant->formats; |
| 713 | rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 289 | rot->num_formats = variant->num_formats; |
| 714 | rot->regs = devm_ioremap_resource(dev, rot->regs_res); | 290 | rot->dev = dev; |
| 291 | regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 292 | rot->regs = devm_ioremap_resource(dev, regs_res); | ||
| 715 | if (IS_ERR(rot->regs)) | 293 | if (IS_ERR(rot->regs)) |
| 716 | return PTR_ERR(rot->regs); | 294 | return PTR_ERR(rot->regs); |
| 717 | 295 | ||
| 718 | rot->irq = platform_get_irq(pdev, 0); | 296 | irq = platform_get_irq(pdev, 0); |
| 719 | if (rot->irq < 0) { | 297 | if (irq < 0) { |
| 720 | dev_err(dev, "failed to get irq\n"); | 298 | dev_err(dev, "failed to get irq\n"); |
| 721 | return rot->irq; | 299 | return irq; |
| 722 | } | 300 | } |
| 723 | 301 | ||
| 724 | ret = devm_request_threaded_irq(dev, rot->irq, NULL, | 302 | ret = devm_request_irq(dev, irq, rotator_irq_handler, 0, dev_name(dev), |
| 725 | rotator_irq_handler, IRQF_ONESHOT, "drm_rotator", rot); | 303 | rot); |
| 726 | if (ret < 0) { | 304 | if (ret < 0) { |
| 727 | dev_err(dev, "failed to request irq\n"); | 305 | dev_err(dev, "failed to request irq\n"); |
| 728 | return ret; | 306 | return ret; |
| @@ -734,35 +312,19 @@ static int rotator_probe(struct platform_device *pdev) · 5 unmodified lines | |||
| 734 | return PTR_ERR(rot->clock); | 312 | return PTR_ERR(rot->clock); |
| 735 | } | 313 | } |
| 736 | 314 | ||
| 315 | pm_runtime_use_autosuspend(dev); | ||
| 316 | pm_runtime_set_autosuspend_delay(dev, ROTATOR_AUTOSUSPEND_DELAY); | ||
| 737 | pm_runtime_enable(dev); | 317 | pm_runtime_enable(dev); |
| 738 | |||
| 739 | ippdrv = &rot->ippdrv; | ||
| 740 | ippdrv->dev = dev; | ||
| 741 | ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops; | ||
| 742 | ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops; | ||
| 743 | ippdrv->check_property = rotator_ippdrv_check_property; | ||
| 744 | ippdrv->start = rotator_ippdrv_start; | ||
| 745 | ret = rotator_init_prop_list(ippdrv); | ||
| 746 | if (ret < 0) { | ||
| 747 | dev_err(dev, "failed to init property list.\n"); | ||
| 748 | goto err_ippdrv_register; | ||
| 749 | } | ||
| 750 | |||
| 751 | DRM_DEBUG_KMS("ippdrv[%pK]\n", ippdrv); | ||
| 752 | |||
| 753 | platform_set_drvdata(pdev, rot); | 318 | platform_set_drvdata(pdev, rot); |
| 754 | 319 | ||
| 755 | ret = exynos_drm_ippdrv_register(ippdrv); | 320 | ret = component_add(dev, &rotator_component_ops); |
| 756 | if (ret < 0) { | 321 | if (ret) |
| 757 | dev_err(dev, "failed to register drm rotator device\n"); | 322 | goto err_component; |
| 758 | goto err_ippdrv_register; | ||
| 759 | } | ||
| 760 | |||
| 761 | dev_info(dev, "The exynos rotator is probed successfully\n"); | ||
| 762 | 323 | ||
| 763 | return 0; | 324 | return 0; |
| 764 | 325 | ||
| 765 | err_ippdrv_register: | 326 | err_component: |
| 327 | pm_runtime_dont_use_autosuspend(dev); | ||
| 766 | pm_runtime_disable(dev); | 328 | pm_runtime_disable(dev); |
| 767 | return ret; | 329 | return ret; |
| 768 | } | 330 | } |
| @@ -770,45 +332,101 @@ err_ippdrv_register: · 1 unmodified line | |||
| 770 | static int rotator_remove(struct platform_device *pdev) | 332 | static int rotator_remove(struct platform_device *pdev) |
| 771 | { | 333 | { |
| 772 | struct device *dev = &pdev->dev; | 334 | struct device *dev = &pdev->dev; |
| 773 | struct rot_context *rot = dev_get_drvdata(dev); | ||
| 774 | struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; | ||
| 775 | |||
| 776 | exynos_drm_ippdrv_unregister(ippdrv); | ||
| 777 | 335 | ||
| 336 | component_del(dev, &rotator_component_ops); | ||
| 337 | pm_runtime_dont_use_autosuspend(dev); | ||
| 778 | pm_runtime_disable(dev); | 338 | pm_runtime_disable(dev); |
| 779 | 339 | ||
| 780 | return 0; | 340 | return 0; |
| 781 | } | 341 | } |
| 782 | 342 | ||
| 783 | #ifdef CONFIG_PM | 343 | #ifdef CONFIG_PM |
| 784 | static int rotator_clk_crtl(struct rot_context *rot, bool enable) | ||
| 785 | { | ||
| 786 | if (enable) { | ||
| 787 | clk_prepare_enable(rot->clock); | ||
| 788 | rot->suspended = false; | ||
| 789 | } else { | ||
| 790 | clk_disable_unprepare(rot->clock); | ||
| 791 | rot->suspended = true; | ||
| 792 | } | ||
| 793 | |||
| 794 | return 0; | ||
| 795 | } | ||
| 796 | |||
| 797 | static int rotator_runtime_suspend(struct device *dev) | 344 | static int rotator_runtime_suspend(struct device *dev) |
| 798 | { | 345 | { |
| 799 | struct rot_context *rot = dev_get_drvdata(dev); | 346 | struct rot_context *rot = dev_get_drvdata(dev); |
| 800 | 347 | ||
| 801 | return rotator_clk_crtl(rot, false); | 348 | clk_disable_unprepare(rot->clock); |
| 349 | return 0; | ||
| 802 | } | 350 | } |
| 803 | 351 | ||
| 804 | static int rotator_runtime_resume(struct device *dev) | 352 | static int rotator_runtime_resume(struct device *dev) |
| 805 | { | 353 | { |
| 806 | struct rot_context *rot = dev_get_drvdata(dev); | 354 | struct rot_context *rot = dev_get_drvdata(dev); |
| 807 | 355 | ||
| 808 | return rotator_clk_crtl(rot, true); | 356 | return clk_prepare_enable(rot->clock); |
| 809 | } | 357 | } |
| 810 | #endif | 358 | #endif |
| 811 | 359 | ||
| 360 | static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits[] = { | ||
| 361 | { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) }, | ||
| 362 | { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) }, | ||
| 363 | }; | ||
| 364 | |||
| 365 | static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits[] = { | ||
| 366 | { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) }, | ||
| 367 | { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) }, | ||
| 368 | }; | ||
| 369 | |||
| 370 | static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits[] = { | ||
| 371 | { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) }, | ||
| 372 | { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) }, | ||
| 373 | }; | ||
| 374 | |||
| 375 | static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits[] = { | ||
| 376 | { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) }, | ||
| 377 | { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) }, | ||
| 378 | }; | ||
| 379 | |||
| 380 | static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits[] = { | ||
| 381 | { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_32K }, .v = { 32, SZ_32K }) }, | ||
| 382 | { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) }, | ||
| 383 | }; | ||
| 384 | |||
| 385 | static const struct exynos_drm_ipp_formats rotator_4210_formats[] = { | ||
| 386 | { IPP_SRCDST_FORMAT(XRGB8888, rotator_4210_rbg888_limits) }, | ||
| 387 | { IPP_SRCDST_FORMAT(NV12, rotator_4210_yuv_limits) }, | ||
| 388 | }; | ||
| 389 | |||
| 390 | static const struct exynos_drm_ipp_formats rotator_4412_formats[] = { | ||
| 391 | { IPP_SRCDST_FORMAT(XRGB8888, rotator_4412_rbg888_limits) }, | ||
| 392 | { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) }, | ||
| 393 | }; | ||
| 394 | |||
| 395 | static const struct exynos_drm_ipp_formats rotator_5250_formats[] = { | ||
| 396 | { IPP_SRCDST_FORMAT(XRGB8888, rotator_5250_rbg888_limits) }, | ||
| 397 | { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) }, | ||
| 398 | }; | ||
| 399 | |||
| 400 | static const struct rot_variant rotator_4210_data = { | ||
| 401 | .formats = rotator_4210_formats, | ||
| 402 | .num_formats = ARRAY_SIZE(rotator_4210_formats), | ||
| 403 | }; | ||
| 404 | |||
| 405 | static const struct rot_variant rotator_4412_data = { | ||
| 406 | .formats = rotator_4412_formats, | ||
| 407 | .num_formats = ARRAY_SIZE(rotator_4412_formats), | ||
| 408 | }; | ||
| 409 | |||
| 410 | static const struct rot_variant rotator_5250_data = { | ||
| 411 | .formats = rotator_5250_formats, | ||
| 412 | .num_formats = ARRAY_SIZE(rotator_5250_formats), | ||
| 413 | }; | ||
| 414 | |||
| 415 | static const struct of_device_id exynos_rotator_match[] = { | ||
| 416 | { | ||
| 417 | .compatible = "samsung,exynos4210-rotator", | ||
| 418 | .data = &rotator_4210_data, | ||
| 419 | }, { | ||
| 420 | .compatible = "samsung,exynos4212-rotator", | ||
| 421 | .data = &rotator_4412_data, | ||
| 422 | }, { | ||
| 423 | .compatible = "samsung,exynos5250-rotator", | ||
| 424 | .data = &rotator_5250_data, | ||
| 425 | }, { | ||
| 426 | }, | ||
| 427 | }; | ||
| 428 | MODULE_DEVICE_TABLE(of, exynos_rotator_match); | ||
| 429 | |||
| 812 | static const struct dev_pm_ops rotator_pm_ops = { | 430 | static const struct dev_pm_ops rotator_pm_ops = { |
| 813 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | 431 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
| 814 | pm_runtime_force_resume) | 432 | pm_runtime_force_resume) |
| @@ -820,7 +438,7 @@ struct platform_driver rotator_driver = { · 5 unmodified lines | |||
| 820 | .probe = rotator_probe, | 438 | .probe = rotator_probe, |
| 821 | .remove = rotator_remove, | 439 | .remove = rotator_remove, |
| 822 | .driver = { | 440 | .driver = { |
| 823 | .name = "exynos-rot", | 441 | .name = "exynos-rotator", |
| 824 | .owner = THIS_MODULE, | 442 | .owner = THIS_MODULE, |
| 825 | .pm = &rotator_pm_ops, | 443 | .pm = &rotator_pm_ops, |
| 826 | .of_match_table = exynos_rotator_match, | 444 | .of_match_table = exynos_rotator_match, |
| @@ -0,0 +1,694 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2017 Samsung Electronics Co.Ltd | ||
| 3 | * Author: | ||
| 4 | * Andrzej Pietrasiewicz <andrzej.p@samsung.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundationr | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/component.h> | ||
| 13 | #include <linux/err.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/io.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/of_device.h> | ||
| 19 | #include <linux/pm_runtime.h> | ||
| 20 | |||
| 21 | #include <drm/drmP.h> | ||
| 22 | #include <drm/exynos_drm.h> | ||
| 23 | #include "regs-scaler.h" | ||
| 24 | #include "exynos_drm_fb.h" | ||
| 25 | #include "exynos_drm_drv.h" | ||
| 26 | #include "exynos_drm_iommu.h" | ||
| 27 | #include "exynos_drm_ipp.h" | ||
| 28 | |||
| 29 | #define scaler_read(offset) readl(scaler->regs + (offset)) | ||
| 30 | #define scaler_write(cfg, offset) writel(cfg, scaler->regs + (offset)) | ||
| 31 | #define SCALER_MAX_CLK 4 | ||
| 32 | #define SCALER_AUTOSUSPEND_DELAY 2000 | ||
| 33 | |||
| 34 | struct scaler_data { | ||
| 35 | const char *clk_name[SCALER_MAX_CLK]; | ||
| 36 | unsigned int num_clk; | ||
| 37 | const struct exynos_drm_ipp_formats *formats; | ||
| 38 | unsigned int num_formats; | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct scaler_context { | ||
| 42 | struct exynos_drm_ipp ipp; | ||
| 43 | struct drm_device *drm_dev; | ||
| 44 | struct device *dev; | ||
| 45 | void __iomem *regs; | ||
| 46 | struct clk *clock[SCALER_MAX_CLK]; | ||
| 47 | struct exynos_drm_ipp_task *task; | ||
| 48 | const struct scaler_data *scaler_data; | ||
| 49 | }; | ||
| 50 | |||
| 51 | static u32 scaler_get_format(u32 drm_fmt) | ||
| 52 | { | ||
| 53 | switch (drm_fmt) { | ||
| 54 | case DRM_FORMAT_NV21: | ||
| 55 | return SCALER_YUV420_2P_UV; | ||
| 56 | case DRM_FORMAT_NV12: | ||
| 57 | return SCALER_YUV420_2P_VU; | ||
| 58 | case DRM_FORMAT_YUV420: | ||
| 59 | return SCALER_YUV420_3P; | ||
| 60 | case DRM_FORMAT_YUYV: | ||
| 61 | return SCALER_YUV422_1P_YUYV; | ||
| 62 | case DRM_FORMAT_UYVY: | ||
| 63 | return SCALER_YUV422_1P_UYVY; | ||
| 64 | case DRM_FORMAT_YVYU: | ||
| 65 | return SCALER_YUV422_1P_YVYU; | ||
| 66 | case DRM_FORMAT_NV61: | ||
| 67 | return SCALER_YUV422_2P_UV; | ||
| 68 | case DRM_FORMAT_NV16: | ||
| 69 | return SCALER_YUV422_2P_VU; | ||
| 70 | case DRM_FORMAT_YUV422: | ||
| 71 | return SCALER_YUV422_3P; | ||
| 72 | case DRM_FORMAT_NV42: | ||
| 73 | return SCALER_YUV444_2P_UV; | ||
| 74 | case DRM_FORMAT_NV24: | ||
| 75 | return SCALER_YUV444_2P_VU; | ||
| 76 | case DRM_FORMAT_YUV444: | ||
| 77 | return SCALER_YUV444_3P; | ||
| 78 | case DRM_FORMAT_RGB565: | ||
| 79 | return SCALER_RGB_565; | ||
| 80 | case DRM_FORMAT_XRGB1555: | ||
| 81 | return SCALER_ARGB1555; | ||
| 82 | case DRM_FORMAT_ARGB1555: | ||
| 83 | return SCALER_ARGB1555; | ||
| 84 | case DRM_FORMAT_XRGB4444: | ||
| 85 | return SCALER_ARGB4444; | ||
| 86 | case DRM_FORMAT_ARGB4444: | ||
| 87 | return SCALER_ARGB4444; | ||
| 88 | case DRM_FORMAT_XRGB8888: | ||
| 89 | return SCALER_ARGB8888; | ||
| 90 | case DRM_FORMAT_ARGB8888: | ||
| 91 | return SCALER_ARGB8888; | ||
| 92 | case DRM_FORMAT_RGBX8888: | ||
| 93 | return SCALER_RGBA8888; | ||
| 94 | case DRM_FORMAT_RGBA8888: | ||
| 95 | return SCALER_RGBA8888; | ||
| 96 | default: | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | |||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline void scaler_enable_int(struct scaler_context *scaler) | ||
| 104 | { | ||
| 105 | u32 val; | ||
| 106 | |||
| 107 | val = SCALER_INT_EN_TIMEOUT | | ||
| 108 | SCALER_INT_EN_ILLEGAL_BLEND | | ||
| 109 | SCALER_INT_EN_ILLEGAL_RATIO | | ||
| 110 | SCALER_INT_EN_ILLEGAL_DST_HEIGHT | | ||
| 111 | SCALER_INT_EN_ILLEGAL_DST_WIDTH | | ||
| 112 | SCALER_INT_EN_ILLEGAL_DST_V_POS | | ||
| 113 | SCALER_INT_EN_ILLEGAL_DST_H_POS | | ||
| 114 | SCALER_INT_EN_ILLEGAL_DST_C_SPAN | | ||
| 115 | SCALER_INT_EN_ILLEGAL_DST_Y_SPAN | | ||
| 116 | SCALER_INT_EN_ILLEGAL_DST_CR_BASE | | ||
| 117 | SCALER_INT_EN_ILLEGAL_DST_CB_BASE | | ||
| 118 | SCALER_INT_EN_ILLEGAL_DST_Y_BASE | | ||
| 119 | SCALER_INT_EN_ILLEGAL_DST_COLOR | | ||
| 120 | SCALER_INT_EN_ILLEGAL_SRC_HEIGHT | | ||
| 121 | SCALER_INT_EN_ILLEGAL_SRC_WIDTH | | ||
| 122 | SCALER_INT_EN_ILLEGAL_SRC_CV_POS | | ||
| 123 | SCALER_INT_EN_ILLEGAL_SRC_CH_POS | | ||
| 124 | SCALER_INT_EN_ILLEGAL_SRC_YV_POS | | ||
| 125 | SCALER_INT_EN_ILLEGAL_SRC_YH_POS | | ||
| 126 | SCALER_INT_EN_ILLEGAL_DST_SPAN | | ||
| 127 | SCALER_INT_EN_ILLEGAL_SRC_Y_SPAN | | ||
| 128 | SCALER_INT_EN_ILLEGAL_SRC_CR_BASE | | ||
| 129 | SCALER_INT_EN_ILLEGAL_SRC_CB_BASE | | ||
| 130 | SCALER_INT_EN_ILLEGAL_SRC_Y_BASE | | ||
| 131 | SCALER_INT_EN_ILLEGAL_SRC_COLOR | | ||
| 132 | SCALER_INT_EN_FRAME_END; | ||
| 133 | scaler_write(val, SCALER_INT_EN); | ||
| 134 | } | ||
| 135 | |||
| 136 | static inline void scaler_set_src_fmt(struct scaler_context *scaler, | ||
| 137 | u32 src_fmt) | ||
| 138 | { | ||
| 139 | u32 val; | ||
| 140 | |||
| 141 | val = SCALER_SRC_CFG_SET_COLOR_FORMAT(src_fmt); | ||
| 142 | scaler_write(val, SCALER_SRC_CFG); | ||
| 143 | } | ||
| 144 | |||
| 145 | static inline void scaler_set_src_base(struct scaler_context *scaler, | ||
| 146 | struct exynos_drm_ipp_buffer *src_buf) | ||
| 147 | { | ||
| 148 | static unsigned int bases[] = { | ||
| 149 | SCALER_SRC_Y_BASE, | ||
| 150 | SCALER_SRC_CB_BASE, | ||
| 151 | SCALER_SRC_CR_BASE, | ||
| 152 | }; | ||
| 153 | int i; | ||
| 154 | |||
| 155 | for (i = 0; i < src_buf->format->num_planes; ++i) | ||
| 156 | scaler_write(src_buf->dma_addr[i], bases[i]); | ||
| 157 | } | ||
| 158 | |||
| 159 | static inline void scaler_set_src_span(struct scaler_context *scaler, | ||
| 160 | struct exynos_drm_ipp_buffer *src_buf) | ||
| 161 | { | ||
| 162 | u32 val; | ||
| 163 | |||
| 164 | val = SCALER_SRC_SPAN_SET_Y_SPAN(src_buf->buf.pitch[0] / | ||
| 165 | src_buf->format->cpp[0]); | ||
| 166 | |||
| 167 | if (src_buf->format->num_planes > 1) | ||
| 168 | val |= SCALER_SRC_SPAN_SET_C_SPAN(src_buf->buf.pitch[1]); | ||
| 169 | |||
| 170 | scaler_write(val, SCALER_SRC_SPAN); | ||
| 171 | } | ||
| 172 | |||
| 173 | static inline void scaler_set_src_luma_pos(struct scaler_context *scaler, | ||
| 174 | struct drm_exynos_ipp_task_rect *src_pos) | ||
| 175 | { | ||
| 176 | u32 val; | ||
| 177 | |||
| 178 | val = SCALER_SRC_Y_POS_SET_YH_POS(src_pos->x << 2); | ||
| 179 | val |= SCALER_SRC_Y_POS_SET_YV_POS(src_pos->y << 2); | ||
| 180 | scaler_write(val, SCALER_SRC_Y_POS); | ||
| 181 | scaler_write(val, SCALER_SRC_C_POS); /* ATTENTION! */ | ||
| 182 | } | ||
| 183 | |||
| 184 | static inline void scaler_set_src_wh(struct scaler_context *scaler, | ||
| 185 | struct drm_exynos_ipp_task_rect *src_pos) | ||
| 186 | { | ||
| 187 | u32 val; | ||
| 188 | |||
| 189 | val = SCALER_SRC_WH_SET_WIDTH(src_pos->w); | ||
| 190 | val |= SCALER_SRC_WH_SET_HEIGHT(src_pos->h); | ||
| 191 | scaler_write(val, SCALER_SRC_WH); | ||
| 192 | } | ||
| 193 | |||
| 194 | static inline void scaler_set_dst_fmt(struct scaler_context *scaler, | ||
| 195 | u32 dst_fmt) | ||
| 196 | { | ||
| 197 | u32 val; | ||
| 198 | |||
| 199 | val = SCALER_DST_CFG_SET_COLOR_FORMAT(dst_fmt); | ||
| 200 | scaler_write(val, SCALER_DST_CFG); | ||
| 201 | } | ||
| 202 | |||
| 203 | static inline void scaler_set_dst_base(struct scaler_context *scaler, | ||
| 204 | struct exynos_drm_ipp_buffer *dst_buf) | ||
| 205 | { | ||
| 206 | static unsigned int bases[] = { | ||
| 207 | SCALER_DST_Y_BASE, | ||
| 208 | SCALER_DST_CB_BASE, | ||
| 209 | SCALER_DST_CR_BASE, | ||
| 210 | }; | ||
| 211 | int i; | ||
| 212 | |||
| 213 | for (i = 0; i < dst_buf->format->num_planes; ++i) | ||
| 214 | scaler_write(dst_buf->dma_addr[i], bases[i]); | ||
| 215 | } | ||
| 216 | |||
| 217 | static inline void scaler_set_dst_span(struct scaler_context *scaler, | ||
| 218 | struct exynos_drm_ipp_buffer *dst_buf) | ||
| 219 | { | ||
| 220 | u32 val; | ||
| 221 | |||
| 222 | val = SCALER_DST_SPAN_SET_Y_SPAN(dst_buf->buf.pitch[0] / | ||
| 223 | dst_buf->format->cpp[0]); | ||
| 224 | |||
| 225 | if (dst_buf->format->num_planes > 1) | ||
| 226 | val |= SCALER_DST_SPAN_SET_C_SPAN(dst_buf->buf.pitch[1]); | ||
| 227 | |||
| 228 | scaler_write(val, SCALER_DST_SPAN); | ||
| 229 | } | ||
| 230 | |||
| 231 | static inline void scaler_set_dst_luma_pos(struct scaler_context *scaler, | ||
| 232 | struct drm_exynos_ipp_task_rect *dst_pos) | ||
| 233 | { | ||
| 234 | u32 val; | ||
| 235 | |||
| 236 | val = SCALER_DST_WH_SET_WIDTH(dst_pos->w); | ||
| 237 | val |= SCALER_DST_WH_SET_HEIGHT(dst_pos->h); | ||
| 238 | scaler_write(val, SCALER_DST_WH); | ||
| 239 | } | ||
| 240 | |||
| 241 | static inline void scaler_set_dst_wh(struct scaler_context *scaler, | ||
| 242 | struct drm_exynos_ipp_task_rect *dst_pos) | ||
| 243 | { | ||
| 244 | u32 val; | ||
| 245 | |||
| 246 | val = SCALER_DST_POS_SET_H_POS(dst_pos->x); | ||
| 247 | val |= SCALER_DST_POS_SET_V_POS(dst_pos->y); | ||
| 248 | scaler_write(val, SCALER_DST_POS); | ||
| 249 | } | ||
| 250 | |||
| 251 | static inline void scaler_set_hv_ratio(struct scaler_context *scaler, | ||
| 252 | unsigned int rotation, | ||
| 253 | struct drm_exynos_ipp_task_rect *src_pos, | ||
| 254 | struct drm_exynos_ipp_task_rect *dst_pos) | ||
| 255 | { | ||
| 256 | u32 val, h_ratio, v_ratio; | ||
| 257 | |||
| 258 | if (drm_rotation_90_or_270(rotation)) { | ||
| 259 | h_ratio = (src_pos->h << 16) / dst_pos->w; | ||
| 260 | v_ratio = (src_pos->w << 16) / dst_pos->h; | ||
| 261 | } else { | ||
| 262 | h_ratio = (src_pos->w << 16) / dst_pos->w; | ||
| 263 | v_ratio = (src_pos->h << 16) / dst_pos->h; | ||
| 264 | } | ||
| 265 | |||
| 266 | val = SCALER_H_RATIO_SET(h_ratio); | ||
| 267 | scaler_write(val, SCALER_H_RATIO); | ||
| 268 | |||
| 269 | val = SCALER_V_RATIO_SET(v_ratio); | ||
| 270 | scaler_write(val, SCALER_V_RATIO); | ||
| 271 | } | ||
| 272 | |||
| 273 | static inline void scaler_set_rotation(struct scaler_context *scaler, | ||
| 274 | unsigned int rotation) | ||
| 275 | { | ||
| 276 | u32 val = 0; | ||
| 277 | |||
| 278 | if (rotation & DRM_MODE_ROTATE_90) | ||
| 279 | val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_90); | ||
| 280 | else if (rotation & DRM_MODE_ROTATE_180) | ||
| 281 | val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_180); | ||
| 282 | else if (rotation & DRM_MODE_ROTATE_270) | ||
| 283 | val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_270); | ||
| 284 | if (rotation & DRM_MODE_REFLECT_X) | ||
| 285 | val |= SCALER_ROT_CFG_FLIP_X_EN; | ||
| 286 | if (rotation & DRM_MODE_REFLECT_Y) | ||
| 287 | val |= SCALER_ROT_CFG_FLIP_Y_EN; | ||
| 288 | scaler_write(val, SCALER_ROT_CFG); | ||
| 289 | } | ||
| 290 | |||
| 291 | static inline void scaler_set_csc(struct scaler_context *scaler, | ||
| 292 | const struct drm_format_info *fmt) | ||
| 293 | { | ||
| 294 | static const u32 csc_mtx[2][3][3] = { | ||
| 295 | { /* YCbCr to RGB */ | ||
| 296 | {0x254, 0x000, 0x331}, | ||
| 297 | {0x254, 0xf38, 0xe60}, | ||
| 298 | {0x254, 0x409, 0x000}, | ||
| 299 | }, | ||
| 300 | { /* RGB to YCbCr */ | ||
| 301 | {0x084, 0x102, 0x032}, | ||
| 302 | {0xfb4, 0xf6b, 0x0e1}, | ||
| 303 | {0x0e1, 0xf44, 0xfdc}, | ||
| 304 | }, | ||
| 305 | }; | ||
| 306 | int i, j, dir; | ||
| 307 | |||
| 308 | switch (fmt->format) { | ||
| 309 | case DRM_FORMAT_RGB565: | ||
| 310 | case DRM_FORMAT_XRGB1555: | ||
| 311 | case DRM_FORMAT_ARGB1555: | ||
| 312 | case DRM_FORMAT_XRGB4444: | ||
| 313 | case DRM_FORMAT_ARGB4444: | ||
| 314 | case DRM_FORMAT_XRGB8888: | ||
| 315 | case DRM_FORMAT_ARGB8888: | ||
| 316 | case DRM_FORMAT_RGBX8888: | ||
| 317 | case DRM_FORMAT_RGBA8888: | ||
| 318 | dir = 1; | ||
| 319 | break; | ||
| 320 | default: | ||
| 321 | dir = 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | for (i = 0; i < 3; i++) | ||
| 325 | for (j = 0; j < 3; j++) | ||
| 326 | scaler_write(csc_mtx[dir][i][j], SCALER_CSC_COEF(j, i)); | ||
| 327 | } | ||
| 328 | |||
| 329 | static inline void scaler_set_timer(struct scaler_context *scaler, | ||
| 330 | unsigned int timer, unsigned int divider) | ||
| 331 | { | ||
| 332 | u32 val; | ||
| 333 | |||
| 334 | val = SCALER_TIMEOUT_CTRL_TIMER_ENABLE; | ||
| 335 | val |= SCALER_TIMEOUT_CTRL_SET_TIMER_VALUE(timer); | ||
| 336 | val |= SCALER_TIMEOUT_CTRL_SET_TIMER_DIV(divider); | ||
| 337 | scaler_write(val, SCALER_TIMEOUT_CTRL); | ||
| 338 | } | ||
| 339 | |||
| 340 | static inline void scaler_start_hw(struct scaler_context *scaler) | ||
| 341 | { | ||
| 342 | scaler_write(SCALER_CFG_START_CMD, SCALER_CFG); | ||
| 343 | } | ||
| 344 | |||
| 345 | static int scaler_commit(struct exynos_drm_ipp *ipp, | ||
| 346 | struct exynos_drm_ipp_task *task) | ||
| 347 | { | ||
| 348 | struct scaler_context *scaler = | ||
| 349 | container_of(ipp, struct scaler_context, ipp); | ||
| 350 | |||
| 351 | u32 src_fmt = scaler_get_format(task->src.buf.fourcc); | ||
| 352 | struct drm_exynos_ipp_task_rect *src_pos = &task->src.rect; | ||
| 353 | |||
| 354 | u32 dst_fmt = scaler_get_format(task->dst.buf.fourcc); | ||
| 355 | struct drm_exynos_ipp_task_rect *dst_pos = &task->dst.rect; | ||
| 356 | |||
| 357 | scaler->task = task; | ||
| 358 | |||
| 359 | pm_runtime_get_sync(scaler->dev); | ||
| 360 | |||
| 361 | scaler_set_src_fmt(scaler, src_fmt); | ||
| 362 | scaler_set_src_base(scaler, &task->src); | ||
| 363 | scaler_set_src_span(scaler, &task->src); | ||
| 364 | scaler_set_src_luma_pos(scaler, src_pos); | ||
| 365 | scaler_set_src_wh(scaler, src_pos); | ||
| 366 | |||
| 367 | scaler_set_dst_fmt(scaler, dst_fmt); | ||
| 368 | scaler_set_dst_base(scaler, &task->dst); | ||
| 369 | scaler_set_dst_span(scaler, &task->dst); | ||
| 370 | scaler_set_dst_luma_pos(scaler, dst_pos); | ||
| 371 | scaler_set_dst_wh(scaler, dst_pos); | ||
| 372 | |||
| 373 | scaler_set_hv_ratio(scaler, task->transform.rotation, src_pos, dst_pos); | ||
| 374 | scaler_set_rotation(scaler, task->transform.rotation); | ||
| 375 | |||
| 376 | scaler_set_csc(scaler, task->src.format); | ||
| 377 | |||
| 378 | scaler_set_timer(scaler, 0xffff, 0xf); | ||
| 379 | |||
| 380 | scaler_enable_int(scaler); | ||
| 381 | scaler_start_hw(scaler); | ||
| 382 | |||
| 383 | return 0; | ||
| 384 | } | ||
| 385 | |||
| 386 | static struct exynos_drm_ipp_funcs ipp_funcs = { | ||
| 387 | .commit = scaler_commit, | ||
| 388 | }; | ||
| 389 | |||
| 390 | static inline void scaler_disable_int(struct scaler_context *scaler) | ||
| 391 | { | ||
| 392 | scaler_write(0, SCALER_INT_EN); | ||
| 393 | } | ||
| 394 | |||
| 395 | static inline u32 scaler_get_int_status(struct scaler_context *scaler) | ||
| 396 | { | ||
| 397 | return scaler_read(SCALER_INT_STATUS); | ||
| 398 | } | ||
| 399 | |||
| 400 | static inline bool scaler_task_done(u32 val) | ||
| 401 | { | ||
| 402 | return val & SCALER_INT_STATUS_FRAME_END ? 0 : -EINVAL; | ||
| 403 | } | ||
| 404 | |||
| 405 | static irqreturn_t scaler_irq_handler(int irq, void *arg) | ||
| 406 | { | ||
| 407 | struct scaler_context *scaler = arg; | ||
| 408 | |||
| 409 | u32 val = scaler_get_int_status(scaler); | ||
| 410 | |||
| 411 | scaler_disable_int(scaler); | ||
| 412 | |||
| 413 | if (scaler->task) { | ||
| 414 | struct exynos_drm_ipp_task *task = scaler->task; | ||
| 415 | |||
| 416 | scaler->task = NULL; | ||
| 417 | pm_runtime_mark_last_busy(scaler->dev); | ||
| 418 | pm_runtime_put_autosuspend(scaler->dev); | ||
| 419 | exynos_drm_ipp_task_done(task, scaler_task_done(val)); | ||
| 420 | } | ||
| 421 | |||
| 422 | return IRQ_HANDLED; | ||
| 423 | } | ||
| 424 | |||
| 425 | static int scaler_bind(struct device *dev, struct device *master, void *data) | ||
| 426 | { | ||
| 427 | struct scaler_context *scaler = dev_get_drvdata(dev); | ||
| 428 | struct drm_device *drm_dev = data; | ||
| 429 | struct exynos_drm_ipp *ipp = &scaler->ipp; | ||
| 430 | |||
| 431 | scaler->drm_dev = drm_dev; | ||
| 432 | drm_iommu_attach_device(drm_dev, dev); | ||
| 433 | |||
| 434 | exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs, | ||
| 435 | DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE | | ||
| 436 | DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT, | ||
| 437 | scaler->scaler_data->formats, | ||
| 438 | scaler->scaler_data->num_formats, "scaler"); | ||
| 439 | |||
| 440 | dev_info(dev, "The exynos scaler has been probed successfully\n"); | ||
| 441 | |||
| 442 | return 0; | ||
| 443 | } | ||
| 444 | |||
| 445 | static void scaler_unbind(struct device *dev, struct device *master, | ||
| 446 | void *data) | ||
| 447 | { | ||
| 448 | struct scaler_context *scaler = dev_get_drvdata(dev); | ||
| 449 | struct drm_device *drm_dev = data; | ||
| 450 | struct exynos_drm_ipp *ipp = &scaler->ipp; | ||
| 451 | |||
| 452 | exynos_drm_ipp_unregister(drm_dev, ipp); | ||
| 453 | drm_iommu_detach_device(scaler->drm_dev, scaler->dev); | ||
| 454 | } | ||
| 455 | |||
| 456 | static const struct component_ops scaler_component_ops = { | ||
| 457 | .bind = scaler_bind, | ||
| 458 | .unbind = scaler_unbind, | ||
| 459 | }; | ||
| 460 | |||
| 461 | static int scaler_probe(struct platform_device *pdev) | ||
| 462 | { | ||
| 463 | struct device *dev = &pdev->dev; | ||
| 464 | struct resource *regs_res; | ||
| 465 | struct scaler_context *scaler; | ||
| 466 | int irq; | ||
| 467 | int ret, i; | ||
| 468 | |||
| 469 | scaler = devm_kzalloc(dev, sizeof(*scaler), GFP_KERNEL); | ||
| 470 | if (!scaler) | ||
| 471 | return -ENOMEM; | ||
| 472 | |||
| 473 | scaler->scaler_data = | ||
| 474 | (struct scaler_data *)of_device_get_match_data(dev); | ||
| 475 | |||
| 476 | scaler->dev = dev; | ||
| 477 | regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 478 | scaler->regs = devm_ioremap_resource(dev, regs_res); | ||
| 479 | if (IS_ERR(scaler->regs)) | ||
| 480 | return PTR_ERR(scaler->regs); | ||
| 481 | |||
| 482 | irq = platform_get_irq(pdev, 0); | ||
| 483 | if (irq < 0) { | ||
| 484 | dev_err(dev, "failed to get irq\n"); | ||
| 485 | return irq; | ||
| 486 | } | ||
| 487 | |||
| 488 | ret = devm_request_threaded_irq(dev, irq, NULL, scaler_irq_handler, | ||
| 489 | IRQF_ONESHOT, "drm_scaler", scaler); | ||
| 490 | if (ret < 0) { | ||
| 491 | dev_err(dev, "failed to request irq\n"); | ||
| 492 | return ret; | ||
| 493 | } | ||
| 494 | |||
| 495 | for (i = 0; i < scaler->scaler_data->num_clk; ++i) { | ||
| 496 | scaler->clock[i] = devm_clk_get(dev, | ||
| 497 | scaler->scaler_data->clk_name[i]); | ||
| 498 | if (IS_ERR(scaler->clock[i])) { | ||
| 499 | dev_err(dev, "failed to get clock\n"); | ||
| 500 | return PTR_ERR(scaler->clock[i]); | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | pm_runtime_use_autosuspend(dev); | ||
| 505 | pm_runtime_set_autosuspend_delay(dev, SCALER_AUTOSUSPEND_DELAY); | ||
| 506 | pm_runtime_enable(dev); | ||
| 507 | platform_set_drvdata(pdev, scaler); | ||
| 508 | |||
| 509 | ret = component_add(dev, &scaler_component_ops); | ||
| 510 | if (ret) | ||
| 511 | goto err_ippdrv_register; | ||
| 512 | |||
| 513 | return 0; | ||
| 514 | |||
| 515 | err_ippdrv_register: | ||
| 516 | pm_runtime_dont_use_autosuspend(dev); | ||
| 517 | pm_runtime_disable(dev); | ||
| 518 | return ret; | ||
| 519 | } | ||
| 520 | |||
| 521 | static int scaler_remove(struct platform_device *pdev) | ||
| 522 | { | ||
| 523 | struct device *dev = &pdev->dev; | ||
| 524 | |||
| 525 | component_del(dev, &scaler_component_ops); | ||
| 526 | pm_runtime_dont_use_autosuspend(dev); | ||
| 527 | pm_runtime_disable(dev); | ||
| 528 | |||
| 529 | return 0; | ||
| 530 | } | ||
| 531 | |||
| 532 | #ifdef CONFIG_PM | ||
| 533 | |||
| 534 | static int clk_disable_unprepare_wrapper(struct clk *clk) | ||
| 535 | { | ||
| 536 | clk_disable_unprepare(clk); | ||
| 537 | |||
| 538 | return 0; | ||
| 539 | } | ||
| 540 | |||
| 541 | static int scaler_clk_ctrl(struct scaler_context *scaler, bool enable) | ||
| 542 | { | ||
| 543 | int (*clk_fun)(struct clk *clk), i; | ||
| 544 | |||
| 545 | clk_fun = enable ? clk_prepare_enable : clk_disable_unprepare_wrapper; | ||
| 546 | |||
| 547 | for (i = 0; i < scaler->scaler_data->num_clk; ++i) | ||
| 548 | clk_fun(scaler->clock[i]); | ||
| 549 | |||
| 550 | return 0; | ||
| 551 | } | ||
| 552 | |||
| 553 | static int scaler_runtime_suspend(struct device *dev) | ||
| 554 | { | ||
| 555 | struct scaler_context *scaler = dev_get_drvdata(dev); | ||
| 556 | |||
| 557 | return scaler_clk_ctrl(scaler, false); | ||
| 558 | } | ||
| 559 | |||
| 560 | static int scaler_runtime_resume(struct device *dev) | ||
| 561 | { | ||
| 562 | struct scaler_context *scaler = dev_get_drvdata(dev); | ||
| 563 | |||
| 564 | return scaler_clk_ctrl(scaler, true); | ||
| 565 | } | ||
| 566 | #endif | ||
| 567 | |||
| 568 | static const struct dev_pm_ops scaler_pm_ops = { | ||
| 569 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | ||
| 570 | pm_runtime_force_resume) | ||
| 571 | SET_RUNTIME_PM_OPS(scaler_runtime_suspend, scaler_runtime_resume, NULL) | ||
| 572 | }; | ||
| 573 | |||
| 574 | static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_hv_limits[] = { | ||
| 575 | { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) }, | ||
| 576 | { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) }, | ||
| 577 | { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 }, | ||
| 578 | .v = { 65536 * 1 / 4, 65536 * 16 }) }, | ||
| 579 | }; | ||
| 580 | |||
| 581 | static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_h_limits[] = { | ||
| 582 | { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) }, | ||
| 583 | { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 1) }, | ||
| 584 | { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 }, | ||
| 585 | .v = { 65536 * 1 / 4, 65536 * 16 }) }, | ||
| 586 | }; | ||
| 587 | |||
| 588 | static const struct drm_exynos_ipp_limit scaler_5420_one_pixel_limits[] = { | ||
| 589 | { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) }, | ||
| 590 | { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 }, | ||
| 591 | .v = { 65536 * 1 / 4, 65536 * 16 }) }, | ||
| 592 | }; | ||
| 593 | |||
| 594 | static const struct exynos_drm_ipp_formats exynos5420_formats[] = { | ||
| 595 | /* SCALER_YUV420_2P_UV */ | ||
| 596 | { IPP_SRCDST_FORMAT(NV21, scaler_5420_two_pixel_hv_limits) }, | ||
| 597 | |||
| 598 | /* SCALER_YUV420_2P_VU */ | ||
| 599 | { IPP_SRCDST_FORMAT(NV12, scaler_5420_two_pixel_hv_limits) }, | ||
| 600 | |||
| 601 | /* SCALER_YUV420_3P */ | ||
| 602 | { IPP_SRCDST_FORMAT(YUV420, scaler_5420_two_pixel_hv_limits) }, | ||
| 603 | |||
| 604 | /* SCALER_YUV422_1P_YUYV */ | ||
| 605 | { IPP_SRCDST_FORMAT(YUYV, scaler_5420_two_pixel_h_limits) }, | ||
| 606 | |||
| 607 | /* SCALER_YUV422_1P_UYVY */ | ||
| 608 | { IPP_SRCDST_FORMAT(UYVY, scaler_5420_two_pixel_h_limits) }, | ||
| 609 | |||
| 610 | /* SCALER_YUV422_1P_YVYU */ | ||
| 611 | { IPP_SRCDST_FORMAT(YVYU, scaler_5420_two_pixel_h_limits) }, | ||
| 612 | |||
| 613 | /* SCALER_YUV422_2P_UV */ | ||
| 614 | { IPP_SRCDST_FORMAT(NV61, scaler_5420_two_pixel_h_limits) }, | ||
| 615 | |||
| 616 | /* SCALER_YUV422_2P_VU */ | ||
| 617 | { IPP_SRCDST_FORMAT(NV16, scaler_5420_two_pixel_h_limits) }, | ||
| 618 | |||
| 619 | /* SCALER_YUV422_3P */ | ||
| 620 | { IPP_SRCDST_FORMAT(YUV422, scaler_5420_two_pixel_h_limits) }, | ||
| 621 | |||
| 622 | /* SCALER_YUV444_2P_UV */ | ||
| 623 | { IPP_SRCDST_FORMAT(NV42, scaler_5420_one_pixel_limits) }, | ||
| 624 | |||
| 625 | /* SCALER_YUV444_2P_VU */ | ||
| 626 | { IPP_SRCDST_FORMAT(NV24, scaler_5420_one_pixel_limits) }, | ||
| 627 | |||
| 628 | /* SCALER_YUV444_3P */ | ||
| 629 | { IPP_SRCDST_FORMAT(YUV444, scaler_5420_one_pixel_limits) }, | ||
| 630 | |||
| 631 | /* SCALER_RGB_565 */ | ||
| 632 | { IPP_SRCDST_FORMAT(RGB565, scaler_5420_one_pixel_limits) }, | ||
| 633 | |||
| 634 | /* SCALER_ARGB1555 */ | ||
| 635 | { IPP_SRCDST_FORMAT(XRGB1555, scaler_5420_one_pixel_limits) }, | ||
| 636 | |||
| 637 | /* SCALER_ARGB1555 */ | ||
| 638 | { IPP_SRCDST_FORMAT(ARGB1555, scaler_5420_one_pixel_limits) }, | ||
| 639 | |||
| 640 | /* SCALER_ARGB4444 */ | ||
| 641 | { IPP_SRCDST_FORMAT(XRGB4444, scaler_5420_one_pixel_limits) }, | ||
| 642 | |||
| 643 | /* SCALER_ARGB4444 */ | ||
| 644 | { IPP_SRCDST_FORMAT(ARGB4444, scaler_5420_one_pixel_limits) }, | ||
| 645 | |||
| 646 | /* SCALER_ARGB8888 */ | ||
| 647 | { IPP_SRCDST_FORMAT(XRGB8888, scaler_5420_one_pixel_limits) }, | ||
| 648 | |||
| 649 | /* SCALER_ARGB8888 */ | ||
| 650 | { IPP_SRCDST_FORMAT(ARGB8888, scaler_5420_one_pixel_limits) }, | ||
| 651 | |||
| 652 | /* SCALER_RGBA8888 */ | ||
| 653 | { IPP_SRCDST_FORMAT(RGBX8888, scaler_5420_one_pixel_limits) }, | ||
| 654 | |||
| 655 | /* SCALER_RGBA8888 */ | ||
| 656 | { IPP_SRCDST_FORMAT(RGBA8888, scaler_5420_one_pixel_limits) }, | ||
| 657 | }; | ||
| 658 | |||
| 659 | static const struct scaler_data exynos5420_data = { | ||
| 660 | .clk_name = {"mscl"}, | ||
| 661 | .num_clk = 1, | ||
| 662 | .formats = exynos5420_formats, | ||
| 663 | .num_formats = ARRAY_SIZE(exynos5420_formats), | ||
| 664 | }; | ||
| 665 | |||
| 666 | static const struct scaler_data exynos5433_data = { | ||
| 667 | .clk_name = {"pclk", "aclk", "aclk_xiu"}, | ||
| 668 | .num_clk = 3, | ||
| 669 | .formats = exynos5420_formats, /* intentional */ | ||
| 670 | .num_formats = ARRAY_SIZE(exynos5420_formats), | ||
| 671 | }; | ||
| 672 | |||
| 673 | static const struct of_device_id exynos_scaler_match[] = { | ||
| 674 | { | ||
| 675 | .compatible = "samsung,exynos5420-scaler", | ||
| 676 | .data = &exynos5420_data, | ||
| 677 | }, { | ||
| 678 | .compatible = "samsung,exynos5433-scaler", | ||
| 679 | .data = &exynos5433_data, | ||
| 680 | }, { | ||
| 681 | }, | ||
| 682 | }; | ||
| 683 | MODULE_DEVICE_TABLE(of, exynos_scaler_match); | ||
| 684 | |||
| 685 | struct platform_driver scaler_driver = { | ||
| 686 | .probe = scaler_probe, | ||
| 687 | .remove = scaler_remove, | ||
| 688 | .driver = { | ||
| 689 | .name = "exynos-scaler", | ||
| 690 | .owner = THIS_MODULE, | ||
| 691 | .pm = &scaler_pm_ops, | ||
| 692 | .of_match_table = exynos_scaler_match, | ||
| 693 | }, | ||
| 694 | }; | ||
| @@ -954,8 +954,6 @@ static int hdmi_create_connector(struct drm_encoder *encoder) · 953 unmodified lines | |||
| 954 | drm_mode_connector_attach_encoder(connector, encoder); | 954 | drm_mode_connector_attach_encoder(connector, encoder); |
| 955 | 955 | ||
| 956 | if (hdata->bridge) { | 956 | if (hdata->bridge) { |
| 957 | encoder->bridge = hdata->bridge; | ||
| 958 | hdata->bridge->encoder = encoder; | ||
| 959 | ret = drm_bridge_attach(encoder, hdata->bridge, NULL); | 957 | ret = drm_bridge_attach(encoder, hdata->bridge, NULL); |
| 960 | if (ret) | 958 | if (ret) |
| 961 | DRM_ERROR("Failed to attach bridge\n"); | 959 | DRM_ERROR("Failed to attach bridge\n"); |
| @@ -473,7 +473,7 @@ static void vp_video_buffer(struct mixer_context *ctx, · 472 unmodified lines | |||
| 473 | chroma_addr[1] = chroma_addr[0] + 0x40; | 473 | chroma_addr[1] = chroma_addr[0] + 0x40; |
| 474 | } else { | 474 | } else { |
| 475 | luma_addr[1] = luma_addr[0] + fb->pitches[0]; | 475 | luma_addr[1] = luma_addr[0] + fb->pitches[0]; |
| 476 | chroma_addr[1] = chroma_addr[0] + fb->pitches[0]; | 476 | chroma_addr[1] = chroma_addr[0] + fb->pitches[1]; |
| 477 | } | 477 | } |
| 478 | } else { | 478 | } else { |
| 479 | luma_addr[1] = 0; | 479 | luma_addr[1] = 0; |
| @@ -482,6 +482,7 @@ static void vp_video_buffer(struct mixer_context *ctx, · 2 unmodified lines | |||
| 482 | 482 | ||
| 483 | spin_lock_irqsave(&ctx->reg_slock, flags); | 483 | spin_lock_irqsave(&ctx->reg_slock, flags); |
| 484 | 484 | ||
| 485 | vp_reg_write(ctx, VP_SHADOW_UPDATE, 1); | ||
| 485 | /* interlace or progressive scan mode */ | 486 | /* interlace or progressive scan mode */ |
| 486 | val = (test_bit(MXR_BIT_INTERLACE, &ctx->flags) ? ~0 : 0); | 487 | val = (test_bit(MXR_BIT_INTERLACE, &ctx->flags) ? ~0 : 0); |
| 487 | vp_reg_writemask(ctx, VP_MODE, val, VP_MODE_LINE_SKIP); | 488 | vp_reg_writemask(ctx, VP_MODE, val, VP_MODE_LINE_SKIP); |
| @@ -495,21 +496,23 @@ static void vp_video_buffer(struct mixer_context *ctx, · 7 unmodified lines | |||
| 495 | vp_reg_write(ctx, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) | | 496 | vp_reg_write(ctx, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) | |
| 496 | VP_IMG_VSIZE(fb->height)); | 497 | VP_IMG_VSIZE(fb->height)); |
| 497 | /* chroma plane for NV12/NV21 is half the height of the luma plane */ | 498 | /* chroma plane for NV12/NV21 is half the height of the luma plane */ |
| 498 | vp_reg_write(ctx, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) | | 499 | vp_reg_write(ctx, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[1]) | |
| 499 | VP_IMG_VSIZE(fb->height / 2)); | 500 | VP_IMG_VSIZE(fb->height / 2)); |
| 500 | 501 | ||
| 501 | vp_reg_write(ctx, VP_SRC_WIDTH, state->src.w); | 502 | vp_reg_write(ctx, VP_SRC_WIDTH, state->src.w); |
| 502 | vp_reg_write(ctx, VP_SRC_HEIGHT, state->src.h); | ||
| 503 | vp_reg_write(ctx, VP_SRC_H_POSITION, | 503 | vp_reg_write(ctx, VP_SRC_H_POSITION, |
| 504 | VP_SRC_H_POSITION_VAL(state->src.x)); | 504 | VP_SRC_H_POSITION_VAL(state->src.x)); |
| 505 | vp_reg_write(ctx, VP_SRC_V_POSITION, state->src.y); | ||
| 506 | |||
| 507 | vp_reg_write(ctx, VP_DST_WIDTH, state->crtc.w); | 505 | vp_reg_write(ctx, VP_DST_WIDTH, state->crtc.w); |
| 508 | vp_reg_write(ctx, VP_DST_H_POSITION, state->crtc.x); | 506 | vp_reg_write(ctx, VP_DST_H_POSITION, state->crtc.x); |
| 507 | |||
| 509 | if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) { | 508 | if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) { |
| 509 | vp_reg_write(ctx, VP_SRC_HEIGHT, state->src.h / 2); | ||
| 510 | vp_reg_write(ctx, VP_SRC_V_POSITION, state->src.y / 2); | ||
| 510 | vp_reg_write(ctx, VP_DST_HEIGHT, state->crtc.h / 2); | 511 | vp_reg_write(ctx, VP_DST_HEIGHT, state->crtc.h / 2); |
| 511 | vp_reg_write(ctx, VP_DST_V_POSITION, state->crtc.y / 2); | 512 | vp_reg_write(ctx, VP_DST_V_POSITION, state->crtc.y / 2); |
| 512 | } else { | 513 | } else { |
| 514 | vp_reg_write(ctx, VP_SRC_HEIGHT, state->src.h); | ||
| 515 | vp_reg_write(ctx, VP_SRC_V_POSITION, state->src.y); | ||
| 513 | vp_reg_write(ctx, VP_DST_HEIGHT, state->crtc.h); | 516 | vp_reg_write(ctx, VP_DST_HEIGHT, state->crtc.h); |
| 514 | vp_reg_write(ctx, VP_DST_V_POSITION, state->crtc.y); | 517 | vp_reg_write(ctx, VP_DST_V_POSITION, state->crtc.y); |
| 515 | } | 518 | } |
| @@ -699,6 +702,15 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) · 183 unmodified lines | |||
| 699 | 702 | ||
| 700 | /* interlace scan need to check shadow register */ | 703 | /* interlace scan need to check shadow register */ |
| 701 | if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) { | 704 | if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) { |
| 705 | if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags) && | ||
| 706 | vp_reg_read(ctx, VP_SHADOW_UPDATE)) | ||
| 707 | goto out; | ||
| 708 | |||
| 709 | base = mixer_reg_read(ctx, MXR_CFG); | ||
| 710 | shadow = mixer_reg_read(ctx, MXR_CFG_S); | ||
| 711 | if (base != shadow) | ||
| 712 | goto out; | ||
| 713 | |||
| 702 | base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(0)); | 714 | base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(0)); |
| 703 | shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(0)); | 715 | shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(0)); |
| 704 | if (base != shadow) | 716 | if (base != shadow) |
| @@ -47,6 +47,7 @@ · 46 unmodified lines | |||
| 47 | #define MXR_MO 0x0304 | 47 | #define MXR_MO 0x0304 |
| 48 | #define MXR_RESOLUTION 0x0310 | 48 | #define MXR_RESOLUTION 0x0310 |
| 49 | 49 | ||
| 50 | #define MXR_CFG_S 0x2004 | ||
| 50 | #define MXR_GRAPHIC0_BASE_S 0x2024 | 51 | #define MXR_GRAPHIC0_BASE_S 0x2024 |
| 51 | #define MXR_GRAPHIC1_BASE_S 0x2044 | 52 | #define MXR_GRAPHIC1_BASE_S 0x2044 |
| 52 | 53 | ||
| @@ -0,0 +1,426 @@ | |||
| 1 | /* drivers/gpu/drm/exynos/regs-scaler.h | ||
| 2 | * | ||
| 3 | * Copyright (c) 2017 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> | ||
| 6 | * | ||
| 7 | * Register definition file for Samsung scaler driver | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef EXYNOS_REGS_SCALER_H | ||
| 15 | #define EXYNOS_REGS_SCALER_H | ||
| 16 | |||
| 17 | /* Register part */ | ||
| 18 | |||
| 19 | /* Global setting */ | ||
| 20 | #define SCALER_STATUS 0x0 /* no shadow */ | ||
| 21 | #define SCALER_CFG 0x4 | ||
| 22 | |||
| 23 | /* Interrupt */ | ||
| 24 | #define SCALER_INT_EN 0x8 /* no shadow */ | ||
| 25 | #define SCALER_INT_STATUS 0xc /* no shadow */ | ||
| 26 | |||
| 27 | /* SRC */ | ||
| 28 | #define SCALER_SRC_CFG 0x10 | ||
| 29 | #define SCALER_SRC_Y_BASE 0x14 | ||
| 30 | #define SCALER_SRC_CB_BASE 0x18 | ||
| 31 | #define SCALER_SRC_CR_BASE 0x294 | ||
| 32 | #define SCALER_SRC_SPAN 0x1c | ||
| 33 | #define SCALER_SRC_Y_POS 0x20 | ||
| 34 | #define SCALER_SRC_WH 0x24 | ||
| 35 | #define SCALER_SRC_C_POS 0x28 | ||
| 36 | |||
| 37 | /* DST */ | ||
| 38 | #define SCALER_DST_CFG 0x30 | ||
| 39 | #define SCALER_DST_Y_BASE 0x34 | ||
| 40 | #define SCALER_DST_CB_BASE 0x38 | ||
| 41 | #define SCALER_DST_CR_BASE 0x298 | ||
| 42 | #define SCALER_DST_SPAN 0x3c | ||
| 43 | #define SCALER_DST_WH 0x40 | ||
| 44 | #define SCALER_DST_POS 0x44 | ||
| 45 | |||
| 46 | /* Ratio */ | ||
| 47 | #define SCALER_H_RATIO 0x50 | ||
| 48 | #define SCALER_V_RATIO 0x54 | ||
| 49 | |||
| 50 | /* Rotation */ | ||
| 51 | #define SCALER_ROT_CFG 0x58 | ||
| 52 | |||
| 53 | /* Coefficient */ | ||
| 54 | /* | ||
| 55 | * YHCOEF_{x}{A|B|C|D} CHCOEF_{x}{A|B|C|D} | ||
| 56 | * | ||
| 57 | * A B C D A B C D | ||
| 58 | * 0 60 64 68 6c 140 144 148 14c | ||
| 59 | * 1 70 74 78 7c 150 154 158 15c | ||
| 60 | * 2 80 84 88 8c 160 164 168 16c | ||
| 61 | * 3 90 94 98 9c 170 174 178 17c | ||
| 62 | * 4 a0 a4 a8 ac 180 184 188 18c | ||
| 63 | * 5 b0 b4 b8 bc 190 194 198 19c | ||
| 64 | * 6 c0 c4 c8 cc 1a0 1a4 1a8 1ac | ||
| 65 | * 7 d0 d4 d8 dc 1b0 1b4 1b8 1bc | ||
| 66 | * 8 e0 e4 e8 ec 1c0 1c4 1c8 1cc | ||
| 67 | * | ||
| 68 | * | ||
| 69 | * YVCOEF_{x}{A|B} CVCOEF_{x}{A|B} | ||
| 70 | * | ||
| 71 | * A B A B | ||
| 72 | * 0 f0 f4 1d0 1d4 | ||
| 73 | * 1 f8 fc 1d8 1dc | ||
| 74 | * 2 100 104 1e0 1e4 | ||
| 75 | * 3 108 10c 1e8 1ec | ||
| 76 | * 4 110 114 1f0 1f4 | ||
| 77 | * 5 118 11c 1f8 1fc | ||
| 78 | * 6 120 124 200 204 | ||
| 79 | * 7 128 12c 208 20c | ||
| 80 | * 8 130 134 210 214 | ||
| 81 | */ | ||
| 82 | #define _SCALER_HCOEF_DELTA(r, c) ((r) * 0x10 + (c) * 0x4) | ||
| 83 | #define _SCALER_VCOEF_DELTA(r, c) ((r) * 0x8 + (c) * 0x4) | ||
| 84 | |||
| 85 | #define SCALER_YHCOEF(r, c) (0x60 + _SCALER_HCOEF_DELTA((r), (c))) | ||
| 86 | #define SCALER_YVCOEF(r, c) (0xf0 + _SCALER_VCOEF_DELTA((r), (c))) | ||
| 87 | #define SCALER_CHCOEF(r, c) (0x140 + _SCALER_HCOEF_DELTA((r), (c))) | ||
| 88 | #define SCALER_CVCOEF(r, c) (0x1d0 + _SCALER_VCOEF_DELTA((r), (c))) | ||
| 89 | |||
| 90 | |||
| 91 | /* Color Space Conversion */ | ||
| 92 | #define SCALER_CSC_COEF(x, y) (0x220 + (y) * 0xc + (x) * 0x4) | ||
| 93 | |||
| 94 | /* Dithering */ | ||
| 95 | #define SCALER_DITH_CFG 0x250 | ||
| 96 | |||
| 97 | /* Version Number */ | ||
| 98 | #define SCALER_VER 0x260 /* no shadow */ | ||
| 99 | |||
| 100 | /* Cycle count and Timeout */ | ||
| 101 | #define SCALER_CYCLE_COUNT 0x278 /* no shadow */ | ||
| 102 | #define SCALER_TIMEOUT_CTRL 0x2c0 /* no shadow */ | ||
| 103 | #define SCALER_TIMEOUT_CNT 0x2c4 /* no shadow */ | ||
| 104 | |||
| 105 | /* Blending */ | ||
| 106 | #define SCALER_SRC_BLEND_COLOR 0x280 | ||
| 107 | #define SCALER_SRC_BLEND_ALPHA 0x284 | ||
| 108 | #define SCALER_DST_BLEND_COLOR 0x288 | ||
| 109 | #define SCALER_DST_BLEND_ALPHA 0x28c | ||
| 110 | |||
| 111 | /* Color Fill */ | ||
| 112 | #define SCALER_FILL_COLOR 0x290 | ||
| 113 | |||
| 114 | /* Multiple Command Queue */ | ||
| 115 | #define SCALER_ADDR_Q_CONFIG 0x2a0 /* no shadow */ | ||
| 116 | #define SCALER_SRC_ADDR_Q_STATUS 0x2a4 /* no shadow */ | ||
| 117 | #define SCALER_SRC_ADDR_Q 0x2a8 /* no shadow */ | ||
| 118 | |||
| 119 | /* CRC */ | ||
| 120 | #define SCALER_CRC_COLOR00_10 0x2b0 /* no shadow */ | ||
| 121 | #define SCALER_CRC_COLOR20_30 0x2b4 /* no shadow */ | ||
| 122 | #define SCALER_CRC_COLOR01_11 0x2b8 /* no shadow */ | ||
| 123 | #define SCALER_CRC_COLOR21_31 0x2bc /* no shadow */ | ||
| 124 | |||
| 125 | /* Shadow Registers */ | ||
| 126 | #define SCALER_SHADOW_OFFSET 0x1000 | ||
| 127 | |||
| 128 | |||
| 129 | /* Bit definition part */ | ||
| 130 | #define SCALER_MASK(hi_b, lo_b) ((1 << ((hi_b) - (lo_b) + 1)) - 1) | ||
| 131 | #define SCALER_GET(reg, hi_b, lo_b) \ | ||
| 132 | (((reg) >> (lo_b)) & SCALER_MASK(hi_b, lo_b)) | ||
| 133 | #define SCALER_SET(val, hi_b, lo_b) \ | ||
| 134 | (((val) & SCALER_MASK(hi_b, lo_b)) << lo_b) | ||
| 135 | |||
| 136 | /* SCALER_STATUS */ | ||
| 137 | #define SCALER_STATUS_SCALER_RUNNING (1 << 1) | ||
| 138 | #define SCALER_STATUS_SCALER_READY_CLK_DOWN (1 << 0) | ||
| 139 | |||
| 140 | /* SCALER_CFG */ | ||
| 141 | #define SCALER_CFG_FILL_EN (1 << 24) | ||
| 142 | #define SCALER_CFG_BLEND_COLOR_DIVIDE_ALPHA_EN (1 << 17) | ||
| 143 | #define SCALER_CFG_BLEND_EN (1 << 16) | ||
| 144 | #define SCALER_CFG_CSC_Y_OFFSET_SRC_EN (1 << 10) | ||
| 145 | #define SCALER_CFG_CSC_Y_OFFSET_DST_EN (1 << 9) | ||
| 146 | #define SCALER_CFG_16_BURST_MODE (1 << 8) | ||
| 147 | #define SCALER_CFG_SOFT_RESET (1 << 1) | ||
| 148 | #define SCALER_CFG_START_CMD (1 << 0) | ||
| 149 | |||
| 150 | /* SCALER_INT_EN */ | ||
| 151 | #define SCALER_INT_EN_TIMEOUT (1 << 31) | ||
| 152 | #define SCALER_INT_EN_ILLEGAL_BLEND (1 << 24) | ||
| 153 | #define SCALER_INT_EN_ILLEGAL_RATIO (1 << 23) | ||
| 154 | #define SCALER_INT_EN_ILLEGAL_DST_HEIGHT (1 << 22) | ||
| 155 | #define SCALER_INT_EN_ILLEGAL_DST_WIDTH (1 << 21) | ||
| 156 | #define SCALER_INT_EN_ILLEGAL_DST_V_POS (1 << 20) | ||
| 157 | #define SCALER_INT_EN_ILLEGAL_DST_H_POS (1 << 19) | ||
| 158 | #define SCALER_INT_EN_ILLEGAL_DST_C_SPAN (1 << 18) | ||
| 159 | #define SCALER_INT_EN_ILLEGAL_DST_Y_SPAN (1 << 17) | ||
| 160 | #define SCALER_INT_EN_ILLEGAL_DST_CR_BASE (1 << 16) | ||
| 161 | #define SCALER_INT_EN_ILLEGAL_DST_CB_BASE (1 << 15) | ||
| 162 | #define SCALER_INT_EN_ILLEGAL_DST_Y_BASE (1 << 14) | ||
| 163 | #define SCALER_INT_EN_ILLEGAL_DST_COLOR (1 << 13) | ||
| 164 | #define SCALER_INT_EN_ILLEGAL_SRC_HEIGHT (1 << 12) | ||
| 165 | #define SCALER_INT_EN_ILLEGAL_SRC_WIDTH (1 << 11) | ||
| 166 | #define SCALER_INT_EN_ILLEGAL_SRC_CV_POS (1 << 10) | ||
| 167 | #define SCALER_INT_EN_ILLEGAL_SRC_CH_POS (1 << 9) | ||
| 168 | #define SCALER_INT_EN_ILLEGAL_SRC_YV_POS (1 << 8) | ||
| 169 | #define SCALER_INT_EN_ILLEGAL_SRC_YH_POS (1 << 7) | ||
| 170 | #define SCALER_INT_EN_ILLEGAL_DST_SPAN (1 << 6) | ||
| 171 | #define SCALER_INT_EN_ILLEGAL_SRC_Y_SPAN (1 << 5) | ||
| 172 | #define SCALER_INT_EN_ILLEGAL_SRC_CR_BASE (1 << 4) | ||
| 173 | #define SCALER_INT_EN_ILLEGAL_SRC_CB_BASE (1 << 3) | ||
| 174 | #define SCALER_INT_EN_ILLEGAL_SRC_Y_BASE (1 << 2) | ||
| 175 | #define SCALER_INT_EN_ILLEGAL_SRC_COLOR (1 << 1) | ||
| 176 | #define SCALER_INT_EN_FRAME_END (1 << 0) | ||
| 177 | |||
| 178 | /* SCALER_INT_STATUS */ | ||
| 179 | #define SCALER_INT_STATUS_TIMEOUT (1 << 31) | ||
| 180 | #define SCALER_INT_STATUS_ILLEGAL_BLEND (1 << 24) | ||
| 181 | #define SCALER_INT_STATUS_ILLEGAL_RATIO (1 << 23) | ||
| 182 | #define SCALER_INT_STATUS_ILLEGAL_DST_HEIGHT (1 << 22) | ||
| 183 | #define SCALER_INT_STATUS_ILLEGAL_DST_WIDTH (1 << 21) | ||
| 184 | #define SCALER_INT_STATUS_ILLEGAL_DST_V_POS (1 << 20) | ||
| 185 | #define SCALER_INT_STATUS_ILLEGAL_DST_H_POS (1 << 19) | ||
| 186 | #define SCALER_INT_STATUS_ILLEGAL_DST_C_SPAN (1 << 18) | ||
| 187 | #define SCALER_INT_STATUS_ILLEGAL_DST_Y_SPAN (1 << 17) | ||
| 188 | #define SCALER_INT_STATUS_ILLEGAL_DST_CR_BASE (1 << 16) | ||
| 189 | #define SCALER_INT_STATUS_ILLEGAL_DST_CB_BASE (1 << 15) | ||
| 190 | #define SCALER_INT_STATUS_ILLEGAL_DST_Y_BASE (1 << 14) | ||
| 191 | #define SCALER_INT_STATUS_ILLEGAL_DST_COLOR (1 << 13) | ||
| 192 | #define SCALER_INT_STATUS_ILLEGAL_SRC_HEIGHT (1 << 12) | ||
| 193 | #define SCALER_INT_STATUS_ILLEGAL_SRC_WIDTH (1 << 11) | ||
| 194 | #define SCALER_INT_STATUS_ILLEGAL_SRC_CV_POS (1 << 10) | ||
| 195 | #define SCALER_INT_STATUS_ILLEGAL_SRC_CH_POS (1 << 9) | ||
| 196 | #define SCALER_INT_STATUS_ILLEGAL_SRC_YV_POS (1 << 8) | ||
| 197 | #define SCALER_INT_STATUS_ILLEGAL_SRC_YH_POS (1 << 7) | ||
| 198 | #define SCALER_INT_STATUS_ILLEGAL_DST_SPAN (1 << 6) | ||
| 199 | #define SCALER_INT_STATUS_ILLEGAL_SRC_Y_SPAN (1 << 5) | ||
| 200 | #define SCALER_INT_STATUS_ILLEGAL_SRC_CR_BASE (1 << 4) | ||
| 201 | #define SCALER_INT_STATUS_ILLEGAL_SRC_CB_BASE (1 << 3) | ||
| 202 | #define SCALER_INT_STATUS_ILLEGAL_SRC_Y_BASE (1 << 2) | ||
| 203 | #define SCALER_INT_STATUS_ILLEGAL_SRC_COLOR (1 << 1) | ||
| 204 | #define SCALER_INT_STATUS_FRAME_END (1 << 0) | ||
| 205 | |||
| 206 | /* SCALER_SRC_CFG */ | ||
| 207 | #define SCALER_SRC_CFG_TILE_EN (1 << 10) | ||
| 208 | #define SCALER_SRC_CFG_GET_BYTE_SWAP(r) SCALER_GET(r, 6, 5) | ||
| 209 | #define SCALER_SRC_CFG_SET_BYTE_SWAP(v) SCALER_SET(v, 6, 5) | ||
| 210 | #define SCALER_SRC_CFG_GET_COLOR_FORMAT(r) SCALER_GET(r, 4, 0) | ||
| 211 | #define SCALER_SRC_CFG_SET_COLOR_FORMAT(v) SCALER_SET(v, 4, 0) | ||
| 212 | #define SCALER_YUV420_2P_UV 0 | ||
| 213 | #define SCALER_YUV422_2P_UV 2 | ||
| 214 | #define SCALER_YUV444_2P_UV 3 | ||
| 215 | #define SCALER_RGB_565 4 | ||
| 216 | #define SCALER_ARGB1555 5 | ||
| 217 | #define SCALER_ARGB8888 6 | ||
| 218 | #define SCALER_ARGB8888_PRE 7 | ||
| 219 | #define SCALER_YUV422_1P_YVYU 9 | ||
| 220 | #define SCALER_YUV422_1P_YUYV 10 | ||
| 221 | #define SCALER_YUV422_1P_UYVY 11 | ||
| 222 | #define SCALER_ARGB4444 12 | ||
| 223 | #define SCALER_L8A8 13 | ||
| 224 | #define SCALER_RGBA8888 14 | ||
| 225 | #define SCALER_L8 15 | ||
| 226 | #define SCALER_YUV420_2P_VU 16 | ||
| 227 | #define SCALER_YUV422_2P_VU 18 | ||
| 228 | #define SCALER_YUV444_2P_VU 19 | ||
| 229 | #define SCALER_YUV420_3P 20 | ||
| 230 | #define SCALER_YUV422_3P 22 | ||
| 231 | #define SCALER_YUV444_3P 23 | ||
| 232 | |||
| 233 | /* SCALER_SRC_SPAN */ | ||
| 234 | #define SCALER_SRC_SPAN_GET_C_SPAN(r) SCALER_GET(r, 29, 16) | ||
| 235 | #define SCALER_SRC_SPAN_SET_C_SPAN(v) SCALER_SET(v, 29, 16) | ||
| 236 | #define SCALER_SRC_SPAN_GET_Y_SPAN(r) SCALER_GET(r, 13, 0) | ||
| 237 | #define SCALER_SRC_SPAN_SET_Y_SPAN(v) SCALER_SET(v, 13, 0) | ||
| 238 | |||
| 239 | /* SCALER_SRC_Y_POS */ | ||
| 240 | #define SCALER_SRC_Y_POS_GET_YH_POS(r) SCALER_GET(r, 31, 16) | ||
| 241 | #define SCALER_SRC_Y_POS_SET_YH_POS(v) SCALER_SET(v, 31, 16) | ||
| 242 | #define SCALER_SRC_Y_POS_GET_YV_POS(r) SCALER_GET(r, 15, 0) | ||
| 243 | #define SCALER_SRC_Y_POS_SET_YV_POS(v) SCALER_SET(v, 15, 0) | ||
| 244 | |||
| 245 | /* SCALER_SRC_WH */ | ||
| 246 | #define SCALER_SRC_WH_GET_WIDTH(r) SCALER_GET(r, 29, 16) | ||
| 247 | #define SCALER_SRC_WH_SET_WIDTH(v) SCALER_SET(v, 29, 16) | ||
| 248 | #define SCALER_SRC_WH_GET_HEIGHT(r) SCALER_GET(r, 13, 0) | ||
| 249 | #define SCALER_SRC_WH_SET_HEIGHT(v) SCALER_SET(v, 13, 0) | ||
| 250 | |||
| 251 | /* SCALER_SRC_C_POS */ | ||
| 252 | #define SCALER_SRC_C_POS_GET_CH_POS(r) SCALER_GET(r, 31, 16) | ||
| 253 | #define SCALER_SRC_C_POS_SET_CH_POS(v) SCALER_SET(v, 31, 16) | ||
| 254 | #define SCALER_SRC_C_POS_GET_CV_POS(r) SCALER_GET(r, 15, 0) | ||
| 255 | #define SCALER_SRC_C_POS_SET_CV_POS(v) SCALER_SET(v, 15, 0) | ||
| 256 | |||
| 257 | /* SCALER_DST_CFG */ | ||
| 258 | #define SCALER_DST_CFG_GET_BYTE_SWAP(r) SCALER_GET(r, 6, 5) | ||
| 259 | #define SCALER_DST_CFG_SET_BYTE_SWAP(v) SCALER_SET(v, 6, 5) | ||
| 260 | #define SCALER_DST_CFG_GET_COLOR_FORMAT(r) SCALER_GET(r, 4, 0) | ||
| 261 | #define SCALER_DST_CFG_SET_COLOR_FORMAT(v) SCALER_SET(v, 4, 0) | ||
| 262 | |||
| 263 | /* SCALER_DST_SPAN */ | ||
| 264 | #define SCALER_DST_SPAN_GET_C_SPAN(r) SCALER_GET(r, 29, 16) | ||
| 265 | #define SCALER_DST_SPAN_SET_C_SPAN(v) SCALER_SET(v, 29, 16) | ||
| 266 | #define SCALER_DST_SPAN_GET_Y_SPAN(r) SCALER_GET(r, 13, 0) | ||
| 267 | #define SCALER_DST_SPAN_SET_Y_SPAN(v) SCALER_SET(v, 13, 0) | ||
| 268 | |||
| 269 | /* SCALER_DST_WH */ | ||
| 270 | #define SCALER_DST_WH_GET_WIDTH(r) SCALER_GET(r, 29, 16) | ||
| 271 | #define SCALER_DST_WH_SET_WIDTH(v) SCALER_SET(v, 29, 16) | ||
| 272 | #define SCALER_DST_WH_GET_HEIGHT(r) SCALER_GET(r, 13, 0) | ||
| 273 | #define SCALER_DST_WH_SET_HEIGHT(v) SCALER_SET(v, 13, 0) | ||
| 274 | |||
| 275 | /* SCALER_DST_POS */ | ||
| 276 | #define SCALER_DST_POS_GET_H_POS(r) SCALER_GET(r, 29, 16) | ||
| 277 | #define SCALER_DST_POS_SET_H_POS(v) SCALER_SET(v, 29, 16) | ||
| 278 | #define SCALER_DST_POS_GET_V_POS(r) SCALER_GET(r, 13, 0) | ||
| 279 | #define SCALER_DST_POS_SET_V_POS(v) SCALER_SET(v, 13, 0) | ||
| 280 | |||
| 281 | /* SCALER_H_RATIO */ | ||
| 282 | #define SCALER_H_RATIO_GET(r) SCALER_GET(r, 18, 0) | ||
| 283 | #define SCALER_H_RATIO_SET(v) SCALER_SET(v, 18, 0) | ||
| 284 | |||
| 285 | /* SCALER_V_RATIO */ | ||
| 286 | #define SCALER_V_RATIO_GET(r) SCALER_GET(r, 18, 0) | ||
| 287 | #define SCALER_V_RATIO_SET(v) SCALER_SET(v, 18, 0) | ||
| 288 | |||
| 289 | /* SCALER_ROT_CFG */ | ||
| 290 | #define SCALER_ROT_CFG_FLIP_X_EN (1 << 3) | ||
| 291 | #define SCALER_ROT_CFG_FLIP_Y_EN (1 << 2) | ||
| 292 | #define SCALER_ROT_CFG_GET_ROTMODE(r) SCALER_GET(r, 1, 0) | ||
| 293 | #define SCALER_ROT_CFG_SET_ROTMODE(v) SCALER_SET(v, 1, 0) | ||
| 294 | #define SCALER_ROT_MODE_90 1 | ||
| 295 | #define SCALER_ROT_MODE_180 2 | ||
| 296 | #define SCALER_ROT_MODE_270 3 | ||
| 297 | |||
| 298 | /* SCALER_HCOEF, SCALER_VCOEF */ | ||
| 299 | #define SCALER_COEF_SHIFT(i) (16 * (1 - (i) % 2)) | ||
| 300 | #define SCALER_COEF_GET(r, i) \ | ||
| 301 | (((r) >> SCALER_COEF_SHIFT(i)) & 0x1ff) | ||
| 302 | #define SCALER_COEF_SET(v, i) \ | ||
| 303 | (((v) & 0x1ff) << SCALER_COEF_SHIFT(i)) | ||
| 304 | |||
| 305 | /* SCALER_CSC_COEFxy */ | ||
| 306 | #define SCALER_CSC_COEF_GET(r) SCALER_GET(r, 11, 0) | ||
| 307 | #define SCALER_CSC_COEF_SET(v) SCALER_SET(v, 11, 0) | ||
| 308 | |||
| 309 | /* SCALER_DITH_CFG */ | ||
| 310 | #define SCALER_DITH_CFG_GET_R_TYPE(r) SCALER_GET(r, 8, 6) | ||
| 311 | #define SCALER_DITH_CFG_SET_R_TYPE(v) SCALER_SET(v, 8, 6) | ||
| 312 | #define SCALER_DITH_CFG_GET_G_TYPE(r) SCALER_GET(r, 5, 3) | ||
| 313 | #define SCALER_DITH_CFG_SET_G_TYPE(v) SCALER_SET(v, 5, 3) | ||
| 314 | #define SCALER_DITH_CFG_GET_B_TYPE(r) SCALER_GET(r, 2, 0) | ||
| 315 | #define SCALER_DITH_CFG_SET_B_TYPE(v) SCALER_SET(v, 2, 0) | ||
| 316 | |||
| 317 | /* SCALER_TIMEOUT_CTRL */ | ||
| 318 | #define SCALER_TIMEOUT_CTRL_GET_TIMER_VALUE(r) SCALER_GET(r, 31, 16) | ||
| 319 | #define SCALER_TIMEOUT_CTRL_SET_TIMER_VALUE(v) SCALER_SET(v, 31, 16) | ||
| 320 | #define SCALER_TIMEOUT_CTRL_GET_TIMER_DIV(r) SCALER_GET(r, 7, 4) | ||
| 321 | #define SCALER_TIMEOUT_CTRL_SET_TIMER_DIV(v) SCALER_SET(v, 7, 4) | ||
| 322 | #define SCALER_TIMEOUT_CTRL_TIMER_ENABLE (1 << 0) | ||
| 323 | |||
| 324 | /* SCALER_TIMEOUT_CNT */ | ||
| 325 | #define SCALER_TIMEOUT_CTRL_GET_TIMER_COUNT(r) SCALER_GET(r, 31, 16) | ||
| 326 | |||
| 327 | /* SCALER_SRC_BLEND_COLOR */ | ||
| 328 | #define SCALER_SRC_BLEND_COLOR_SEL_INV (1 << 31) | ||
| 329 | #define SCALER_SRC_BLEND_COLOR_GET_SEL(r) SCALER_GET(r, 30, 29) | ||
| 330 | #define SCALER_SRC_BLEND_COLOR_SET_SEL(v) SCALER_SET(v, 30, 29) | ||
| 331 | #define SCALER_SRC_BLEND_COLOR_OP_SEL_INV (1 << 28) | ||
| 332 | #define SCALER_SRC_BLEND_COLOR_GET_OP_SEL(r) SCALER_GET(r, 27, 24) | ||
| 333 | #define SCALER_SRC_BLEND_COLOR_SET_OP_SEL(v) SCALER_SET(v, 27, 24) | ||
| 334 | #define SCALER_SRC_BLEND_COLOR_GET_COLOR0(r) SCALER_GET(r, 23, 16) | ||
| 335 | #define SCALER_SRC_BLEND_COLOR_SET_COLOR0(v) SCALER_SET(v, 23, 16) | ||
| 336 | #define SCALER_SRC_BLEND_COLOR_GET_COLOR1(r) SCALER_GET(r, 15, 8) | ||
| 337 | #define SCALER_SRC_BLEND_COLOR_SET_COLOR1(v) SCALER_SET(v, 15, 8) | ||
| 338 | #define SCALER_SRC_BLEND_COLOR_GET_COLOR2(r) SCALER_GET(r, 7, 0) | ||
| 339 | #define SCALER_SRC_BLEND_COLOR_SET_COLOR2(v) SCALER_SET(v, 7, 0) | ||
| 340 | |||
| 341 | /* SCALER_SRC_BLEND_ALPHA */ | ||
| 342 | #define SCALER_SRC_BLEND_ALPHA_SEL_INV (1 << 31) | ||
| 343 | #define SCALER_SRC_BLEND_ALPHA_GET_SEL(r) SCALER_GET(r, 30, 29) | ||
| 344 | #define SCALER_SRC_BLEND_ALPHA_SET_SEL(v) SCALER_SET(v, 30, 29) | ||
| 345 | #define SCALER_SRC_BLEND_ALPHA_OP_SEL_INV (1 << 28) | ||
| 346 | #define SCALER_SRC_BLEND_ALPHA_GET_OP_SEL(r) SCALER_GET(r, 27, 24) | ||
| 347 | #define SCALER_SRC_BLEND_ALPHA_SET_OP_SEL(v) SCALER_SET(v, 27, 24) | ||
| 348 | #define SCALER_SRC_BLEND_ALPHA_GET_ALPHA(r) SCALER_GET(r, 7, 0) | ||
| 349 | #define SCALER_SRC_BLEND_ALPHA_SET_ALPHA(v) SCALER_SET(v, 7, 0) | ||
| 350 | |||
| 351 | /* SCALER_DST_BLEND_COLOR */ | ||
| 352 | #define SCALER_DST_BLEND_COLOR_SEL_INV (1 << 31) | ||
| 353 | #define SCALER_DST_BLEND_COLOR_GET_SEL(r) SCALER_GET(r, 30, 29) | ||
| 354 | #define SCALER_DST_BLEND_COLOR_SET_SEL(v) SCALER_SET(v, 30, 29) | ||
| 355 | #define SCALER_DST_BLEND_COLOR_OP_SEL_INV (1 << 28) | ||
| 356 | #define SCALER_DST_BLEND_COLOR_GET_OP_SEL(r) SCALER_GET(r, 27, 24) | ||
| 357 | #define SCALER_DST_BLEND_COLOR_SET_OP_SEL(v) SCALER_SET(v, 27, 24) | ||
| 358 | #define SCALER_DST_BLEND_COLOR_GET_COLOR0(r) SCALER_GET(r, 23, 16) | ||
| 359 | #define SCALER_DST_BLEND_COLOR_SET_COLOR0(v) SCALER_SET(v, 23, 16) | ||
| 360 | #define SCALER_DST_BLEND_COLOR_GET_COLOR1(r) SCALER_GET(r, 15, 8) | ||
| 361 | #define SCALER_DST_BLEND_COLOR_SET_COLOR1(v) SCALER_SET(v, 15, 8) | ||
| 362 | #define SCALER_DST_BLEND_COLOR_GET_COLOR2(r) SCALER_GET(r, 7, 0) | ||
| 363 | #define SCALER_DST_BLEND_COLOR_SET_COLOR2(v) SCALER_SET(v, 7, 0) | ||
| 364 | |||
| 365 | /* SCALER_DST_BLEND_ALPHA */ | ||
| 366 | #define SCALER_DST_BLEND_ALPHA_SEL_INV (1 << 31) | ||
| 367 | #define SCALER_DST_BLEND_ALPHA_GET_SEL(r) SCALER_GET(r, 30, 29) | ||
| 368 | #define SCALER_DST_BLEND_ALPHA_SET_SEL(v) SCALER_SET(v, 30, 29) | ||
| 369 | #define SCALER_DST_BLEND_ALPHA_OP_SEL_INV (1 << 28) | ||
| 370 | #define SCALER_DST_BLEND_ALPHA_GET_OP_SEL(r) SCALER_GET(r, 27, 24) | ||
| 371 | #define SCALER_DST_BLEND_ALPHA_SET_OP_SEL(v) SCALER_SET(v, 27, 24) | ||
| 372 | #define SCALER_DST_BLEND_ALPHA_GET_ALPHA(r) SCALER_GET(r, 7, 0) | ||
| 373 | #define SCALER_DST_BLEND_ALPHA_SET_ALPHA(v) SCALER_SET(v, 7, 0) | ||
| 374 | |||
| 375 | /* SCALER_FILL_COLOR */ | ||
| 376 | #define SCALER_FILL_COLOR_GET_ALPHA(r) SCALER_GET(r, 31, 24) | ||
| 377 | #define SCALER_FILL_COLOR_SET_ALPHA(v) SCALER_SET(v, 31, 24) | ||
| 378 | #define SCALER_FILL_COLOR_GET_FILL_COLOR0(r) SCALER_GET(r, 23, 16) | ||
| 379 | #define SCALER_FILL_COLOR_SET_FILL_COLOR0(v) SCALER_SET(v, 23, 16) | ||
| 380 | #define SCALER_FILL_COLOR_GET_FILL_COLOR1(r) SCALER_GET(r, 15, 8) | ||
| 381 | #define SCALER_FILL_COLOR_SET_FILL_COLOR1(v) SCALER_SET(v, 15, 8) | ||
| 382 | #define SCALER_FILL_COLOR_GET_FILL_COLOR2(r) SCALER_GET(r, 7, 0) | ||
| 383 | #define SCALER_FILL_COLOR_SET_FILL_COLOR2(v) SCALER_SET(v, 7, 0) | ||
| 384 | |||
| 385 | /* SCALER_ADDR_Q_CONFIG */ | ||
| 386 | #define SCALER_ADDR_Q_CONFIG_RST (1 << 0) | ||
| 387 | |||
| 388 | /* SCALER_SRC_ADDR_Q_STATUS */ | ||
| 389 | #define SCALER_SRC_ADDR_Q_STATUS_Y_FULL (1 << 23) | ||
| 390 | #define SCALER_SRC_ADDR_Q_STATUS_Y_EMPTY (1 << 22) | ||
| 391 | #define SCALER_SRC_ADDR_Q_STATUS_GET_Y_WR_IDX(r) SCALER_GET(r, 21, 16) | ||
| 392 | #define SCALER_SRC_ADDR_Q_STATUS_CB_FULL (1 << 15) | ||
| 393 | #define SCALER_SRC_ADDR_Q_STATUS_CB_EMPTY (1 << 14) | ||
| 394 | #define SCALER_SRC_ADDR_Q_STATUS_GET_CB_WR_IDX(r) SCALER_GET(r, 13, 8) | ||
| 395 | #define SCALER_SRC_ADDR_Q_STATUS_CR_FULL (1 << 7) | ||
| 396 | #define SCALER_SRC_ADDR_Q_STATUS_CR_EMPTY (1 << 6) | ||
| 397 | #define SCALER_SRC_ADDR_Q_STATUS_GET_CR_WR_IDX(r) SCALER_GET(r, 5, 0) | ||
| 398 | |||
| 399 | /* SCALER_DST_ADDR_Q_STATUS */ | ||
| 400 | #define SCALER_DST_ADDR_Q_STATUS_Y_FULL (1 << 23) | ||
| 401 | #define SCALER_DST_ADDR_Q_STATUS_Y_EMPTY (1 << 22) | ||
| 402 | #define SCALER_DST_ADDR_Q_STATUS_GET_Y_WR_IDX(r) SCALER_GET(r, 21, 16) | ||
| 403 | #define SCALER_DST_ADDR_Q_STATUS_CB_FULL (1 << 15) | ||
| 404 | #define SCALER_DST_ADDR_Q_STATUS_CB_EMPTY (1 << 14) | ||
| 405 | #define SCALER_DST_ADDR_Q_STATUS_GET_CB_WR_IDX(r) SCALER_GET(r, 13, 8) | ||
| 406 | #define SCALER_DST_ADDR_Q_STATUS_CR_FULL (1 << 7) | ||
| 407 | #define SCALER_DST_ADDR_Q_STATUS_CR_EMPTY (1 << 6) | ||
| 408 | #define SCALER_DST_ADDR_Q_STATUS_GET_CR_WR_IDX(r) SCALER_GET(r, 5, 0) | ||
| 409 | |||
| 410 | /* SCALER_CRC_COLOR00_10 */ | ||
| 411 | #define SCALER_CRC_COLOR00_10_GET_00(r) SCALER_GET(r, 31, 16) | ||
| 412 | #define SCALER_CRC_COLOR00_10_GET_10(r) SCALER_GET(r, 15, 0) | ||
| 413 | |||
| 414 | /* SCALER_CRC_COLOR20_30 */ | ||
| 415 | #define SCALER_CRC_COLOR20_30_GET_20(r) SCALER_GET(r, 31, 16) | ||
| 416 | #define SCALER_CRC_COLOR20_30_GET_30(r) SCALER_GET(r, 15, 0) | ||
| 417 | |||
| 418 | /* SCALER_CRC_COLOR01_11 */ | ||
| 419 | #define SCALER_CRC_COLOR01_11_GET_01(r) SCALER_GET(r, 31, 16) | ||
| 420 | #define SCALER_CRC_COLOR01_11_GET_11(r) SCALER_GET(r, 15, 0) | ||
| 421 | |||
| 422 | /* SCALER_CRC_COLOR21_31 */ | ||
| 423 | #define SCALER_CRC_COLOR21_31_GET_21(r) SCALER_GET(r, 31, 16) | ||
| 424 | #define SCALER_CRC_COLOR21_31_GET_31(r) SCALER_GET(r, 15, 0) | ||
| 425 | |||
| 426 | #endif /* EXYNOS_REGS_SCALER_H */ | ||
| @@ -35,6 +35,7 @@ · 34 unmodified lines | |||
| 35 | */ | 35 | */ |
| 36 | 36 | ||
| 37 | #define I915_CSR_GLK "i915/glk_dmc_ver1_04.bin" | 37 | #define I915_CSR_GLK "i915/glk_dmc_ver1_04.bin" |
| 38 | MODULE_FIRMWARE(I915_CSR_GLK); | ||
| 38 | #define GLK_CSR_VERSION_REQUIRED CSR_VERSION(1, 4) | 39 | #define GLK_CSR_VERSION_REQUIRED CSR_VERSION(1, 4) |
| 39 | 40 | ||
| 40 | #define I915_CSR_CNL "i915/cnl_dmc_ver1_07.bin" | 41 | #define I915_CSR_CNL "i915/cnl_dmc_ver1_07.bin" |
| @@ -741,6 +741,7 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) · 740 unmodified lines | |||
| 741 | struct vc4_async_flip_state { | 741 | struct vc4_async_flip_state { |
| 742 | struct drm_crtc *crtc; | 742 | struct drm_crtc *crtc; |
| 743 | struct drm_framebuffer *fb; | 743 | struct drm_framebuffer *fb; |
| 744 | struct drm_framebuffer *old_fb; | ||
| 744 | struct drm_pending_vblank_event *event; | 745 | struct drm_pending_vblank_event *event; |
| 745 | 746 | ||
| 746 | struct vc4_seqno_cb cb; | 747 | struct vc4_seqno_cb cb; |
| @@ -770,6 +771,23 @@ vc4_async_page_flip_complete(struct vc4_seqno_cb *cb) · 23 unmodified lines | |||
| 770 | 771 | ||
| 771 | drm_crtc_vblank_put(crtc); | 772 | drm_crtc_vblank_put(crtc); |
| 772 | drm_framebuffer_put(flip_state->fb); | 773 | drm_framebuffer_put(flip_state->fb); |
| 774 | |||
| 775 | /* Decrement the BO usecnt in order to keep the inc/dec calls balanced | ||
| 776 | * when the planes are updated through the async update path. | ||
| 777 | * FIXME: we should move to generic async-page-flip when it's | ||
| 778 | * available, so that we can get rid of this hand-made cleanup_fb() | ||
| 779 | * logic. | ||
| 780 | */ | ||
| 781 | if (flip_state->old_fb) { | ||
| 782 | struct drm_gem_cma_object *cma_bo; | ||
| 783 | struct vc4_bo *bo; | ||
| 784 | |||
| 785 | cma_bo = drm_fb_cma_get_gem_obj(flip_state->old_fb, 0); | ||
| 786 | bo = to_vc4_bo(&cma_bo->base); | ||
| 787 | vc4_bo_dec_usecnt(bo); | ||
| 788 | drm_framebuffer_put(flip_state->old_fb); | ||
| 789 | } | ||
| 790 | |||
| 773 | kfree(flip_state); | 791 | kfree(flip_state); |
| 774 | 792 | ||
| 775 | up(&vc4->async_modeset); | 793 | up(&vc4->async_modeset); |
| @@ -794,9 +812,22 @@ static int vc4_async_page_flip(struct drm_crtc *crtc, · 18 unmodified lines | |||
| 794 | struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0); | 812 | struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0); |
| 795 | struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); | 813 | struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); |
| 796 | 814 | ||
| 815 | /* Increment the BO usecnt here, so that we never end up with an | ||
| 816 | * unbalanced number of vc4_bo_{dec,inc}_usecnt() calls when the | ||
| 817 | * plane is later updated through the non-async path. | ||
| 818 | * FIXME: we should move to generic async-page-flip when it's | ||
| 819 | * available, so that we can get rid of this hand-made prepare_fb() | ||
| 820 | * logic. | ||
| 821 | */ | ||
| 822 | ret = vc4_bo_inc_usecnt(bo); | ||
| 823 | if (ret) | ||
| 824 | return ret; | ||
| 825 | |||
| 797 | flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL); | 826 | flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL); |
| 798 | if (!flip_state) | 827 | if (!flip_state) { |
| 828 | vc4_bo_dec_usecnt(bo); | ||
| 799 | return -ENOMEM; | 829 | return -ENOMEM; |
| 830 | } | ||
| 800 | 831 | ||
| 801 | drm_framebuffer_get(fb); | 832 | drm_framebuffer_get(fb); |
| 802 | flip_state->fb = fb; | 833 | flip_state->fb = fb; |
| @@ -807,10 +838,23 @@ static int vc4_async_page_flip(struct drm_crtc *crtc, · 4 unmodified lines | |||
| 807 | ret = down_interruptible(&vc4->async_modeset); | 838 | ret = down_interruptible(&vc4->async_modeset); |
| 808 | if (ret) { | 839 | if (ret) { |
| 809 | drm_framebuffer_put(fb); | 840 | drm_framebuffer_put(fb); |
| 841 | vc4_bo_dec_usecnt(bo); | ||
| 810 | kfree(flip_state); | 842 | kfree(flip_state); |
| 811 | return ret; | 843 | return ret; |
| 812 | } | 844 | } |
| 813 | 845 | ||
| 846 | /* Save the current FB before it's replaced by the new one in | ||
| 847 | * drm_atomic_set_fb_for_plane(). We'll need the old FB in | ||
| 848 | * vc4_async_page_flip_complete() to decrement the BO usecnt and keep | ||
| 849 | * it consistent. | ||
| 850 | * FIXME: we should move to generic async-page-flip when it's | ||
| 851 | * available, so that we can get rid of this hand-made cleanup_fb() | ||
| 852 | * logic. | ||
| 853 | */ | ||
| 854 | flip_state->old_fb = plane->state->fb; | ||
| 855 | if (flip_state->old_fb) | ||
| 856 | drm_framebuffer_get(flip_state->old_fb); | ||
| 857 | |||
| 814 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | 858 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); |
| 815 | 859 | ||
| 816 | /* Immediately update the plane's legacy fb pointer, so that later | 860 | /* Immediately update the plane's legacy fb pointer, so that later |
| @@ -441,11 +441,11 @@ static int vmwgfx_set_config_internal(struct drm_mode_set *set) · 440 unmodified lines | |||
| 441 | struct drm_crtc *crtc = set->crtc; | 441 | struct drm_crtc *crtc = set->crtc; |
| 442 | struct drm_framebuffer *fb; | 442 | struct drm_framebuffer *fb; |
| 443 | struct drm_crtc *tmp; | 443 | struct drm_crtc *tmp; |
| 444 | struct drm_modeset_acquire_ctx *ctx; | ||
| 445 | struct drm_device *dev = set->crtc->dev; | 444 | struct drm_device *dev = set->crtc->dev; |
| 445 | struct drm_modeset_acquire_ctx ctx; | ||
| 446 | int ret; | 446 | int ret; |
| 447 | 447 | ||
| 448 | ctx = dev->mode_config.acquire_ctx; | 448 | drm_modeset_acquire_init(&ctx, 0); |
| 449 | 449 | ||
| 450 | restart: | 450 | restart: |
| 451 | /* | 451 | /* |
| @@ -458,7 +458,7 @@ restart: · 6 unmodified lines | |||
| 458 | 458 | ||
| 459 | fb = set->fb; | 459 | fb = set->fb; |
| 460 | 460 | ||
| 461 | ret = crtc->funcs->set_config(set, ctx); | 461 | ret = crtc->funcs->set_config(set, &ctx); |
| 462 | if (ret == 0) { | 462 | if (ret == 0) { |
| 463 | crtc->primary->crtc = crtc; | 463 | crtc->primary->crtc = crtc; |
| 464 | crtc->primary->fb = fb; | 464 | crtc->primary->fb = fb; |
| @@ -473,20 +473,13 @@ restart: · 8 unmodified lines | |||
| 473 | } | 473 | } |
| 474 | 474 | ||
| 475 | if (ret == -EDEADLK) { | 475 | if (ret == -EDEADLK) { |
| 476 | dev->mode_config.acquire_ctx = NULL; | 476 | drm_modeset_backoff(&ctx); |
| 477 | |||
| 478 | retry_locking: | ||
| 479 | drm_modeset_backoff(ctx); | ||
| 480 | |||
| 481 | ret = drm_modeset_lock_all_ctx(dev, ctx); | ||
| 482 | if (ret) | ||
| 483 | goto retry_locking; | ||
| 484 | |||
| 485 | dev->mode_config.acquire_ctx = ctx; | ||
| 486 | |||
| 487 | goto restart; | 477 | goto restart; |
| 488 | } | 478 | } |
| 489 | 479 | ||
| 480 | drm_modeset_drop_locks(&ctx); | ||
| 481 | drm_modeset_acquire_fini(&ctx); | ||
| 482 | |||
| 490 | return ret; | 483 | return ret; |
| 491 | } | 484 | } |
| 492 | 485 | ||
| @@ -624,7 +617,6 @@ static int vmw_fb_set_par(struct fb_info *info) · 131 unmodified lines | |||
| 624 | } | 617 | } |
| 625 | 618 | ||
| 626 | mutex_lock(&par->bo_mutex); | 619 | mutex_lock(&par->bo_mutex); |
| 627 | drm_modeset_lock_all(vmw_priv->dev); | ||
| 628 | ret = vmw_fb_kms_framebuffer(info); | 620 | ret = vmw_fb_kms_framebuffer(info); |
| 629 | if (ret) | 621 | if (ret) |
| 630 | goto out_unlock; | 622 | goto out_unlock; |
| @@ -657,7 +649,6 @@ out_unlock: · 26 unmodified lines | |||
| 657 | drm_mode_destroy(vmw_priv->dev, old_mode); | 649 | drm_mode_destroy(vmw_priv->dev, old_mode); |
| 658 | par->set_mode = mode; | 650 | par->set_mode = mode; |
| 659 | 651 | ||
| 660 | drm_modeset_unlock_all(vmw_priv->dev); | ||
| 661 | mutex_unlock(&par->bo_mutex); | 652 | mutex_unlock(&par->bo_mutex); |
| 662 | 653 | ||
| 663 | return ret; | 654 | return ret; |
| @@ -713,18 +704,14 @@ int vmw_fb_init(struct vmw_private *vmw_priv) · 49 unmodified lines | |||
| 713 | par->max_width = fb_width; | 704 | par->max_width = fb_width; |
| 714 | par->max_height = fb_height; | 705 | par->max_height = fb_height; |
| 715 | 706 | ||
| 716 | drm_modeset_lock_all(vmw_priv->dev); | ||
| 717 | ret = vmw_kms_fbdev_init_data(vmw_priv, 0, par->max_width, | 707 | ret = vmw_kms_fbdev_init_data(vmw_priv, 0, par->max_width, |
| 718 | par->max_height, &par->con, | 708 | par->max_height, &par->con, |
| 719 | &par->crtc, &init_mode); | 709 | &par->crtc, &init_mode); |
| 720 | if (ret) { | 710 | if (ret) |
| 721 | drm_modeset_unlock_all(vmw_priv->dev); | ||
| 722 | goto err_kms; | 711 | goto err_kms; |
| 723 | } | ||
| 724 | 712 | ||
| 725 | info->var.xres = init_mode->hdisplay; | 713 | info->var.xres = init_mode->hdisplay; |
| 726 | info->var.yres = init_mode->vdisplay; | 714 | info->var.yres = init_mode->vdisplay; |
| 727 | drm_modeset_unlock_all(vmw_priv->dev); | ||
| 728 | 715 | ||
| 729 | /* | 716 | /* |
| 730 | * Create buffers and alloc memory | 717 | * Create buffers and alloc memory |
| @@ -832,7 +819,9 @@ int vmw_fb_close(struct vmw_private *vmw_priv) · 101 unmodified lines | |||
| 832 | cancel_delayed_work_sync(&par->local_work); | 819 | cancel_delayed_work_sync(&par->local_work); |
| 833 | unregister_framebuffer(info); | 820 | unregister_framebuffer(info); |
| 834 | 821 | ||
| 822 | mutex_lock(&par->bo_mutex); | ||
| 835 | (void) vmw_fb_kms_detach(par, true, true); | 823 | (void) vmw_fb_kms_detach(par, true, true); |
| 824 | mutex_unlock(&par->bo_mutex); | ||
| 836 | 825 | ||
| 837 | vfree(par->vmalloc); | 826 | vfree(par->vmalloc); |
| 838 | framebuffer_release(info); | 827 | framebuffer_release(info); |
| @@ -2595,6 +2595,7 @@ void vmw_kms_helper_resource_finish(struct vmw_validation_ctx *ctx, · 2594 unmodified lines | |||
| 2595 | vmw_kms_helper_buffer_finish(res->dev_priv, NULL, ctx->buf, | 2595 | vmw_kms_helper_buffer_finish(res->dev_priv, NULL, ctx->buf, |
| 2596 | out_fence, NULL); | 2596 | out_fence, NULL); |
| 2597 | 2597 | ||
| 2598 | vmw_dmabuf_unreference(&ctx->buf); | ||
| 2598 | vmw_resource_unreserve(res, false, NULL, 0); | 2599 | vmw_resource_unreserve(res, false, NULL, 0); |
| 2599 | mutex_unlock(&res->dev_priv->cmdbuf_mutex); | 2600 | mutex_unlock(&res->dev_priv->cmdbuf_mutex); |
| 2600 | } | 2601 | } |
| @@ -2680,7 +2681,9 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, · 79 unmodified lines | |||
| 2680 | struct vmw_display_unit *du; | 2681 | struct vmw_display_unit *du; |
| 2681 | struct drm_display_mode *mode; | 2682 | struct drm_display_mode *mode; |
| 2682 | int i = 0; | 2683 | int i = 0; |
| 2684 | int ret = 0; | ||
| 2683 | 2685 | ||
| 2686 | mutex_lock(&dev_priv->dev->mode_config.mutex); | ||
| 2684 | list_for_each_entry(con, &dev_priv->dev->mode_config.connector_list, | 2687 | list_for_each_entry(con, &dev_priv->dev->mode_config.connector_list, |
| 2685 | head) { | 2688 | head) { |
| 2686 | if (i == unit) | 2689 | if (i == unit) |
| @@ -2691,7 +2694,8 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, · 4 unmodified lines | |||
| 2691 | 2694 | ||
| 2692 | if (i != unit) { | 2695 | if (i != unit) { |
| 2693 | DRM_ERROR("Could not find initial display unit.\n"); | 2696 | DRM_ERROR("Could not find initial display unit.\n"); |
| 2694 | return -EINVAL; | 2697 | ret = -EINVAL; |
| 2698 | goto out_unlock; | ||
| 2695 | } | 2699 | } |
| 2696 | 2700 | ||
| 2697 | if (list_empty(&con->modes)) | 2701 | if (list_empty(&con->modes)) |
| @@ -2699,7 +2703,8 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, · 1 unmodified line | |||
| 2699 | 2703 | ||
| 2700 | if (list_empty(&con->modes)) { | 2704 | if (list_empty(&con->modes)) { |
| 2701 | DRM_ERROR("Could not find initial display mode.\n"); | 2705 | DRM_ERROR("Could not find initial display mode.\n"); |
| 2702 | return -EINVAL; | 2706 | ret = -EINVAL; |
| 2707 | goto out_unlock; | ||
| 2703 | } | 2708 | } |
| 2704 | 2709 | ||
| 2705 | du = vmw_connector_to_du(con); | 2710 | du = vmw_connector_to_du(con); |
| @@ -2720,7 +2725,10 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, · 14 unmodified lines | |||
| 2720 | head); | 2725 | head); |
| 2721 | } | 2726 | } |
| 2722 | 2727 | ||
| 2723 | return 0; | 2728 | out_unlock: |
| 2729 | mutex_unlock(&dev_priv->dev->mode_config.mutex); | ||
| 2730 | |||
| 2731 | return ret; | ||
| 2724 | } | 2732 | } |
| 2725 | 2733 | ||
| 2726 | /** | 2734 | /** |
| @@ -135,6 +135,219 @@ struct drm_exynos_g2d_exec { · 134 unmodified lines | |||
| 135 | __u64 async; | 135 | __u64 async; |
| 136 | }; | 136 | }; |
| 137 | 137 | ||
| 138 | /* Exynos DRM IPP v2 API */ | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Enumerate available IPP hardware modules. | ||
| 142 | * | ||
| 143 | * @count_ipps: size of ipp_id array / number of ipp modules (set by driver) | ||
| 144 | * @reserved: padding | ||
| 145 | * @ipp_id_ptr: pointer to ipp_id array or NULL | ||
| 146 | */ | ||
| 147 | struct drm_exynos_ioctl_ipp_get_res { | ||
| 148 | __u32 count_ipps; | ||
| 149 | __u32 reserved; | ||
| 150 | __u64 ipp_id_ptr; | ||
| 151 | }; | ||
| 152 | |||
| 153 | enum drm_exynos_ipp_format_type { | ||
| 154 | DRM_EXYNOS_IPP_FORMAT_SOURCE = 0x01, | ||
| 155 | DRM_EXYNOS_IPP_FORMAT_DESTINATION = 0x02, | ||
| 156 | }; | ||
| 157 | |||
| 158 | struct drm_exynos_ipp_format { | ||
| 159 | __u32 fourcc; | ||
| 160 | __u32 type; | ||
| 161 | __u64 modifier; | ||
| 162 | }; | ||
| 163 | |||
| 164 | enum drm_exynos_ipp_capability { | ||
| 165 | DRM_EXYNOS_IPP_CAP_CROP = 0x01, | ||
| 166 | DRM_EXYNOS_IPP_CAP_ROTATE = 0x02, | ||
| 167 | DRM_EXYNOS_IPP_CAP_SCALE = 0x04, | ||
| 168 | DRM_EXYNOS_IPP_CAP_CONVERT = 0x08, | ||
| 169 | }; | ||
| 170 | |||
| 171 | /** | ||
| 172 | * Get IPP hardware capabilities and supported image formats. | ||
| 173 | * | ||
| 174 | * @ipp_id: id of IPP module to query | ||
| 175 | * @capabilities: bitmask of drm_exynos_ipp_capability (set by driver) | ||
| 176 | * @reserved: padding | ||
| 177 | * @formats_count: size of formats array (in entries) / number of filled | ||
| 178 | * formats (set by driver) | ||
| 179 | * @formats_ptr: pointer to formats array or NULL | ||
| 180 | */ | ||
| 181 | struct drm_exynos_ioctl_ipp_get_caps { | ||
| 182 | __u32 ipp_id; | ||
| 183 | __u32 capabilities; | ||
| 184 | __u32 reserved; | ||
| 185 | __u32 formats_count; | ||
| 186 | __u64 formats_ptr; | ||
| 187 | }; | ||
| 188 | |||
| 189 | enum drm_exynos_ipp_limit_type { | ||
| 190 | /* size (horizontal/vertial) limits, in pixels (min, max, alignment) */ | ||
| 191 | DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE = 0x0001, | ||
| 192 | /* scale ratio (horizonta/vertial), 16.16 fixed point (min, max) */ | ||
| 193 | DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE = 0x0002, | ||
| 194 | |||
| 195 | /* image buffer area */ | ||
| 196 | DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER = 0x0001 << 16, | ||
| 197 | /* src/dst rectangle area */ | ||
| 198 | DRM_EXYNOS_IPP_LIMIT_SIZE_AREA = 0x0002 << 16, | ||
| 199 | /* src/dst rectangle area when rotation enabled */ | ||
| 200 | DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED = 0x0003 << 16, | ||
| 201 | |||
| 202 | DRM_EXYNOS_IPP_LIMIT_TYPE_MASK = 0x000f, | ||
| 203 | DRM_EXYNOS_IPP_LIMIT_SIZE_MASK = 0x000f << 16, | ||
| 204 | }; | ||
| 205 | |||
| 206 | struct drm_exynos_ipp_limit_val { | ||
| 207 | __u32 min; | ||
| 208 | __u32 max; | ||
| 209 | __u32 align; | ||
| 210 | __u32 reserved; | ||
| 211 | }; | ||
| 212 | |||
| 213 | /** | ||
| 214 | * IPP module limitation. | ||
| 215 | * | ||
| 216 | * @type: limit type (see drm_exynos_ipp_limit_type enum) | ||
| 217 | * @reserved: padding | ||
| 218 | * @h: horizontal limits | ||
| 219 | * @v: vertical limits | ||
| 220 | */ | ||
| 221 | struct drm_exynos_ipp_limit { | ||
| 222 | __u32 type; | ||
| 223 | __u32 reserved; | ||
| 224 | struct drm_exynos_ipp_limit_val h; | ||
| 225 | struct drm_exynos_ipp_limit_val v; | ||
| 226 | }; | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Get IPP limits for given image format. | ||
| 230 | * | ||
| 231 | * @ipp_id: id of IPP module to query | ||
| 232 | * @fourcc: image format code (see DRM_FORMAT_* in drm_fourcc.h) | ||
| 233 | * @modifier: image format modifier (see DRM_FORMAT_MOD_* in drm_fourcc.h) | ||
| 234 | * @type: source/destination identifier (drm_exynos_ipp_format_flag enum) | ||
| 235 | * @limits_count: size of limits array (in entries) / number of filled entries | ||
| 236 | * (set by driver) | ||
| 237 | * @limits_ptr: pointer to limits array or NULL | ||
| 238 | */ | ||
| 239 | struct drm_exynos_ioctl_ipp_get_limits { | ||
| 240 | __u32 ipp_id; | ||
| 241 | __u32 fourcc; | ||
| 242 | __u64 modifier; | ||
| 243 | __u32 type; | ||
| 244 | __u32 limits_count; | ||
| 245 | __u64 limits_ptr; | ||
| 246 | }; | ||
| 247 | |||
| 248 | enum drm_exynos_ipp_task_id { | ||
| 249 | /* buffer described by struct drm_exynos_ipp_task_buffer */ | ||
| 250 | DRM_EXYNOS_IPP_TASK_BUFFER = 0x0001, | ||
| 251 | /* rectangle described by struct drm_exynos_ipp_task_rect */ | ||
| 252 | DRM_EXYNOS_IPP_TASK_RECTANGLE = 0x0002, | ||
| 253 | /* transformation described by struct drm_exynos_ipp_task_transform */ | ||
| 254 | DRM_EXYNOS_IPP_TASK_TRANSFORM = 0x0003, | ||
| 255 | /* alpha configuration described by struct drm_exynos_ipp_task_alpha */ | ||
| 256 | DRM_EXYNOS_IPP_TASK_ALPHA = 0x0004, | ||
| 257 | |||
| 258 | /* source image data (for buffer and rectangle chunks) */ | ||
| 259 | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE = 0x0001 << 16, | ||
| 260 | /* destination image data (for buffer and rectangle chunks) */ | ||
| 261 | DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION = 0x0002 << 16, | ||
| 262 | }; | ||
| 263 | |||
| 264 | /** | ||
| 265 | * Memory buffer with image data. | ||
| 266 | * | ||
| 267 | * @id: must be DRM_EXYNOS_IPP_TASK_BUFFER | ||
| 268 | * other parameters are same as for AddFB2 generic DRM ioctl | ||
| 269 | */ | ||
| 270 | struct drm_exynos_ipp_task_buffer { | ||
| 271 | __u32 id; | ||
| 272 | __u32 fourcc; | ||
| 273 | __u32 width, height; | ||
| 274 | __u32 gem_id[4]; | ||
| 275 | __u32 offset[4]; | ||
| 276 | __u32 pitch[4]; | ||
| 277 | __u64 modifier; | ||
| 278 | }; | ||
| 279 | |||
| 280 | /** | ||
| 281 | * Rectangle for processing. | ||
| 282 | * | ||
| 283 | * @id: must be DRM_EXYNOS_IPP_TASK_RECTANGLE | ||
| 284 | * @reserved: padding | ||
| 285 | * @x,@y: left corner in pixels | ||
| 286 | * @w,@h: width/height in pixels | ||
| 287 | */ | ||
| 288 | struct drm_exynos_ipp_task_rect { | ||
| 289 | __u32 id; | ||
| 290 | __u32 reserved; | ||
| 291 | __u32 x; | ||
| 292 | __u32 y; | ||
| 293 | __u32 w; | ||
| 294 | __u32 h; | ||
| 295 | }; | ||
| 296 | |||
| 297 | /** | ||
| 298 | * Image tranformation description. | ||
| 299 | * | ||
| 300 | * @id: must be DRM_EXYNOS_IPP_TASK_TRANSFORM | ||
| 301 | * @rotation: DRM_MODE_ROTATE_* and DRM_MODE_REFLECT_* values | ||
| 302 | */ | ||
| 303 | struct drm_exynos_ipp_task_transform { | ||
| 304 | __u32 id; | ||
| 305 | __u32 rotation; | ||
| 306 | }; | ||
| 307 | |||
| 308 | /** | ||
| 309 | * Image global alpha configuration for formats without alpha values. | ||
| 310 | * | ||
| 311 | * @id: must be DRM_EXYNOS_IPP_TASK_ALPHA | ||
| 312 | * @value: global alpha value (0-255) | ||
| 313 | */ | ||
| 314 | struct drm_exynos_ipp_task_alpha { | ||
| 315 | __u32 id; | ||
| 316 | __u32 value; | ||
| 317 | }; | ||
| 318 | |||
| 319 | enum drm_exynos_ipp_flag { | ||
| 320 | /* generate DRM event after processing */ | ||
| 321 | DRM_EXYNOS_IPP_FLAG_EVENT = 0x01, | ||
| 322 | /* dry run, only check task parameters */ | ||
| 323 | DRM_EXYNOS_IPP_FLAG_TEST_ONLY = 0x02, | ||
| 324 | /* non-blocking processing */ | ||
| 325 | DRM_EXYNOS_IPP_FLAG_NONBLOCK = 0x04, | ||
| 326 | }; | ||
| 327 | |||
| 328 | #define DRM_EXYNOS_IPP_FLAGS (DRM_EXYNOS_IPP_FLAG_EVENT |\ | ||
| 329 | DRM_EXYNOS_IPP_FLAG_TEST_ONLY | DRM_EXYNOS_IPP_FLAG_NONBLOCK) | ||
| 330 | |||
| 331 | /** | ||
| 332 | * Perform image processing described by array of drm_exynos_ipp_task_* | ||
| 333 | * structures (parameters array). | ||
| 334 | * | ||
| 335 | * @ipp_id: id of IPP module to run the task | ||
| 336 | * @flags: bitmask of drm_exynos_ipp_flag values | ||
| 337 | * @reserved: padding | ||
| 338 | * @params_size: size of parameters array (in bytes) | ||
| 339 | * @params_ptr: pointer to parameters array or NULL | ||
| 340 | * @user_data: (optional) data for drm event | ||
| 341 | */ | ||
| 342 | struct drm_exynos_ioctl_ipp_commit { | ||
| 343 | __u32 ipp_id; | ||
| 344 | __u32 flags; | ||
| 345 | __u32 reserved; | ||
| 346 | __u32 params_size; | ||
| 347 | __u64 params_ptr; | ||
| 348 | __u64 user_data; | ||
| 349 | }; | ||
| 350 | |||
| 138 | #define DRM_EXYNOS_GEM_CREATE 0x00 | 351 | #define DRM_EXYNOS_GEM_CREATE 0x00 |
| 139 | #define DRM_EXYNOS_GEM_MAP 0x01 | 352 | #define DRM_EXYNOS_GEM_MAP 0x01 |
| 140 | /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ | 353 | /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ |
| @@ -147,6 +360,11 @@ struct drm_exynos_g2d_exec { · 6 unmodified lines | |||
| 147 | #define DRM_EXYNOS_G2D_EXEC 0x22 | 360 | #define DRM_EXYNOS_G2D_EXEC 0x22 |
| 148 | 361 | ||
| 149 | /* Reserved 0x30 ~ 0x33 for obsolete Exynos IPP ioctls */ | 362 | /* Reserved 0x30 ~ 0x33 for obsolete Exynos IPP ioctls */ |
| 363 | /* IPP - Image Post Processing */ | ||
| 364 | #define DRM_EXYNOS_IPP_GET_RESOURCES 0x40 | ||
| 365 | #define DRM_EXYNOS_IPP_GET_CAPS 0x41 | ||
| 366 | #define DRM_EXYNOS_IPP_GET_LIMITS 0x42 | ||
| 367 | #define DRM_EXYNOS_IPP_COMMIT 0x43 | ||
| 150 | 368 | ||
| 151 | #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ | 369 | #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ |
| 152 | DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) | 370 | DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) |
| @@ -165,8 +383,20 @@ struct drm_exynos_g2d_exec { · 12 unmodified lines | |||
| 165 | #define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ | 383 | #define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ |
| 166 | DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) | 384 | DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) |
| 167 | 385 | ||
| 386 | #define DRM_IOCTL_EXYNOS_IPP_GET_RESOURCES DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 387 | DRM_EXYNOS_IPP_GET_RESOURCES, \ | ||
| 388 | struct drm_exynos_ioctl_ipp_get_res) | ||
| 389 | #define DRM_IOCTL_EXYNOS_IPP_GET_CAPS DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 390 | DRM_EXYNOS_IPP_GET_CAPS, struct drm_exynos_ioctl_ipp_get_caps) | ||
| 391 | #define DRM_IOCTL_EXYNOS_IPP_GET_LIMITS DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 392 | DRM_EXYNOS_IPP_GET_LIMITS, \ | ||
| 393 | struct drm_exynos_ioctl_ipp_get_limits) | ||
| 394 | #define DRM_IOCTL_EXYNOS_IPP_COMMIT DRM_IOWR(DRM_COMMAND_BASE + \ | ||
| 395 | DRM_EXYNOS_IPP_COMMIT, struct drm_exynos_ioctl_ipp_commit) | ||
| 396 | |||
| 168 | /* EXYNOS specific events */ | 397 | /* EXYNOS specific events */ |
| 169 | #define DRM_EXYNOS_G2D_EVENT 0x80000000 | 398 | #define DRM_EXYNOS_G2D_EVENT 0x80000000 |
| 399 | #define DRM_EXYNOS_IPP_EVENT 0x80000002 | ||
| 170 | 400 | ||
| 171 | struct drm_exynos_g2d_event { | 401 | struct drm_exynos_g2d_event { |
| 172 | struct drm_event base; | 402 | struct drm_event base; |
| @@ -177,6 +407,16 @@ struct drm_exynos_g2d_event { · 4 unmodified lines | |||
| 177 | __u32 reserved; | 407 | __u32 reserved; |
| 178 | }; | 408 | }; |
| 179 | 409 | ||
| 410 | struct drm_exynos_ipp_event { | ||
| 411 | struct drm_event base; | ||
| 412 | __u64 user_data; | ||
| 413 | __u32 tv_sec; | ||
| 414 | __u32 tv_usec; | ||
| 415 | __u32 ipp_id; | ||
| 416 | __u32 sequence; | ||
| 417 | __u64 reserved; | ||
| 418 | }; | ||
| 419 | |||
| 180 | #if defined(__cplusplus) | 420 | #if defined(__cplusplus) |
| 181 | } | 421 | } |
| 182 | #endif | 422 | #endif |