drm: renesas: shmobile: Atomic conversion + DT support Currently, there are two drivers for the LCD controller on Renesas SuperH-based and ARM-based SH-Mobile and R-Mobile SoCs: 1. sh_mobile_lcdcfb, using the fbdev framework, 2. shmob_drm, using the DRM framework. However, only the former driver is used, as all platform support integrates the former. None of these drivers support DT-based systems. Convert the SH-Mobile DRM driver to atomic modesetting, and add DT support, complemented by the customary set of fixes and improvements. Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Link: https://lore.kernel.org/r/cover.1694767208.git.geert+renesas@glider.be/ Signed-off-by: Dave Airlie <airlied@redhat.com>
Unified · Split
| @@ -0,0 +1,130 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/display/renesas,shmobile-lcdc.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Renesas SH-Mobile LCD Controller (LCDC) | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | ||
| 11 | - Geert Uytterhoeven <geert+renesas@glider.be> | ||
| 12 | |||
| 13 | properties: | ||
| 14 | compatible: | ||
| 15 | enum: | ||
| 16 | - renesas,r8a7740-lcdc # R-Mobile A1 | ||
| 17 | - renesas,sh73a0-lcdc # SH-Mobile AG5 | ||
| 18 | |||
| 19 | reg: | ||
| 20 | maxItems: 1 | ||
| 21 | |||
| 22 | interrupts: | ||
| 23 | maxItems: 1 | ||
| 24 | |||
| 25 | clocks: | ||
| 26 | minItems: 1 | ||
| 27 | maxItems: 5 | ||
| 28 | description: | ||
| 29 | Only the functional clock is mandatory. | ||
| 30 | Some of the optional clocks are model-dependent (e.g. "video" (a.k.a. | ||
| 31 | "vou" or "dv_clk") is available on R-Mobile A1 only). | ||
| 32 | |||
| 33 | clock-names: | ||
| 34 | minItems: 1 | ||
| 35 | items: | ||
| 36 | - const: fck | ||
| 37 | - enum: [ media, lclk, hdmi, video ] | ||
| 38 | - enum: [ media, lclk, hdmi, video ] | ||
| 39 | - enum: [ media, lclk, hdmi, video ] | ||
| 40 | - enum: [ media, lclk, hdmi, video ] | ||
| 41 | |||
| 42 | power-domains: | ||
| 43 | maxItems: 1 | ||
| 44 | |||
| 45 | ports: | ||
| 46 | $ref: /schemas/graph.yaml#/properties/ports | ||
| 47 | |||
| 48 | properties: | ||
| 49 | port@0: | ||
| 50 | $ref: /schemas/graph.yaml#/properties/port | ||
| 51 | description: LCD port (R-Mobile A1 and SH-Mobile AG5) | ||
| 52 | unevaluatedProperties: false | ||
| 53 | |||
| 54 | port@1: | ||
| 55 | $ref: /schemas/graph.yaml#/properties/port | ||
| 56 | description: HDMI port (R-Mobile A1 LCDC1 and SH-Mobile AG5) | ||
| 57 | unevaluatedProperties: false | ||
| 58 | |||
| 59 | port@2: | ||
| 60 | $ref: /schemas/graph.yaml#/properties/port | ||
| 61 | description: MIPI-DSI port (SH-Mobile AG5) | ||
| 62 | unevaluatedProperties: false | ||
| 63 | |||
| 64 | required: | ||
| 65 | - port@0 | ||
| 66 | |||
| 67 | unevaluatedProperties: false | ||
| 68 | |||
| 69 | required: | ||
| 70 | - compatible | ||
| 71 | - reg | ||
| 72 | - interrupts | ||
| 73 | - clocks | ||
| 74 | - clock-names | ||
| 75 | - power-domains | ||
| 76 | - ports | ||
| 77 | |||
| 78 | additionalProperties: false | ||
| 79 | |||
| 80 | allOf: | ||
| 81 | - if: | ||
| 82 | properties: | ||
| 83 | compatible: | ||
| 84 | contains: | ||
| 85 | const: renesas,r8a7740-lcdc | ||
| 86 | then: | ||
| 87 | properties: | ||
| 88 | ports: | ||
| 89 | properties: | ||
| 90 | port@2: false | ||
| 91 | |||
| 92 | - if: | ||
| 93 | properties: | ||
| 94 | compatible: | ||
| 95 | contains: | ||
| 96 | const: renesas,sh73a0-lcdc | ||
| 97 | then: | ||
| 98 | properties: | ||
| 99 | ports: | ||
| 100 | required: | ||
| 101 | - port@1 | ||
| 102 | - port@2 | ||
| 103 | |||
| 104 | examples: | ||
| 105 | - | | ||
| 106 | #include <dt-bindings/clock/r8a7740-clock.h> | ||
| 107 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 108 | |||
| 109 | lcd-controller@fe940000 { | ||
| 110 | compatible = "renesas,r8a7740-lcdc"; | ||
| 111 | reg = <0xfe940000 0x4000>; | ||
| 112 | interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>; | ||
| 113 | clocks = <&mstp1_clks R8A7740_CLK_LCDC0>, | ||
| 114 | <&cpg_clocks R8A7740_CLK_M3>, <&lcdlclk0_clk>, | ||
| 115 | <&vou_clk>; | ||
| 116 | clock-names = "fck", "media", "lclk", "video"; | ||
| 117 | power-domains = <&pd_a4lc>; | ||
| 118 | |||
| 119 | ports { | ||
| 120 | #address-cells = <1>; | ||
| 121 | #size-cells = <0>; | ||
| 122 | |||
| 123 | port@0 { | ||
| 124 | reg = <0>; | ||
| 125 | |||
| 126 | lcdc0_rgb: endpoint { | ||
| 127 | }; | ||
| 128 | }; | ||
| 129 | }; | ||
| 130 | }; | ||
| @@ -949,6 +949,78 @@ The following tables list existing packed RGB formats. · 948 unmodified lines | |||
| 949 | - b\ :sub:`2` | 949 | - b\ :sub:`2` |
| 950 | - b\ :sub:`1` | 950 | - b\ :sub:`1` |
| 951 | - b\ :sub:`0` | 951 | - b\ :sub:`0` |
| 952 | * .. _MEDIA-BUS-FMT-RGB666-2X9-BE: | ||
| 953 | |||
| 954 | - MEDIA_BUS_FMT_RGB666_2X9_BE | ||
| 955 | - 0x1025 | ||
| 956 | - | ||
| 957 | - | ||
| 958 | - | ||
| 959 | - | ||
| 960 | - | ||
| 961 | - | ||
| 962 | - | ||
| 963 | - | ||
| 964 | - | ||
| 965 | - | ||
| 966 | - | ||
| 967 | - | ||
| 968 | - | ||
| 969 | - | ||
| 970 | - | ||
| 971 | - | ||
| 972 | - | ||
| 973 | - | ||
| 974 | - | ||
| 975 | - | ||
| 976 | - | ||
| 977 | - | ||
| 978 | - | ||
| 979 | - | ||
| 980 | - r\ :sub:`5` | ||
| 981 | - r\ :sub:`4` | ||
| 982 | - r\ :sub:`3` | ||
| 983 | - r\ :sub:`2` | ||
| 984 | - r\ :sub:`1` | ||
| 985 | - r\ :sub:`0` | ||
| 986 | - g\ :sub:`5` | ||
| 987 | - g\ :sub:`4` | ||
| 988 | - g\ :sub:`3` | ||
| 989 | * - | ||
| 990 | - | ||
| 991 | - | ||
| 992 | - | ||
| 993 | - | ||
| 994 | - | ||
| 995 | - | ||
| 996 | - | ||
| 997 | - | ||
| 998 | - | ||
| 999 | - | ||
| 1000 | - | ||
| 1001 | - | ||
| 1002 | - | ||
| 1003 | - | ||
| 1004 | - | ||
| 1005 | - | ||
| 1006 | - | ||
| 1007 | - | ||
| 1008 | - | ||
| 1009 | - | ||
| 1010 | - | ||
| 1011 | - | ||
| 1012 | - | ||
| 1013 | - | ||
| 1014 | - | ||
| 1015 | - g\ :sub:`2` | ||
| 1016 | - g\ :sub:`1` | ||
| 1017 | - g\ :sub:`0` | ||
| 1018 | - b\ :sub:`5` | ||
| 1019 | - b\ :sub:`4` | ||
| 1020 | - b\ :sub:`3` | ||
| 1021 | - b\ :sub:`2` | ||
| 1022 | - b\ :sub:`1` | ||
| 1023 | - b\ :sub:`0` | ||
| 952 | * .. _MEDIA-BUS-FMT-BGR666-1X18: | 1024 | * .. _MEDIA-BUS-FMT-BGR666-1X18: |
| 953 | 1025 | ||
| 954 | - MEDIA_BUS_FMT_BGR666_1X18 | 1026 | - MEDIA_BUS_FMT_BGR666_1X18 |
| @@ -7053,7 +7053,7 @@ F: drivers/gpu/host1x/ · 7052 unmodified lines | |||
| 7053 | F: include/linux/host1x.h | 7053 | F: include/linux/host1x.h |
| 7054 | F: include/uapi/drm/tegra_drm.h | 7054 | F: include/uapi/drm/tegra_drm.h |
| 7055 | 7055 | ||
| 7056 | DRM DRIVERS FOR RENESAS | 7056 | DRM DRIVERS FOR RENESAS R-CAR |
| 7057 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 7057 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| 7058 | M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> | 7058 | M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> |
| 7059 | L: dri-devel@lists.freedesktop.org | 7059 | L: dri-devel@lists.freedesktop.org |
| @@ -7064,7 +7064,16 @@ F: Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml · 4 unmodified lines | |||
| 7064 | F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.yaml | 7064 | F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.yaml |
| 7065 | F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml | 7065 | F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml |
| 7066 | F: Documentation/devicetree/bindings/display/renesas,du.yaml | 7066 | F: Documentation/devicetree/bindings/display/renesas,du.yaml |
| 7067 | F: drivers/gpu/drm/renesas/ | 7067 | F: drivers/gpu/drm/renesas/rcar-du/ |
| 7068 | |||
| 7069 | DRM DRIVERS FOR RENESAS SHMOBILE | ||
| 7070 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 7071 | M: Geert Uytterhoeven <geert+renesas@glider.be> | ||
| 7072 | L: dri-devel@lists.freedesktop.org | ||
| 7073 | L: linux-renesas-soc@vger.kernel.org | ||
| 7074 | S: Supported | ||
| 7075 | F: Documentation/devicetree/bindings/display/renesas,shmobile-lcdc.yaml | ||
| 7076 | F: drivers/gpu/drm/renesas/shmobile/ | ||
| 7068 | F: include/linux/platform_data/shmob_drm.h | 7077 | F: include/linux/platform_data/shmob_drm.h |
| 7069 | 7078 | ||
| 7070 | DRM DRIVERS FOR ROCKCHIP | 7079 | DRM DRIVERS FOR ROCKCHIP |
| @@ -1,11 +1,12 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | config DRM_SHMOBILE | 2 | config DRM_SHMOBILE |
| 3 | tristate "DRM Support for SH Mobile" | 3 | tristate "DRM Support for SH Mobile" |
| 4 | depends on DRM | 4 | depends on DRM && PM |
| 5 | depends on ARCH_RENESAS || ARCH_SHMOBILE || COMPILE_TEST | 5 | depends on ARCH_RENESAS || ARCH_SHMOBILE || COMPILE_TEST |
| 6 | select BACKLIGHT_CLASS_DEVICE | 6 | select BACKLIGHT_CLASS_DEVICE |
| 7 | select DRM_KMS_HELPER | 7 | select DRM_KMS_HELPER |
| 8 | select DRM_GEM_DMA_HELPER | 8 | select DRM_GEM_DMA_HELPER |
| 9 | select VIDEOMODE_HELPERS | ||
| 9 | help | 10 | help |
| 10 | Choose this option if you have an SH Mobile chipset. | 11 | Choose this option if you have an SH Mobile chipset. |
| 11 | If M is selected the module will be called shmob-drm. | 12 | If M is selected the module will be called shmob-drm. |
| @@ -1,6 +1,5 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | shmob-drm-y := shmob_drm_backlight.o \ | 2 | shmob-drm-y := shmob_drm_crtc.o \ |
| 3 | shmob_drm_crtc.o \ | ||
| 4 | shmob_drm_drv.o \ | 3 | shmob_drm_drv.o \ |
| 5 | shmob_drm_kms.o \ | 4 | shmob_drm_kms.o \ |
| 6 | shmob_drm_plane.o | 5 | shmob_drm_plane.o |
| @@ -1,82 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * shmob_drm_backlight.c -- SH Mobile DRM Backlight | ||
| 4 | * | ||
| 5 | * Copyright (C) 2012 Renesas Electronics Corporation | ||
| 6 | * | ||
| 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/backlight.h> | ||
| 11 | |||
| 12 | #include "shmob_drm_backlight.h" | ||
| 13 | #include "shmob_drm_crtc.h" | ||
| 14 | #include "shmob_drm_drv.h" | ||
| 15 | |||
| 16 | static int shmob_drm_backlight_update(struct backlight_device *bdev) | ||
| 17 | { | ||
| 18 | struct shmob_drm_connector *scon = bl_get_data(bdev); | ||
| 19 | struct shmob_drm_device *sdev = scon->connector.dev->dev_private; | ||
| 20 | const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; | ||
| 21 | int brightness = backlight_get_brightness(bdev); | ||
| 22 | |||
| 23 | return bdata->set_brightness(brightness); | ||
| 24 | } | ||
| 25 | |||
| 26 | static int shmob_drm_backlight_get_brightness(struct backlight_device *bdev) | ||
| 27 | { | ||
| 28 | struct shmob_drm_connector *scon = bl_get_data(bdev); | ||
| 29 | struct shmob_drm_device *sdev = scon->connector.dev->dev_private; | ||
| 30 | const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; | ||
| 31 | |||
| 32 | return bdata->get_brightness(); | ||
| 33 | } | ||
| 34 | |||
| 35 | static const struct backlight_ops shmob_drm_backlight_ops = { | ||
| 36 | .options = BL_CORE_SUSPENDRESUME, | ||
| 37 | .update_status = shmob_drm_backlight_update, | ||
| 38 | .get_brightness = shmob_drm_backlight_get_brightness, | ||
| 39 | }; | ||
| 40 | |||
| 41 | void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode) | ||
| 42 | { | ||
| 43 | if (scon->backlight == NULL) | ||
| 44 | return; | ||
| 45 | |||
| 46 | scon->backlight->props.power = mode == DRM_MODE_DPMS_ON | ||
| 47 | ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | ||
| 48 | backlight_update_status(scon->backlight); | ||
| 49 | } | ||
| 50 | |||
| 51 | int shmob_drm_backlight_init(struct shmob_drm_connector *scon) | ||
| 52 | { | ||
| 53 | struct shmob_drm_device *sdev = scon->connector.dev->dev_private; | ||
| 54 | const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; | ||
| 55 | struct drm_connector *connector = &scon->connector; | ||
| 56 | struct drm_device *dev = connector->dev; | ||
| 57 | struct backlight_device *backlight; | ||
| 58 | |||
| 59 | if (!bdata->max_brightness) | ||
| 60 | return 0; | ||
| 61 | |||
| 62 | backlight = backlight_device_register(bdata->name, dev->dev, scon, | ||
| 63 | &shmob_drm_backlight_ops, NULL); | ||
| 64 | if (IS_ERR(backlight)) { | ||
| 65 | dev_err(dev->dev, "unable to register backlight device: %ld\n", | ||
| 66 | PTR_ERR(backlight)); | ||
| 67 | return PTR_ERR(backlight); | ||
| 68 | } | ||
| 69 | |||
| 70 | backlight->props.max_brightness = bdata->max_brightness; | ||
| 71 | backlight->props.brightness = bdata->max_brightness; | ||
| 72 | backlight->props.power = FB_BLANK_POWERDOWN; | ||
| 73 | backlight_update_status(backlight); | ||
| 74 | |||
| 75 | scon->backlight = backlight; | ||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 79 | void shmob_drm_backlight_exit(struct shmob_drm_connector *scon) | ||
| 80 | { | ||
| 81 | backlight_device_unregister(scon->backlight); | ||
| 82 | } | ||
| @@ -1,19 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0+ */ | ||
| 2 | /* | ||
| 3 | * shmob_drm_backlight.h -- SH Mobile DRM Backlight | ||
| 4 | * | ||
| 5 | * Copyright (C) 2012 Renesas Electronics Corporation | ||
| 6 | * | ||
| 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef __SHMOB_DRM_BACKLIGHT_H__ | ||
| 11 | #define __SHMOB_DRM_BACKLIGHT_H__ | ||
| 12 | |||
| 13 | struct shmob_drm_connector; | ||
| 14 | |||
| 15 | void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode); | ||
| 16 | int shmob_drm_backlight_init(struct shmob_drm_connector *scon); | ||
| 17 | void shmob_drm_backlight_exit(struct shmob_drm_connector *scon); | ||
| 18 | |||
| 19 | #endif /* __SHMOB_DRM_BACKLIGHT_H__ */ | ||
| @@ -7,9 +7,18 @@ · 6 unmodified lines | |||
| 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <linux/backlight.h> | ||
| 11 | #include <linux/clk.h> | 10 | #include <linux/clk.h> |
| 12 | 11 | #include <linux/media-bus-format.h> | |
| 12 | #include <linux/of.h> | ||
| 13 | #include <linux/of_graph.h> | ||
| 14 | #include <linux/pm_runtime.h> | ||
| 15 | |||
| 16 | #include <drm/drm_atomic.h> | ||
| 17 | #include <drm/drm_atomic_helper.h> | ||
| 18 | #include <drm/drm_atomic_state_helper.h> | ||
| 19 | #include <drm/drm_atomic_uapi.h> | ||
| 20 | #include <drm/drm_bridge.h> | ||
| 21 | #include <drm/drm_bridge_connector.h> | ||
| 13 | #include <drm/drm_crtc.h> | 22 | #include <drm/drm_crtc.h> |
| 14 | #include <drm/drm_crtc_helper.h> | 23 | #include <drm/drm_crtc_helper.h> |
| 15 | #include <drm/drm_fb_dma_helper.h> | 24 | #include <drm/drm_fb_dma_helper.h> |
| @@ -18,85 +27,123 @@ · 2 unmodified lines | |||
| 18 | #include <drm/drm_gem_dma_helper.h> | 27 | #include <drm/drm_gem_dma_helper.h> |
| 19 | #include <drm/drm_modeset_helper.h> | 28 | #include <drm/drm_modeset_helper.h> |
| 20 | #include <drm/drm_modeset_helper_vtables.h> | 29 | #include <drm/drm_modeset_helper_vtables.h> |
| 21 | #include <drm/drm_plane_helper.h> | 30 | #include <drm/drm_panel.h> |
| 22 | #include <drm/drm_probe_helper.h> | 31 | #include <drm/drm_probe_helper.h> |
| 23 | #include <drm/drm_simple_kms_helper.h> | 32 | #include <drm/drm_simple_kms_helper.h> |
| 24 | #include <drm/drm_vblank.h> | 33 | #include <drm/drm_vblank.h> |
| 25 | 34 | ||
| 26 | #include "shmob_drm_backlight.h" | 35 | #include <video/videomode.h> |
| 36 | |||
| 27 | #include "shmob_drm_crtc.h" | 37 | #include "shmob_drm_crtc.h" |
| 28 | #include "shmob_drm_drv.h" | 38 | #include "shmob_drm_drv.h" |
| 29 | #include "shmob_drm_kms.h" | 39 | #include "shmob_drm_kms.h" |
| 30 | #include "shmob_drm_plane.h" | 40 | #include "shmob_drm_plane.h" |
| 31 | #include "shmob_drm_regs.h" | 41 | #include "shmob_drm_regs.h" |
| 32 | 42 | ||
| 33 | /* | ||
| 34 | * TODO: panel support | ||
| 35 | */ | ||
| 36 | |||
| 37 | /* ----------------------------------------------------------------------------- | 43 | /* ----------------------------------------------------------------------------- |
| 38 | * Clock management | 44 | * Page Flip |
| 39 | */ | 45 | */ |
| 40 | 46 | ||
| 41 | static int shmob_drm_clk_on(struct shmob_drm_device *sdev) | 47 | void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc) |
| 42 | { | 48 | { |
| 43 | int ret; | 49 | struct drm_pending_vblank_event *event; |
| 50 | struct drm_device *dev = scrtc->base.dev; | ||
| 51 | unsigned long flags; | ||
| 44 | 52 | ||
| 45 | if (sdev->clock) { | 53 | spin_lock_irqsave(&dev->event_lock, flags); |
| 46 | ret = clk_prepare_enable(sdev->clock); | 54 | event = scrtc->event; |
| 47 | if (ret < 0) | 55 | scrtc->event = NULL; |
| 48 | return ret; | 56 | if (event) { |
| 57 | drm_crtc_send_vblank_event(&scrtc->base, event); | ||
| 58 | wake_up(&scrtc->flip_wait); | ||
| 59 | drm_crtc_vblank_put(&scrtc->base); | ||
| 49 | } | 60 | } |
| 61 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 62 | } | ||
| 50 | 63 | ||
| 51 | return 0; | 64 | static bool shmob_drm_crtc_page_flip_pending(struct shmob_drm_crtc *scrtc) |
| 65 | { | ||
| 66 | struct drm_device *dev = scrtc->base.dev; | ||
| 67 | unsigned long flags; | ||
| 68 | bool pending; | ||
| 69 | |||
| 70 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 71 | pending = scrtc->event != NULL; | ||
| 72 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 73 | |||
| 74 | return pending; | ||
| 52 | } | 75 | } |
| 53 | 76 | ||
| 54 | static void shmob_drm_clk_off(struct shmob_drm_device *sdev) | 77 | static void shmob_drm_crtc_wait_page_flip(struct shmob_drm_crtc *scrtc) |
| 55 | { | 78 | { |
| 56 | if (sdev->clock) | 79 | struct drm_crtc *crtc = &scrtc->base; |
| 57 | clk_disable_unprepare(sdev->clock); | 80 | struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); |
| 81 | |||
| 82 | if (wait_event_timeout(scrtc->flip_wait, | ||
| 83 | !shmob_drm_crtc_page_flip_pending(scrtc), | ||
| 84 | msecs_to_jiffies(50))) | ||
| 85 | return; | ||
| 86 | |||
| 87 | dev_warn(sdev->dev, "page flip timeout\n"); | ||
| 88 | |||
| 89 | shmob_drm_crtc_finish_page_flip(scrtc); | ||
| 58 | } | 90 | } |
| 59 | 91 | ||
| 60 | /* ----------------------------------------------------------------------------- | 92 | /* ----------------------------------------------------------------------------- |
| 61 | * CRTC | 93 | * CRTC |
| 62 | */ | 94 | */ |
| 63 | 95 | ||
| 96 | static const struct { | ||
| 97 | u32 fmt; | ||
| 98 | u32 ldmt1r; | ||
| 99 | } shmob_drm_bus_fmts[] = { | ||
| 100 | { MEDIA_BUS_FMT_RGB888_3X8, LDMT1R_MIFTYP_RGB8 }, | ||
| 101 | { MEDIA_BUS_FMT_RGB666_2X9_BE, LDMT1R_MIFTYP_RGB9 }, | ||
| 102 | { MEDIA_BUS_FMT_RGB888_2X12_BE, LDMT1R_MIFTYP_RGB12A }, | ||
| 103 | { MEDIA_BUS_FMT_RGB444_1X12, LDMT1R_MIFTYP_RGB12B }, | ||
| 104 | { MEDIA_BUS_FMT_RGB565_1X16, LDMT1R_MIFTYP_RGB16 }, | ||
| 105 | { MEDIA_BUS_FMT_RGB666_1X18, LDMT1R_MIFTYP_RGB18 }, | ||
| 106 | { MEDIA_BUS_FMT_RGB888_1X24, LDMT1R_MIFTYP_RGB24 }, | ||
| 107 | { MEDIA_BUS_FMT_UYVY8_1X16, LDMT1R_MIFTYP_YCBCR }, | ||
| 108 | }; | ||
| 109 | |||
| 64 | static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc) | 110 | static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc) |
| 65 | { | 111 | { |
| 66 | struct drm_crtc *crtc = &scrtc->crtc; | 112 | struct drm_crtc *crtc = &scrtc->base; |
| 67 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | 113 | struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); |
| 68 | const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; | 114 | const struct drm_display_info *info = &sdev->connector->display_info; |
| 69 | const struct drm_display_mode *mode = &crtc->mode; | 115 | const struct drm_display_mode *mode = &crtc->mode; |
| 116 | unsigned int i; | ||
| 70 | u32 value; | 117 | u32 value; |
| 71 | 118 | ||
| 72 | value = sdev->ldmt1r | 119 | if (!info->num_bus_formats || !info->bus_formats) { |
| 73 | | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL) | 120 | dev_warn(sdev->dev, "No bus format reported, using RGB888\n"); |
| 74 | | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL) | 121 | value = LDMT1R_MIFTYP_RGB24; |
| 75 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0) | 122 | } else { |
| 76 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0) | 123 | for (i = 0; i < ARRAY_SIZE(shmob_drm_bus_fmts); i++) { |
| 77 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0) | 124 | if (shmob_drm_bus_fmts[i].fmt == info->bus_formats[0]) |
| 78 | | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0) | 125 | break; |
| 79 | | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0); | 126 | } |
| 80 | lcdc_write(sdev, LDMT1R, value); | 127 | if (i < ARRAY_SIZE(shmob_drm_bus_fmts)) { |
| 81 | 128 | value = shmob_drm_bus_fmts[i].ldmt1r; | |
| 82 | if (idata->interface >= SHMOB_DRM_IFACE_SYS8A && | 129 | } else { |
| 83 | idata->interface <= SHMOB_DRM_IFACE_SYS24) { | 130 | dev_warn(sdev->dev, |
| 84 | /* Setup SYS bus. */ | 131 | "unsupported bus format 0x%x, using RGB888\n", |
| 85 | value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT) | 132 | info->bus_formats[0]); |
| 86 | | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0) | 133 | value = LDMT1R_MIFTYP_RGB24; |
| 87 | | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0) | 134 | } |
| 88 | | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT) | ||
| 89 | | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT) | ||
| 90 | | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT); | ||
| 91 | lcdc_write(sdev, LDMT2R, value); | ||
| 92 | |||
| 93 | value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT) | ||
| 94 | | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT) | ||
| 95 | | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT) | ||
| 96 | | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT); | ||
| 97 | lcdc_write(sdev, LDMT3R, value); | ||
| 98 | } | 135 | } |
| 99 | 136 | ||
| 137 | if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) | ||
| 138 | value |= LDMT1R_DWPOL; | ||
| 139 | if (info->bus_flags & DRM_BUS_FLAG_DE_LOW) | ||
| 140 | value |= LDMT1R_DIPOL; | ||
| 141 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
| 142 | value |= LDMT1R_VPOL; | ||
| 143 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
| 144 | value |= LDMT1R_HPOL; | ||
| 145 | lcdc_write(sdev, LDMT1R, value); | ||
| 146 | |||
| 100 | value = ((mode->hdisplay / 8) << 16) /* HDCN */ | 147 | value = ((mode->hdisplay / 8) << 16) /* HDCN */ |
| 101 | | (mode->htotal / 8); /* HTCN */ | 148 | | (mode->htotal / 8); /* HTCN */ |
| 102 | lcdc_write(sdev, LDHCNR, value); | 149 | lcdc_write(sdev, LDHCNR, value); |
| @@ -121,7 +168,7 @@ static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc) · 18 unmodified lines | |||
| 121 | 168 | ||
| 122 | static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start) | 169 | static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start) |
| 123 | { | 170 | { |
| 124 | struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private; | 171 | struct shmob_drm_device *sdev = to_shmob_device(scrtc->base.dev); |
| 125 | u32 value; | 172 | u32 value; |
| 126 | 173 | ||
| 127 | value = lcdc_read(sdev, LDCNT2R); | 174 | value = lcdc_read(sdev, LDCNT2R); |
| @@ -145,34 +192,23 @@ static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start) · 17 unmodified lines | |||
| 145 | } | 192 | } |
| 146 | } | 193 | } |
| 147 | 194 | ||
| 148 | /* | 195 | static inline struct shmob_drm_crtc *to_shmob_crtc(struct drm_crtc *crtc) |
| 149 | * shmob_drm_crtc_start - Configure and start the LCDC | 196 | { |
| 150 | * @scrtc: the SH Mobile CRTC | 197 | return container_of(crtc, struct shmob_drm_crtc, base); |
| 151 | * | 198 | } |
| 152 | * Configure and start the LCDC device. External devices (clocks, MERAM, panels, | 199 | |
| 153 | * ...) are not touched by this function. | 200 | static void shmob_drm_crtc_atomic_enable(struct drm_crtc *crtc, |
| 154 | */ | 201 | struct drm_atomic_state *state) |
| 155 | static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) | ||
| 156 | { | 202 | { |
| 157 | struct drm_crtc *crtc = &scrtc->crtc; | 203 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); |
| 158 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | 204 | struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); |
| 159 | const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; | 205 | unsigned int clk_div = sdev->config.clk_div; |
| 160 | const struct shmob_drm_format_info *format; | 206 | struct device *dev = sdev->dev; |
| 161 | struct drm_device *dev = sdev->ddev; | ||
| 162 | struct drm_plane *plane; | ||
| 163 | u32 value; | 207 | u32 value; |
| 164 | int ret; | 208 | int ret; |
| 165 | 209 | ||
| 166 | if (scrtc->started) | 210 | ret = pm_runtime_resume_and_get(dev); |
| 167 | return; | 211 | if (ret) |
| 168 | |||
| 169 | format = shmob_drm_format_info(crtc->primary->fb->format->format); | ||
| 170 | if (WARN_ON(format == NULL)) | ||
| 171 | return; | ||
| 172 | |||
| 173 | /* Enable clocks before accessing the hardware. */ | ||
| 174 | ret = shmob_drm_clk_on(sdev); | ||
| 175 | if (ret < 0) | ||
| 176 | return; | 212 | return; |
| 177 | 213 | ||
| 178 | /* Reset and enable the LCDC. */ | 214 | /* Reset and enable the LCDC. */ |
| @@ -188,79 +224,50 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) · 9 unmodified lines | |||
| 188 | lcdc_write(sdev, LDPMR, 0); | 224 | lcdc_write(sdev, LDPMR, 0); |
| 189 | 225 | ||
| 190 | value = sdev->lddckr; | 226 | value = sdev->lddckr; |
| 191 | if (idata->clk_div) { | 227 | if (clk_div) { |
| 192 | /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider | 228 | /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider |
| 193 | * denominator. | 229 | * denominator. |
| 194 | */ | 230 | */ |
| 195 | lcdc_write(sdev, LDDCKPAT1R, 0); | 231 | lcdc_write(sdev, LDDCKPAT1R, 0); |
| 196 | lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1); | 232 | lcdc_write(sdev, LDDCKPAT2R, (1 << (clk_div / 2)) - 1); |
| 197 | 233 | ||
| 198 | if (idata->clk_div == 1) | 234 | if (clk_div == 1) |
| 199 | value |= LDDCKR_MOSEL; | 235 | value |= LDDCKR_MOSEL; |
| 200 | else | 236 | else |
| 201 | value |= idata->clk_div; | 237 | value |= clk_div; |
| 202 | } | 238 | } |
| 203 | 239 | ||
| 204 | lcdc_write(sdev, LDDCKR, value); | 240 | lcdc_write(sdev, LDDCKR, value); |
| 205 | lcdc_write(sdev, LDDCKSTPR, 0); | 241 | lcdc_write(sdev, LDDCKSTPR, 0); |
| 206 | lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0); | 242 | lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0); |
| 207 | 243 | ||
| 208 | /* TODO: Setup SYS panel */ | ||
| 209 | |||
| 210 | /* Setup geometry, format, frame buffer memory and operation mode. */ | 244 | /* Setup geometry, format, frame buffer memory and operation mode. */ |
| 211 | shmob_drm_crtc_setup_geometry(scrtc); | 245 | shmob_drm_crtc_setup_geometry(scrtc); |
| 212 | 246 | ||
| 213 | /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */ | ||
| 214 | lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1); | ||
| 215 | lcdc_write(sdev, LDMLSR, scrtc->line_size); | ||
| 216 | lcdc_write(sdev, LDSA1R, scrtc->dma[0]); | ||
| 217 | if (format->yuv) | ||
| 218 | lcdc_write(sdev, LDSA2R, scrtc->dma[1]); | ||
| 219 | lcdc_write(sdev, LDSM1R, 0); | 247 | lcdc_write(sdev, LDSM1R, 0); |
| 220 | 248 | ||
| 221 | /* Word and long word swap. */ | ||
| 222 | switch (format->fourcc) { | ||
| 223 | case DRM_FORMAT_RGB565: | ||
| 224 | case DRM_FORMAT_NV21: | ||
| 225 | case DRM_FORMAT_NV61: | ||
| 226 | case DRM_FORMAT_NV42: | ||
| 227 | value = LDDDSR_LS | LDDDSR_WS; | ||
| 228 | break; | ||
| 229 | case DRM_FORMAT_RGB888: | ||
| 230 | case DRM_FORMAT_NV12: | ||
| 231 | case DRM_FORMAT_NV16: | ||
| 232 | case DRM_FORMAT_NV24: | ||
| 233 | value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; | ||
| 234 | break; | ||
| 235 | case DRM_FORMAT_ARGB8888: | ||
| 236 | case DRM_FORMAT_XRGB8888: | ||
| 237 | default: | ||
| 238 | value = LDDDSR_LS; | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | lcdc_write(sdev, LDDDSR, value); | ||
| 242 | |||
| 243 | /* Setup planes. */ | ||
| 244 | drm_for_each_legacy_plane(plane, dev) { | ||
| 245 | if (plane->crtc == crtc) | ||
| 246 | shmob_drm_plane_setup(plane); | ||
| 247 | } | ||
| 248 | |||
| 249 | /* Enable the display output. */ | 249 | /* Enable the display output. */ |
| 250 | lcdc_write(sdev, LDCNT1R, LDCNT1R_DE); | 250 | lcdc_write(sdev, LDCNT1R, LDCNT1R_DE); |
| 251 | 251 | ||
| 252 | shmob_drm_crtc_start_stop(scrtc, true); | 252 | shmob_drm_crtc_start_stop(scrtc, true); |
| 253 | 253 | ||
| 254 | scrtc->started = true; | 254 | /* Turn vertical blank interrupt reporting back on. */ |
| 255 | drm_crtc_vblank_on(crtc); | ||
| 255 | } | 256 | } |
| 256 | 257 | ||
| 257 | static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc) | 258 | static void shmob_drm_crtc_atomic_disable(struct drm_crtc *crtc, |
| 259 | struct drm_atomic_state *state) | ||
| 258 | { | 260 | { |
| 259 | struct drm_crtc *crtc = &scrtc->crtc; | 261 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); |
| 260 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | 262 | struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); |
| 261 | 263 | ||
| 262 | if (!scrtc->started) | 264 | /* |
| 263 | return; | 265 | * Disable vertical blank interrupt reporting. We first need to wait |
| 266 | * for page flip completion before stopping the CRTC as userspace | ||
| 267 | * expects page flips to eventually complete. | ||
| 268 | */ | ||
| 269 | shmob_drm_crtc_wait_page_flip(scrtc); | ||
| 270 | drm_crtc_vblank_off(crtc); | ||
| 264 | 271 | ||
| 265 | /* Stop the LCDC. */ | 272 | /* Stop the LCDC. */ |
| 266 | shmob_drm_crtc_start_stop(scrtc, false); | 273 | shmob_drm_crtc_start_stop(scrtc, false); |
| @@ -268,145 +275,31 @@ static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc) · 1 unmodified line | |||
| 268 | /* Disable the display output. */ | 275 | /* Disable the display output. */ |
| 269 | lcdc_write(sdev, LDCNT1R, 0); | 276 | lcdc_write(sdev, LDCNT1R, 0); |
| 270 | 277 | ||
| 271 | /* Stop clocks. */ | 278 | pm_runtime_put(sdev->dev); |
| 272 | shmob_drm_clk_off(sdev); | ||
| 273 | |||
| 274 | scrtc->started = false; | ||
| 275 | } | ||
| 276 | |||
| 277 | void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc) | ||
| 278 | { | ||
| 279 | shmob_drm_crtc_stop(scrtc); | ||
| 280 | } | ||
| 281 | |||
| 282 | void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc) | ||
| 283 | { | ||
| 284 | if (scrtc->dpms != DRM_MODE_DPMS_ON) | ||
| 285 | return; | ||
| 286 | |||
| 287 | shmob_drm_crtc_start(scrtc); | ||
| 288 | } | 279 | } |
| 289 | 280 | ||
| 290 | static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc, | 281 | static void shmob_drm_crtc_atomic_flush(struct drm_crtc *crtc, |
| 291 | int x, int y) | 282 | struct drm_atomic_state *state) |
| 292 | { | 283 | { |
| 293 | struct drm_crtc *crtc = &scrtc->crtc; | 284 | struct drm_pending_vblank_event *event; |
| 294 | struct drm_framebuffer *fb = crtc->primary->fb; | 285 | struct drm_device *dev = crtc->dev; |
| 295 | struct drm_gem_dma_object *gem; | 286 | unsigned long flags; |
| 296 | unsigned int bpp; | ||
| 297 | |||
| 298 | bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp; | ||
| 299 | gem = drm_fb_dma_get_gem_obj(fb, 0); | ||
| 300 | scrtc->dma[0] = gem->dma_addr + fb->offsets[0] | ||
| 301 | + y * fb->pitches[0] + x * bpp / 8; | ||
| 302 | |||
| 303 | if (scrtc->format->yuv) { | ||
| 304 | bpp = scrtc->format->bpp - 8; | ||
| 305 | gem = drm_fb_dma_get_gem_obj(fb, 1); | ||
| 306 | scrtc->dma[1] = gem->dma_addr + fb->offsets[1] | ||
| 307 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] | ||
| 308 | + x * (bpp == 16 ? 2 : 1); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc) | ||
| 313 | { | ||
| 314 | struct drm_crtc *crtc = &scrtc->crtc; | ||
| 315 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
| 316 | |||
| 317 | shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y); | ||
| 318 | |||
| 319 | lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]); | ||
| 320 | if (scrtc->format->yuv) | ||
| 321 | lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]); | ||
| 322 | |||
| 323 | lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS); | ||
| 324 | } | ||
| 325 | |||
| 326 | #define to_shmob_crtc(c) container_of(c, struct shmob_drm_crtc, crtc) | ||
| 327 | |||
| 328 | static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
| 329 | { | ||
| 330 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | ||
| 331 | |||
| 332 | if (scrtc->dpms == mode) | ||
| 333 | return; | ||
| 334 | |||
| 335 | if (mode == DRM_MODE_DPMS_ON) | ||
| 336 | shmob_drm_crtc_start(scrtc); | ||
| 337 | else | ||
| 338 | shmob_drm_crtc_stop(scrtc); | ||
| 339 | |||
| 340 | scrtc->dpms = mode; | ||
| 341 | } | ||
| 342 | |||
| 343 | static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc) | ||
| 344 | { | ||
| 345 | shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
| 346 | } | ||
| 347 | 287 | ||
| 348 | static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc, | 288 | if (crtc->state->event) { |
| 349 | struct drm_display_mode *mode, | 289 | spin_lock_irqsave(&dev->event_lock, flags); |
| 350 | struct drm_display_mode *adjusted_mode, | 290 | event = crtc->state->event; |
| 351 | int x, int y, | 291 | crtc->state->event = NULL; |
| 352 | struct drm_framebuffer *old_fb) | 292 | drm_crtc_send_vblank_event(crtc, event); |
| 353 | { | 293 | spin_unlock_irqrestore(&dev->event_lock, flags); |
| 354 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | ||
| 355 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | ||
| 356 | const struct shmob_drm_format_info *format; | ||
| 357 | |||
| 358 | format = shmob_drm_format_info(crtc->primary->fb->format->format); | ||
| 359 | if (format == NULL) { | ||
| 360 | dev_dbg(sdev->dev, "mode_set: unsupported format %p4cc\n", | ||
| 361 | &crtc->primary->fb->format->format); | ||
| 362 | return -EINVAL; | ||
| 363 | } | 294 | } |
| 364 | |||
| 365 | scrtc->format = format; | ||
| 366 | scrtc->line_size = crtc->primary->fb->pitches[0]; | ||
| 367 | |||
| 368 | shmob_drm_crtc_compute_base(scrtc, x, y); | ||
| 369 | |||
| 370 | return 0; | ||
| 371 | } | ||
| 372 | |||
| 373 | static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc) | ||
| 374 | { | ||
| 375 | shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | ||
| 376 | } | ||
| 377 | |||
| 378 | static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
| 379 | struct drm_framebuffer *old_fb) | ||
| 380 | { | ||
| 381 | shmob_drm_crtc_update_base(to_shmob_crtc(crtc)); | ||
| 382 | |||
| 383 | return 0; | ||
| 384 | } | 295 | } |
| 385 | 296 | ||
| 386 | static const struct drm_crtc_helper_funcs crtc_helper_funcs = { | 297 | static const struct drm_crtc_helper_funcs crtc_helper_funcs = { |
| 387 | .dpms = shmob_drm_crtc_dpms, | 298 | .atomic_flush = shmob_drm_crtc_atomic_flush, |
| 388 | .prepare = shmob_drm_crtc_mode_prepare, | 299 | .atomic_enable = shmob_drm_crtc_atomic_enable, |
| 389 | .commit = shmob_drm_crtc_mode_commit, | 300 | .atomic_disable = shmob_drm_crtc_atomic_disable, |
| 390 | .mode_set = shmob_drm_crtc_mode_set, | ||
| 391 | .mode_set_base = shmob_drm_crtc_mode_set_base, | ||
| 392 | }; | 301 | }; |
| 393 | 302 | ||
| 394 | void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc) | ||
| 395 | { | ||
| 396 | struct drm_pending_vblank_event *event; | ||
| 397 | struct drm_device *dev = scrtc->crtc.dev; | ||
| 398 | unsigned long flags; | ||
| 399 | |||
| 400 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 401 | event = scrtc->event; | ||
| 402 | scrtc->event = NULL; | ||
| 403 | if (event) { | ||
| 404 | drm_crtc_send_vblank_event(&scrtc->crtc, event); | ||
| 405 | drm_crtc_vblank_put(&scrtc->crtc); | ||
| 406 | } | ||
| 407 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 408 | } | ||
| 409 | |||
| 410 | static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, | 303 | static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, |
| 411 | struct drm_framebuffer *fb, | 304 | struct drm_framebuffer *fb, |
| 412 | struct drm_pending_vblank_event *event, | 305 | struct drm_pending_vblank_event *event, |
| @@ -414,7 +307,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, · 1 unmodified line | |||
| 414 | struct drm_modeset_acquire_ctx *ctx) | 307 | struct drm_modeset_acquire_ctx *ctx) |
| 415 | { | 308 | { |
| 416 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | 309 | struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); |
| 417 | struct drm_device *dev = scrtc->crtc.dev; | 310 | struct drm_device *dev = scrtc->base.dev; |
| 418 | unsigned long flags; | 311 | unsigned long flags; |
| 419 | 312 | ||
| 420 | spin_lock_irqsave(&dev->event_lock, flags); | 313 | spin_lock_irqsave(&dev->event_lock, flags); |
| @@ -424,12 +317,11 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, · 3 unmodified lines | |||
| 424 | } | 317 | } |
| 425 | spin_unlock_irqrestore(&dev->event_lock, flags); | 318 | spin_unlock_irqrestore(&dev->event_lock, flags); |
| 426 | 319 | ||
| 427 | crtc->primary->fb = fb; | 320 | drm_atomic_set_fb_for_plane(crtc->primary->state, fb); |
| 428 | shmob_drm_crtc_update_base(scrtc); | ||
| 429 | 321 | ||
| 430 | if (event) { | 322 | if (event) { |
| 431 | event->pipe = 0; | 323 | event->pipe = 0; |
| 432 | drm_crtc_vblank_get(&scrtc->crtc); | 324 | drm_crtc_vblank_get(&scrtc->base); |
| 433 | spin_lock_irqsave(&dev->event_lock, flags); | 325 | spin_lock_irqsave(&dev->event_lock, flags); |
| 434 | scrtc->event = event; | 326 | scrtc->event = event; |
| 435 | spin_unlock_irqrestore(&dev->event_lock, flags); | 327 | spin_unlock_irqrestore(&dev->event_lock, flags); |
| @@ -457,7 +349,7 @@ static void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev, · 21 unmodified lines | |||
| 457 | 349 | ||
| 458 | static int shmob_drm_enable_vblank(struct drm_crtc *crtc) | 350 | static int shmob_drm_enable_vblank(struct drm_crtc *crtc) |
| 459 | { | 351 | { |
| 460 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | 352 | struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); |
| 461 | 353 | ||
| 462 | shmob_drm_crtc_enable_vblank(sdev, true); | 354 | shmob_drm_crtc_enable_vblank(sdev, true); |
| 463 | 355 | ||
| @@ -466,88 +358,65 @@ static int shmob_drm_enable_vblank(struct drm_crtc *crtc) · 2 unmodified lines | |||
| 466 | 358 | ||
| 467 | static void shmob_drm_disable_vblank(struct drm_crtc *crtc) | 359 | static void shmob_drm_disable_vblank(struct drm_crtc *crtc) |
| 468 | { | 360 | { |
| 469 | struct shmob_drm_device *sdev = crtc->dev->dev_private; | 361 | struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); |
| 470 | 362 | ||
| 471 | shmob_drm_crtc_enable_vblank(sdev, false); | 363 | shmob_drm_crtc_enable_vblank(sdev, false); |
| 472 | } | 364 | } |
| 473 | 365 | ||
| 474 | static const struct drm_crtc_funcs crtc_funcs = { | 366 | static const struct drm_crtc_funcs crtc_funcs = { |
| 367 | .reset = drm_atomic_helper_crtc_reset, | ||
| 475 | .destroy = drm_crtc_cleanup, | 368 | .destroy = drm_crtc_cleanup, |
| 476 | .set_config = drm_crtc_helper_set_config, | 369 | .set_config = drm_atomic_helper_set_config, |
| 477 | .page_flip = shmob_drm_crtc_page_flip, | 370 | .page_flip = shmob_drm_crtc_page_flip, |
| 371 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | ||
| 372 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | ||
| 478 | .enable_vblank = shmob_drm_enable_vblank, | 373 | .enable_vblank = shmob_drm_enable_vblank, |
| 479 | .disable_vblank = shmob_drm_disable_vblank, | 374 | .disable_vblank = shmob_drm_disable_vblank, |
| 480 | }; | 375 | }; |
| 481 | 376 | ||
| 482 | static const uint32_t modeset_formats[] = { | ||
| 483 | DRM_FORMAT_RGB565, | ||
| 484 | DRM_FORMAT_RGB888, | ||
| 485 | DRM_FORMAT_ARGB8888, | ||
| 486 | DRM_FORMAT_XRGB8888, | ||
| 487 | }; | ||
| 488 | |||
| 489 | static const struct drm_plane_funcs primary_plane_funcs = { | ||
| 490 | DRM_PLANE_NON_ATOMIC_FUNCS, | ||
| 491 | }; | ||
| 492 | |||
| 493 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev) | 377 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev) |
| 494 | { | 378 | { |
| 495 | struct drm_crtc *crtc = &sdev->crtc.crtc; | 379 | struct drm_crtc *crtc = &sdev->crtc.base; |
| 496 | struct drm_plane *primary; | 380 | struct drm_plane *primary, *plane; |
| 381 | unsigned int i; | ||
| 497 | int ret; | 382 | int ret; |
| 498 | 383 | ||
| 499 | sdev->crtc.dpms = DRM_MODE_DPMS_OFF; | 384 | init_waitqueue_head(&sdev->crtc.flip_wait); |
| 500 | 385 | ||
| 501 | primary = __drm_universal_plane_alloc(sdev->ddev, sizeof(*primary), 0, | 386 | primary = shmob_drm_plane_create(sdev, DRM_PLANE_TYPE_PRIMARY, 0); |
| 502 | 0, &primary_plane_funcs, | ||
| 503 | modeset_formats, | ||
| 504 | ARRAY_SIZE(modeset_formats), | ||
| 505 | NULL, DRM_PLANE_TYPE_PRIMARY, | ||
| 506 | NULL); | ||
| 507 | if (IS_ERR(primary)) | 387 | if (IS_ERR(primary)) |
| 508 | return PTR_ERR(primary); | 388 | return PTR_ERR(primary); |
| 509 | 389 | ||
| 510 | ret = drm_crtc_init_with_planes(sdev->ddev, crtc, primary, NULL, | 390 | for (i = 1; i < 5; ++i) { |
| 391 | plane = shmob_drm_plane_create(sdev, DRM_PLANE_TYPE_OVERLAY, i); | ||
| 392 | if (IS_ERR(plane)) | ||
| 393 | return PTR_ERR(plane); | ||
| 394 | } | ||
| 395 | |||
| 396 | ret = drm_crtc_init_with_planes(&sdev->ddev, crtc, primary, NULL, | ||
| 511 | &crtc_funcs, NULL); | 397 | &crtc_funcs, NULL); |
| 512 | if (ret < 0) { | 398 | if (ret < 0) |
| 513 | drm_plane_cleanup(primary); | ||
| 514 | kfree(primary); | ||
| 515 | return ret; | 399 | return ret; |
| 516 | } | ||
| 517 | 400 | ||
| 518 | drm_crtc_helper_add(crtc, &crtc_helper_funcs); | 401 | drm_crtc_helper_add(crtc, &crtc_helper_funcs); |
| 519 | 402 | ||
| 403 | /* Start with vertical blank interrupt reporting disabled. */ | ||
| 404 | drm_crtc_vblank_off(crtc); | ||
| 405 | |||
| 520 | return 0; | 406 | return 0; |
| 521 | } | 407 | } |
| 522 | 408 | ||
| 523 | /* ----------------------------------------------------------------------------- | 409 | /* ----------------------------------------------------------------------------- |
| 524 | * Encoder | 410 | * Legacy Encoder |
| 525 | */ | 411 | */ |
| 526 | 412 | ||
| 527 | #define to_shmob_encoder(e) \ | ||
| 528 | container_of(e, struct shmob_drm_encoder, encoder) | ||
| 529 | |||
| 530 | static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
| 531 | { | ||
| 532 | struct shmob_drm_encoder *senc = to_shmob_encoder(encoder); | ||
| 533 | struct shmob_drm_device *sdev = encoder->dev->dev_private; | ||
| 534 | struct shmob_drm_connector *scon = &sdev->connector; | ||
| 535 | |||
| 536 | if (senc->dpms == mode) | ||
| 537 | return; | ||
| 538 | |||
| 539 | shmob_drm_backlight_dpms(scon, mode); | ||
| 540 | |||
| 541 | senc->dpms = mode; | ||
| 542 | } | ||
| 543 | |||
| 544 | static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder, | 413 | static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder, |
| 545 | const struct drm_display_mode *mode, | 414 | const struct drm_display_mode *mode, |
| 546 | struct drm_display_mode *adjusted_mode) | 415 | struct drm_display_mode *adjusted_mode) |
| 547 | { | 416 | { |
| 548 | struct drm_device *dev = encoder->dev; | 417 | struct drm_device *dev = encoder->dev; |
| 549 | struct shmob_drm_device *sdev = dev->dev_private; | 418 | struct shmob_drm_device *sdev = to_shmob_device(dev); |
| 550 | struct drm_connector *connector = &sdev->connector.connector; | 419 | struct drm_connector *connector = sdev->connector; |
| 551 | const struct drm_display_mode *panel_mode; | 420 | const struct drm_display_mode *panel_mode; |
| 552 | 421 | ||
| 553 | if (list_empty(&connector->modes)) { | 422 | if (list_empty(&connector->modes)) { |
| @@ -563,60 +432,61 @@ static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder, · 9 unmodified lines | |||
| 563 | return true; | 432 | return true; |
| 564 | } | 433 | } |
| 565 | 434 | ||
| 566 | static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder) | ||
| 567 | { | ||
| 568 | /* No-op, everything is handled in the CRTC code. */ | ||
| 569 | } | ||
| 570 | |||
| 571 | static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder, | ||
| 572 | struct drm_display_mode *mode, | ||
| 573 | struct drm_display_mode *adjusted_mode) | ||
| 574 | { | ||
| 575 | /* No-op, everything is handled in the CRTC code. */ | ||
| 576 | } | ||
| 577 | |||
| 578 | static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder) | ||
| 579 | { | ||
| 580 | /* No-op, everything is handled in the CRTC code. */ | ||
| 581 | } | ||
| 582 | |||
| 583 | static const struct drm_encoder_helper_funcs encoder_helper_funcs = { | 435 | static const struct drm_encoder_helper_funcs encoder_helper_funcs = { |
| 584 | .dpms = shmob_drm_encoder_dpms, | ||
| 585 | .mode_fixup = shmob_drm_encoder_mode_fixup, | 436 | .mode_fixup = shmob_drm_encoder_mode_fixup, |
| 586 | .prepare = shmob_drm_encoder_mode_prepare, | ||
| 587 | .commit = shmob_drm_encoder_mode_commit, | ||
| 588 | .mode_set = shmob_drm_encoder_mode_set, | ||
| 589 | }; | 437 | }; |
| 590 | 438 | ||
| 439 | /* ----------------------------------------------------------------------------- | ||
| 440 | * Encoder | ||
| 441 | */ | ||
| 442 | |||
| 591 | int shmob_drm_encoder_create(struct shmob_drm_device *sdev) | 443 | int shmob_drm_encoder_create(struct shmob_drm_device *sdev) |
| 592 | { | 444 | { |
| 593 | struct drm_encoder *encoder = &sdev->encoder.encoder; | 445 | struct drm_encoder *encoder = &sdev->encoder; |
| 446 | struct drm_bridge *bridge; | ||
| 594 | int ret; | 447 | int ret; |
| 595 | 448 | ||
| 596 | sdev->encoder.dpms = DRM_MODE_DPMS_OFF; | ||
| 597 | |||
| 598 | encoder->possible_crtcs = 1; | 449 | encoder->possible_crtcs = 1; |
| 599 | 450 | ||
| 600 | ret = drm_simple_encoder_init(sdev->ddev, encoder, | 451 | ret = drm_simple_encoder_init(&sdev->ddev, encoder, |
| 601 | DRM_MODE_ENCODER_LVDS); | 452 | DRM_MODE_ENCODER_DPI); |
| 602 | if (ret < 0) | 453 | if (ret < 0) |
| 603 | return ret; | 454 | return ret; |
| 604 | 455 | ||
| 605 | drm_encoder_helper_add(encoder, &encoder_helper_funcs); | 456 | if (sdev->pdata) { |
| 457 | drm_encoder_helper_add(encoder, &encoder_helper_funcs); | ||
| 458 | return 0; | ||
| 459 | } | ||
| 460 | |||
| 461 | /* Create a panel bridge */ | ||
| 462 | bridge = devm_drm_of_get_bridge(sdev->dev, sdev->dev->of_node, 0, 0); | ||
| 463 | if (IS_ERR(bridge)) | ||
| 464 | return PTR_ERR(bridge); | ||
| 465 | |||
| 466 | /* Attach the bridge to the encoder */ | ||
| 467 | ret = drm_bridge_attach(encoder, bridge, NULL, | ||
| 468 | DRM_BRIDGE_ATTACH_NO_CONNECTOR); | ||
| 469 | if (ret) { | ||
| 470 | dev_err(sdev->dev, "failed to attach bridge: %pe\n", | ||
| 471 | ERR_PTR(ret)); | ||
| 472 | return ret; | ||
| 473 | } | ||
| 606 | 474 | ||
| 607 | return 0; | 475 | return 0; |
| 608 | } | 476 | } |
| 609 | 477 | ||
| 610 | /* ----------------------------------------------------------------------------- | 478 | /* ----------------------------------------------------------------------------- |
| 611 | * Connector | 479 | * Legacy Connector |
| 612 | */ | 480 | */ |
| 613 | 481 | ||
| 614 | #define to_shmob_connector(c) \ | 482 | static inline struct shmob_drm_connector *to_shmob_connector(struct drm_connector *connector) |
| 615 | container_of(c, struct shmob_drm_connector, connector) | 483 | { |
| 484 | return container_of(connector, struct shmob_drm_connector, base); | ||
| 485 | } | ||
| 616 | 486 | ||
| 617 | static int shmob_drm_connector_get_modes(struct drm_connector *connector) | 487 | static int shmob_drm_connector_get_modes(struct drm_connector *connector) |
| 618 | { | 488 | { |
| 619 | struct shmob_drm_device *sdev = connector->dev->dev_private; | 489 | struct shmob_drm_connector *scon = to_shmob_connector(connector); |
| 620 | struct drm_display_mode *mode; | 490 | struct drm_display_mode *mode; |
| 621 | 491 | ||
| 622 | mode = drm_mode_create(connector->dev); | 492 | mode = drm_mode_create(connector->dev); |
| @@ -624,22 +494,10 @@ static int shmob_drm_connector_get_modes(struct drm_connector *connector) · 1 unmodified line | |||
| 624 | return 0; | 494 | return 0; |
| 625 | 495 | ||
| 626 | mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; | 496 | mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; |
| 627 | mode->clock = sdev->pdata->panel.mode.clock; | ||
| 628 | mode->hdisplay = sdev->pdata->panel.mode.hdisplay; | ||
| 629 | mode->hsync_start = sdev->pdata->panel.mode.hsync_start; | ||
| 630 | mode->hsync_end = sdev->pdata->panel.mode.hsync_end; | ||
| 631 | mode->htotal = sdev->pdata->panel.mode.htotal; | ||
| 632 | mode->vdisplay = sdev->pdata->panel.mode.vdisplay; | ||
| 633 | mode->vsync_start = sdev->pdata->panel.mode.vsync_start; | ||
| 634 | mode->vsync_end = sdev->pdata->panel.mode.vsync_end; | ||
| 635 | mode->vtotal = sdev->pdata->panel.mode.vtotal; | ||
| 636 | mode->flags = sdev->pdata->panel.mode.flags; | ||
| 637 | |||
| 638 | drm_mode_set_name(mode); | ||
| 639 | drm_mode_probed_add(connector, mode); | ||
| 640 | 497 | ||
| 641 | connector->display_info.width_mm = sdev->pdata->panel.width_mm; | 498 | drm_display_mode_from_videomode(scon->mode, mode); |
| 642 | connector->display_info.height_mm = sdev->pdata->panel.height_mm; | 499 | |
| 500 | drm_mode_probed_add(connector, mode); | ||
| 643 | 501 | ||
| 644 | return 1; | 502 | return 1; |
| 645 | } | 503 | } |
| @@ -659,54 +517,106 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { · 13 unmodified lines | |||
| 659 | 517 | ||
| 660 | static void shmob_drm_connector_destroy(struct drm_connector *connector) | 518 | static void shmob_drm_connector_destroy(struct drm_connector *connector) |
| 661 | { | 519 | { |
| 662 | struct shmob_drm_connector *scon = to_shmob_connector(connector); | ||
| 663 | |||
| 664 | shmob_drm_backlight_exit(scon); | ||
| 665 | drm_connector_unregister(connector); | 520 | drm_connector_unregister(connector); |
| 666 | drm_connector_cleanup(connector); | 521 | drm_connector_cleanup(connector); |
| 522 | |||
| 523 | kfree(connector); | ||
| 667 | } | 524 | } |
| 668 | 525 | ||
| 669 | static const struct drm_connector_funcs connector_funcs = { | 526 | static const struct drm_connector_funcs connector_funcs = { |
| 670 | .dpms = drm_helper_connector_dpms, | 527 | .reset = drm_atomic_helper_connector_reset, |
| 671 | .fill_modes = drm_helper_probe_single_connector_modes, | 528 | .fill_modes = drm_helper_probe_single_connector_modes, |
| 672 | .destroy = shmob_drm_connector_destroy, | 529 | .destroy = shmob_drm_connector_destroy, |
| 530 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
| 531 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
| 673 | }; | 532 | }; |
| 674 | 533 | ||
| 675 | int shmob_drm_connector_create(struct shmob_drm_device *sdev, | 534 | static struct drm_connector * |
| 676 | struct drm_encoder *encoder) | 535 | shmob_drm_connector_init(struct shmob_drm_device *sdev, |
| 536 | struct drm_encoder *encoder) | ||
| 677 | { | 537 | { |
| 678 | struct drm_connector *connector = &sdev->connector.connector; | 538 | u32 bus_fmt = sdev->pdata->iface.bus_fmt; |
| 539 | struct shmob_drm_connector *scon; | ||
| 540 | struct drm_connector *connector; | ||
| 541 | struct drm_display_info *info; | ||
| 542 | unsigned int i; | ||
| 679 | int ret; | 543 | int ret; |
| 680 | 544 | ||
| 681 | sdev->connector.encoder = encoder; | 545 | for (i = 0; i < ARRAY_SIZE(shmob_drm_bus_fmts); i++) { |
| 546 | if (shmob_drm_bus_fmts[i].fmt == bus_fmt) | ||
| 547 | break; | ||
| 548 | } | ||
| 549 | if (i == ARRAY_SIZE(shmob_drm_bus_fmts)) { | ||
| 550 | dev_err(sdev->dev, "unsupported bus format 0x%x\n", bus_fmt); | ||
| 551 | return ERR_PTR(-EINVAL); | ||
| 552 | } | ||
| 682 | 553 | ||
| 683 | connector->display_info.width_mm = sdev->pdata->panel.width_mm; | 554 | scon = kzalloc(sizeof(*scon), GFP_KERNEL); |
| 684 | connector->display_info.height_mm = sdev->pdata->panel.height_mm; | 555 | if (!scon) |
| 556 | return ERR_PTR(-ENOMEM); | ||
| 685 | 557 | ||
| 686 | ret = drm_connector_init(sdev->ddev, connector, &connector_funcs, | 558 | connector = &scon->base; |
| 687 | DRM_MODE_CONNECTOR_LVDS); | 559 | scon->encoder = encoder; |
| 688 | if (ret < 0) | 560 | scon->mode = &sdev->pdata->panel.mode; |
| 689 | return ret; | 561 | |
| 562 | info = &connector->display_info; | ||
| 563 | info->width_mm = sdev->pdata->panel.width_mm; | ||
| 564 | info->height_mm = sdev->pdata->panel.height_mm; | ||
| 565 | |||
| 566 | if (scon->mode->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) | ||
| 567 | info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; | ||
| 568 | if (scon->mode->flags & DISPLAY_FLAGS_DE_LOW) | ||
| 569 | info->bus_flags |= DRM_BUS_FLAG_DE_LOW; | ||
| 570 | |||
| 571 | ret = drm_display_info_set_bus_formats(info, &bus_fmt, 1); | ||
| 572 | if (ret < 0) { | ||
| 573 | kfree(scon); | ||
| 574 | return ERR_PTR(ret); | ||
| 575 | } | ||
| 576 | |||
| 577 | ret = drm_connector_init(&sdev->ddev, connector, &connector_funcs, | ||
| 578 | DRM_MODE_CONNECTOR_DPI); | ||
| 579 | if (ret < 0) { | ||
| 580 | kfree(scon); | ||
| 581 | return ERR_PTR(ret); | ||
| 582 | } | ||
| 690 | 583 | ||
| 691 | drm_connector_helper_add(connector, &connector_helper_funcs); | 584 | drm_connector_helper_add(connector, &connector_helper_funcs); |
| 692 | 585 | ||
| 693 | ret = shmob_drm_backlight_init(&sdev->connector); | 586 | return connector; |
| 694 | if (ret < 0) | 587 | } |
| 695 | goto err_cleanup; | 588 | |
| 589 | /* ----------------------------------------------------------------------------- | ||
| 590 | * Connector | ||
| 591 | */ | ||
| 592 | |||
| 593 | int shmob_drm_connector_create(struct shmob_drm_device *sdev, | ||
| 594 | struct drm_encoder *encoder) | ||
| 595 | { | ||
| 596 | struct drm_connector *connector; | ||
| 597 | int ret; | ||
| 598 | |||
| 599 | if (sdev->pdata) | ||
| 600 | connector = shmob_drm_connector_init(sdev, encoder); | ||
| 601 | else | ||
| 602 | connector = drm_bridge_connector_init(&sdev->ddev, encoder); | ||
| 603 | if (IS_ERR(connector)) { | ||
| 604 | dev_err(sdev->dev, "failed to created connector: %pe\n", | ||
| 605 | connector); | ||
| 606 | return PTR_ERR(connector); | ||
| 607 | } | ||
| 696 | 608 | ||
| 697 | ret = drm_connector_attach_encoder(connector, encoder); | 609 | ret = drm_connector_attach_encoder(connector, encoder); |
| 698 | if (ret < 0) | 610 | if (ret < 0) |
| 699 | goto err_backlight; | 611 | goto error; |
| 612 | |||
| 613 | connector->dpms = DRM_MODE_DPMS_OFF; | ||
| 700 | 614 | ||
| 701 | drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); | 615 | sdev->connector = connector; |
| 702 | drm_object_property_set_value(&connector->base, | ||
| 703 | sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); | ||
| 704 | 616 | ||
| 705 | return 0; | 617 | return 0; |
| 706 | 618 | ||
| 707 | err_backlight: | 619 | error: |
| 708 | shmob_drm_backlight_exit(&sdev->connector); | ||
| 709 | err_cleanup: | ||
| 710 | drm_connector_cleanup(connector); | 620 | drm_connector_cleanup(connector); |
| 711 | return ret; | 621 | return ret; |
| 712 | } | 622 | } |
| @@ -14,39 +14,30 @@ · 13 unmodified lines | |||
| 14 | #include <drm/drm_connector.h> | 14 | #include <drm/drm_connector.h> |
| 15 | #include <drm/drm_encoder.h> | 15 | #include <drm/drm_encoder.h> |
| 16 | 16 | ||
| 17 | struct backlight_device; | 17 | #include <linux/wait.h> |
| 18 | |||
| 19 | #include <video/videomode.h> | ||
| 20 | |||
| 18 | struct drm_pending_vblank_event; | 21 | struct drm_pending_vblank_event; |
| 19 | struct shmob_drm_device; | 22 | struct shmob_drm_device; |
| 20 | struct shmob_drm_format_info; | 23 | struct shmob_drm_format_info; |
| 21 | 24 | ||
| 22 | struct shmob_drm_crtc { | 25 | struct shmob_drm_crtc { |
| 23 | struct drm_crtc crtc; | 26 | struct drm_crtc base; |
| 24 | 27 | ||
| 25 | struct drm_pending_vblank_event *event; | 28 | struct drm_pending_vblank_event *event; |
| 26 | int dpms; | 29 | wait_queue_head_t flip_wait; |
| 27 | |||
| 28 | const struct shmob_drm_format_info *format; | ||
| 29 | unsigned long dma[2]; | ||
| 30 | unsigned int line_size; | ||
| 31 | bool started; | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct shmob_drm_encoder { | ||
| 35 | struct drm_encoder encoder; | ||
| 36 | int dpms; | ||
| 37 | }; | 30 | }; |
| 38 | 31 | ||
| 32 | /* Legacy connector */ | ||
| 39 | struct shmob_drm_connector { | 33 | struct shmob_drm_connector { |
| 40 | struct drm_connector connector; | 34 | struct drm_connector base; |
| 41 | struct drm_encoder *encoder; | 35 | struct drm_encoder *encoder; |
| 42 | 36 | const struct videomode *mode; | |
| 43 | struct backlight_device *backlight; | ||
| 44 | }; | 37 | }; |
| 45 | 38 | ||
| 46 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev); | 39 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev); |
| 47 | void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc); | 40 | void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc); |
| 48 | void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc); | ||
| 49 | void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc); | ||
| 50 | 41 | ||
| 51 | int shmob_drm_encoder_create(struct shmob_drm_device *sdev); | 42 | int shmob_drm_encoder_create(struct shmob_drm_device *sdev); |
| 52 | int shmob_drm_connector_create(struct shmob_drm_device *sdev, | 43 | int shmob_drm_connector_create(struct shmob_drm_device *sdev, |
| @@ -11,13 +11,17 @@ · 10 unmodified lines | |||
| 11 | #include <linux/io.h> | 11 | #include <linux/io.h> |
| 12 | #include <linux/mm.h> | 12 | #include <linux/mm.h> |
| 13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
| 14 | #include <linux/of.h> | ||
| 14 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
| 15 | #include <linux/pm.h> | 16 | #include <linux/pm.h> |
| 17 | #include <linux/pm_runtime.h> | ||
| 16 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
| 17 | 19 | ||
| 20 | #include <drm/drm_atomic_helper.h> | ||
| 18 | #include <drm/drm_drv.h> | 21 | #include <drm/drm_drv.h> |
| 19 | #include <drm/drm_fbdev_generic.h> | 22 | #include <drm/drm_fbdev_generic.h> |
| 20 | #include <drm/drm_gem_dma_helper.h> | 23 | #include <drm/drm_gem_dma_helper.h> |
| 24 | #include <drm/drm_modeset_helper.h> | ||
| 21 | #include <drm/drm_module.h> | 25 | #include <drm/drm_module.h> |
| 22 | #include <drm/drm_probe_helper.h> | 26 | #include <drm/drm_probe_helper.h> |
| 23 | #include <drm/drm_vblank.h> | 27 | #include <drm/drm_vblank.h> |
| @@ -31,57 +35,23 @@ · 7 unmodified lines | |||
| 31 | * Hardware initialization | 35 | * Hardware initialization |
| 32 | */ | 36 | */ |
| 33 | 37 | ||
| 34 | static int shmob_drm_init_interface(struct shmob_drm_device *sdev) | ||
| 35 | { | ||
| 36 | static const u32 ldmt1r[] = { | ||
| 37 | [SHMOB_DRM_IFACE_RGB8] = LDMT1R_MIFTYP_RGB8, | ||
| 38 | [SHMOB_DRM_IFACE_RGB9] = LDMT1R_MIFTYP_RGB9, | ||
| 39 | [SHMOB_DRM_IFACE_RGB12A] = LDMT1R_MIFTYP_RGB12A, | ||
| 40 | [SHMOB_DRM_IFACE_RGB12B] = LDMT1R_MIFTYP_RGB12B, | ||
| 41 | [SHMOB_DRM_IFACE_RGB16] = LDMT1R_MIFTYP_RGB16, | ||
| 42 | [SHMOB_DRM_IFACE_RGB18] = LDMT1R_MIFTYP_RGB18, | ||
| 43 | [SHMOB_DRM_IFACE_RGB24] = LDMT1R_MIFTYP_RGB24, | ||
| 44 | [SHMOB_DRM_IFACE_YUV422] = LDMT1R_MIFTYP_YCBCR, | ||
| 45 | [SHMOB_DRM_IFACE_SYS8A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A, | ||
| 46 | [SHMOB_DRM_IFACE_SYS8B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B, | ||
| 47 | [SHMOB_DRM_IFACE_SYS8C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C, | ||
| 48 | [SHMOB_DRM_IFACE_SYS8D] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D, | ||
| 49 | [SHMOB_DRM_IFACE_SYS9] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9, | ||
| 50 | [SHMOB_DRM_IFACE_SYS12] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12, | ||
| 51 | [SHMOB_DRM_IFACE_SYS16A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A, | ||
| 52 | [SHMOB_DRM_IFACE_SYS16B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B, | ||
| 53 | [SHMOB_DRM_IFACE_SYS16C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C, | ||
| 54 | [SHMOB_DRM_IFACE_SYS18] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18, | ||
| 55 | [SHMOB_DRM_IFACE_SYS24] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24, | ||
| 56 | }; | ||
| 57 | |||
| 58 | if (sdev->pdata->iface.interface >= ARRAY_SIZE(ldmt1r)) { | ||
| 59 | dev_err(sdev->dev, "invalid interface type %u\n", | ||
| 60 | sdev->pdata->iface.interface); | ||
| 61 | return -EINVAL; | ||
| 62 | } | ||
| 63 | |||
| 64 | sdev->ldmt1r = ldmt1r[sdev->pdata->iface.interface]; | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev, | 38 | static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev, |
| 69 | enum shmob_drm_clk_source clksrc) | 39 | enum shmob_drm_clk_source clksrc) |
| 70 | { | 40 | { |
| 71 | struct clk *clk; | 41 | struct clk *clk; |
| 72 | char *clkname; | 42 | char *clkname; |
| 73 | 43 | ||
| 74 | switch (clksrc) { | 44 | switch (clksrc) { |
| 75 | case SHMOB_DRM_CLK_BUS: | 45 | case SHMOB_DRM_CLK_BUS: |
| 76 | clkname = "bus_clk"; | 46 | clkname = "fck"; |
| 77 | sdev->lddckr = LDDCKR_ICKSEL_BUS; | 47 | sdev->lddckr = LDDCKR_ICKSEL_BUS; |
| 78 | break; | 48 | break; |
| 79 | case SHMOB_DRM_CLK_PERIPHERAL: | 49 | case SHMOB_DRM_CLK_PERIPHERAL: |
| 80 | clkname = "peripheral_clk"; | 50 | clkname = "media"; |
| 81 | sdev->lddckr = LDDCKR_ICKSEL_MIPI; | 51 | sdev->lddckr = LDDCKR_ICKSEL_MIPI; |
| 82 | break; | 52 | break; |
| 83 | case SHMOB_DRM_CLK_EXTERNAL: | 53 | case SHMOB_DRM_CLK_EXTERNAL: |
| 84 | clkname = NULL; | 54 | clkname = "lclk"; |
| 85 | sdev->lddckr = LDDCKR_ICKSEL_HDMI; | 55 | sdev->lddckr = LDDCKR_ICKSEL_HDMI; |
| 86 | break; | 56 | break; |
| 87 | default: | 57 | default: |
| @@ -105,7 +75,7 @@ static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev, · 17 unmodified lines | |||
| 105 | static irqreturn_t shmob_drm_irq(int irq, void *arg) | 75 | static irqreturn_t shmob_drm_irq(int irq, void *arg) |
| 106 | { | 76 | { |
| 107 | struct drm_device *dev = arg; | 77 | struct drm_device *dev = arg; |
| 108 | struct shmob_drm_device *sdev = dev->dev_private; | 78 | struct shmob_drm_device *sdev = to_shmob_device(dev); |
| 109 | unsigned long flags; | 79 | unsigned long flags; |
| 110 | u32 status; | 80 | u32 status; |
| 111 | 81 | ||
| @@ -119,7 +89,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg) · 7 unmodified lines | |||
| 119 | spin_unlock_irqrestore(&sdev->irq_lock, flags); | 89 | spin_unlock_irqrestore(&sdev->irq_lock, flags); |
| 120 | 90 | ||
| 121 | if (status & LDINTR_VES) { | 91 | if (status & LDINTR_VES) { |
| 122 | drm_handle_vblank(dev, 0); | 92 | drm_crtc_handle_vblank(&sdev->crtc.base); |
| 123 | shmob_drm_crtc_finish_page_flip(&sdev->crtc); | 93 | shmob_drm_crtc_finish_page_flip(&sdev->crtc); |
| 124 | } | 94 | } |
| 125 | 95 | ||
| @@ -129,7 +99,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg) · 3 unmodified lines | |||
| 129 | DEFINE_DRM_GEM_DMA_FOPS(shmob_drm_fops); | 99 | DEFINE_DRM_GEM_DMA_FOPS(shmob_drm_fops); |
| 130 | 100 | ||
| 131 | static const struct drm_driver shmob_drm_driver = { | 101 | static const struct drm_driver shmob_drm_driver = { |
| 132 | .driver_features = DRIVER_GEM | DRIVER_MODESET, | 102 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
| 133 | DRM_GEM_DMA_DRIVER_OPS, | 103 | DRM_GEM_DMA_DRIVER_OPS, |
| 134 | .fops = &shmob_drm_fops, | 104 | .fops = &shmob_drm_fops, |
| 135 | .name = "shmob-drm", | 105 | .name = "shmob-drm", |
| @@ -147,26 +117,45 @@ static int shmob_drm_pm_suspend(struct device *dev) · 11 unmodified lines | |||
| 147 | { | 117 | { |
| 148 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); | 118 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); |
| 149 | 119 | ||
| 150 | drm_kms_helper_poll_disable(sdev->ddev); | 120 | return drm_mode_config_helper_suspend(&sdev->ddev); |
| 151 | shmob_drm_crtc_suspend(&sdev->crtc); | 121 | } |
| 122 | |||
| 123 | static int shmob_drm_pm_resume(struct device *dev) | ||
| 124 | { | ||
| 125 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); | ||
| 126 | |||
| 127 | return drm_mode_config_helper_resume(&sdev->ddev); | ||
| 128 | } | ||
| 129 | |||
| 130 | static int shmob_drm_pm_runtime_suspend(struct device *dev) | ||
| 131 | { | ||
| 132 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); | ||
| 133 | |||
| 134 | if (sdev->clock) | ||
| 135 | clk_disable_unprepare(sdev->clock); | ||
| 152 | 136 | ||
| 153 | return 0; | 137 | return 0; |
| 154 | } | 138 | } |
| 155 | 139 | ||
| 156 | static int shmob_drm_pm_resume(struct device *dev) | 140 | static int shmob_drm_pm_runtime_resume(struct device *dev) |
| 157 | { | 141 | { |
| 158 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); | 142 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); |
| 143 | int ret; | ||
| 159 | 144 | ||
| 160 | drm_modeset_lock_all(sdev->ddev); | 145 | if (sdev->clock) { |
| 161 | shmob_drm_crtc_resume(&sdev->crtc); | 146 | ret = clk_prepare_enable(sdev->clock); |
| 162 | drm_modeset_unlock_all(sdev->ddev); | 147 | if (ret < 0) |
| 148 | return ret; | ||
| 149 | } | ||
| 163 | 150 | ||
| 164 | drm_kms_helper_poll_enable(sdev->ddev); | ||
| 165 | return 0; | 151 | return 0; |
| 166 | } | 152 | } |
| 167 | 153 | ||
| 168 | static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops, | 154 | static const struct dev_pm_ops shmob_drm_pm_ops = { |
| 169 | shmob_drm_pm_suspend, shmob_drm_pm_resume); | 155 | SYSTEM_SLEEP_PM_OPS(shmob_drm_pm_suspend, shmob_drm_pm_resume) |
| 156 | RUNTIME_PM_OPS(shmob_drm_pm_runtime_suspend, | ||
| 157 | shmob_drm_pm_runtime_resume, NULL) | ||
| 158 | }; | ||
| 170 | 159 | ||
| 171 | /* ----------------------------------------------------------------------------- | 160 | /* ----------------------------------------------------------------------------- |
| 172 | * Platform driver | 161 | * Platform driver |
| @@ -175,37 +164,45 @@ static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops, · 2 unmodified lines | |||
| 175 | static void shmob_drm_remove(struct platform_device *pdev) | 164 | static void shmob_drm_remove(struct platform_device *pdev) |
| 176 | { | 165 | { |
| 177 | struct shmob_drm_device *sdev = platform_get_drvdata(pdev); | 166 | struct shmob_drm_device *sdev = platform_get_drvdata(pdev); |
| 178 | struct drm_device *ddev = sdev->ddev; | 167 | struct drm_device *ddev = &sdev->ddev; |
| 179 | 168 | ||
| 180 | drm_dev_unregister(ddev); | 169 | drm_dev_unregister(ddev); |
| 170 | drm_atomic_helper_shutdown(ddev); | ||
| 181 | drm_kms_helper_poll_fini(ddev); | 171 | drm_kms_helper_poll_fini(ddev); |
| 182 | free_irq(sdev->irq, ddev); | ||
| 183 | drm_dev_put(ddev); | ||
| 184 | } | 172 | } |
| 185 | 173 | ||
| 186 | static int shmob_drm_probe(struct platform_device *pdev) | 174 | static int shmob_drm_probe(struct platform_device *pdev) |
| 187 | { | 175 | { |
| 188 | struct shmob_drm_platform_data *pdata = pdev->dev.platform_data; | 176 | struct shmob_drm_platform_data *pdata = pdev->dev.platform_data; |
| 177 | const struct shmob_drm_config *config; | ||
| 189 | struct shmob_drm_device *sdev; | 178 | struct shmob_drm_device *sdev; |
| 190 | struct drm_device *ddev; | 179 | struct drm_device *ddev; |
| 191 | unsigned int i; | ||
| 192 | int ret; | 180 | int ret; |
| 193 | 181 | ||
| 194 | if (pdata == NULL) { | 182 | config = of_device_get_match_data(&pdev->dev); |
| 183 | if (!config && !pdata) { | ||
| 195 | dev_err(&pdev->dev, "no platform data\n"); | 184 | dev_err(&pdev->dev, "no platform data\n"); |
| 196 | return -EINVAL; | 185 | return -EINVAL; |
| 197 | } | 186 | } |
| 198 | 187 | ||
| 199 | /* | 188 | /* |
| 200 | * Allocate and initialize the driver private data, I/O resources and | 189 | * Allocate and initialize the DRM device, driver private data, I/O |
| 201 | * clocks. | 190 | * resources and clocks. |
| 202 | */ | 191 | */ |
| 203 | sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); | 192 | sdev = devm_drm_dev_alloc(&pdev->dev, &shmob_drm_driver, |
| 204 | if (sdev == NULL) | 193 | struct shmob_drm_device, ddev); |
| 205 | return -ENOMEM; | 194 | if (IS_ERR(sdev)) |
| 195 | return PTR_ERR(sdev); | ||
| 206 | 196 | ||
| 197 | ddev = &sdev->ddev; | ||
| 207 | sdev->dev = &pdev->dev; | 198 | sdev->dev = &pdev->dev; |
| 208 | sdev->pdata = pdata; | 199 | if (config) { |
| 200 | sdev->config = *config; | ||
| 201 | } else { | ||
| 202 | sdev->pdata = pdata; | ||
| 203 | sdev->config.clk_source = pdata->clk_source; | ||
| 204 | sdev->config.clk_div = pdata->iface.clk_div; | ||
| 205 | } | ||
| 209 | spin_lock_init(&sdev->irq_lock); | 206 | spin_lock_init(&sdev->irq_lock); |
| 210 | 207 | ||
| 211 | platform_set_drvdata(pdev, sdev); | 208 | platform_set_drvdata(pdev, sdev); |
| @@ -214,49 +211,32 @@ static int shmob_drm_probe(struct platform_device *pdev) · 2 unmodified lines | |||
| 214 | if (IS_ERR(sdev->mmio)) | 211 | if (IS_ERR(sdev->mmio)) |
| 215 | return PTR_ERR(sdev->mmio); | 212 | return PTR_ERR(sdev->mmio); |
| 216 | 213 | ||
| 217 | ret = shmob_drm_setup_clocks(sdev, pdata->clk_source); | 214 | ret = shmob_drm_setup_clocks(sdev, sdev->config.clk_source); |
| 218 | if (ret < 0) | 215 | if (ret < 0) |
| 219 | return ret; | 216 | return ret; |
| 220 | 217 | ||
| 221 | ret = shmob_drm_init_interface(sdev); | 218 | ret = devm_pm_runtime_enable(&pdev->dev); |
| 222 | if (ret < 0) | 219 | if (ret) |
| 223 | return ret; | 220 | return ret; |
| 224 | 221 | ||
| 225 | /* Allocate and initialize the DRM device. */ | ||
| 226 | ddev = drm_dev_alloc(&shmob_drm_driver, &pdev->dev); | ||
| 227 | if (IS_ERR(ddev)) | ||
| 228 | return PTR_ERR(ddev); | ||
| 229 | |||
| 230 | sdev->ddev = ddev; | ||
| 231 | ddev->dev_private = sdev; | ||
| 232 | |||
| 233 | ret = shmob_drm_modeset_init(sdev); | ||
| 234 | if (ret < 0) { | ||
| 235 | dev_err(&pdev->dev, "failed to initialize mode setting\n"); | ||
| 236 | goto err_free_drm_dev; | ||
| 237 | } | ||
| 238 | |||
| 239 | for (i = 0; i < 4; ++i) { | ||
| 240 | ret = shmob_drm_plane_create(sdev, i); | ||
| 241 | if (ret < 0) { | ||
| 242 | dev_err(&pdev->dev, "failed to create plane %u\n", i); | ||
| 243 | goto err_modeset_cleanup; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | ret = drm_vblank_init(ddev, 1); | 222 | ret = drm_vblank_init(ddev, 1); |
| 248 | if (ret < 0) { | 223 | if (ret < 0) { |
| 249 | dev_err(&pdev->dev, "failed to initialize vblank\n"); | 224 | dev_err(&pdev->dev, "failed to initialize vblank\n"); |
| 250 | goto err_modeset_cleanup; | 225 | return ret; |
| 251 | } | 226 | } |
| 252 | 227 | ||
| 228 | ret = shmob_drm_modeset_init(sdev); | ||
| 229 | if (ret < 0) | ||
| 230 | return dev_err_probe(&pdev->dev, ret, | ||
| 231 | "failed to initialize mode setting\n"); | ||
| 232 | |||
| 253 | ret = platform_get_irq(pdev, 0); | 233 | ret = platform_get_irq(pdev, 0); |
| 254 | if (ret < 0) | 234 | if (ret < 0) |
| 255 | goto err_modeset_cleanup; | 235 | goto err_modeset_cleanup; |
| 256 | sdev->irq = ret; | 236 | sdev->irq = ret; |
| 257 | 237 | ||
| 258 | ret = request_irq(sdev->irq, shmob_drm_irq, 0, ddev->driver->name, | 238 | ret = devm_request_irq(&pdev->dev, sdev->irq, shmob_drm_irq, 0, |
| 259 | ddev); | 239 | ddev->driver->name, ddev); |
| 260 | if (ret < 0) { | 240 | if (ret < 0) { |
| 261 | dev_err(&pdev->dev, "failed to install IRQ handler\n"); | 241 | dev_err(&pdev->dev, "failed to install IRQ handler\n"); |
| 262 | goto err_modeset_cleanup; | 242 | goto err_modeset_cleanup; |
| @@ -268,28 +248,35 @@ static int shmob_drm_probe(struct platform_device *pdev) · 5 unmodified lines | |||
| 268 | */ | 248 | */ |
| 269 | ret = drm_dev_register(ddev, 0); | 249 | ret = drm_dev_register(ddev, 0); |
| 270 | if (ret < 0) | 250 | if (ret < 0) |
| 271 | goto err_irq_uninstall; | 251 | goto err_modeset_cleanup; |
| 272 | 252 | ||
| 273 | drm_fbdev_generic_setup(ddev, 16); | 253 | drm_fbdev_generic_setup(ddev, 16); |
| 274 | 254 | ||
| 275 | return 0; | 255 | return 0; |
| 276 | 256 | ||
| 277 | err_irq_uninstall: | ||
| 278 | free_irq(sdev->irq, ddev); | ||
| 279 | err_modeset_cleanup: | 257 | err_modeset_cleanup: |
| 280 | drm_kms_helper_poll_fini(ddev); | 258 | drm_kms_helper_poll_fini(ddev); |
| 281 | err_free_drm_dev: | ||
| 282 | drm_dev_put(ddev); | ||
| 283 | |||
| 284 | return ret; | 259 | return ret; |
| 285 | } | 260 | } |
| 286 | 261 | ||
| 262 | static const struct shmob_drm_config shmob_arm_config = { | ||
| 263 | .clk_source = SHMOB_DRM_CLK_BUS, | ||
| 264 | .clk_div = 5, | ||
| 265 | }; | ||
| 266 | |||
| 267 | static const struct of_device_id shmob_drm_of_table[] __maybe_unused = { | ||
| 268 | { .compatible = "renesas,r8a7740-lcdc", .data = &shmob_arm_config, }, | ||
| 269 | { .compatible = "renesas,sh73a0-lcdc", .data = &shmob_arm_config, }, | ||
| 270 | { /* sentinel */ } | ||
| 271 | }; | ||
| 272 | |||
| 287 | static struct platform_driver shmob_drm_platform_driver = { | 273 | static struct platform_driver shmob_drm_platform_driver = { |
| 288 | .probe = shmob_drm_probe, | 274 | .probe = shmob_drm_probe, |
| 289 | .remove_new = shmob_drm_remove, | 275 | .remove_new = shmob_drm_remove, |
| 290 | .driver = { | 276 | .driver = { |
| 291 | .name = "shmob-drm", | 277 | .name = "shmob-drm", |
| 292 | .pm = pm_sleep_ptr(&shmob_drm_pm_ops), | 278 | .of_match_table = of_match_ptr(shmob_drm_of_table), |
| 279 | .pm = &shmob_drm_pm_ops, | ||
| 293 | }, | 280 | }, |
| 294 | }; | 281 | }; |
| 295 | 282 | ||
| @@ -20,23 +20,33 @@ struct clk; · 19 unmodified lines | |||
| 20 | struct device; | 20 | struct device; |
| 21 | struct drm_device; | 21 | struct drm_device; |
| 22 | 22 | ||
| 23 | struct shmob_drm_config { | ||
| 24 | enum shmob_drm_clk_source clk_source; | ||
| 25 | unsigned int clk_div; | ||
| 26 | }; | ||
| 27 | |||
| 23 | struct shmob_drm_device { | 28 | struct shmob_drm_device { |
| 24 | struct device *dev; | 29 | struct device *dev; |
| 25 | const struct shmob_drm_platform_data *pdata; | 30 | const struct shmob_drm_platform_data *pdata; |
| 31 | struct shmob_drm_config config; | ||
| 26 | 32 | ||
| 27 | void __iomem *mmio; | 33 | void __iomem *mmio; |
| 28 | struct clk *clock; | 34 | struct clk *clock; |
| 29 | u32 lddckr; | 35 | u32 lddckr; |
| 30 | u32 ldmt1r; | ||
| 31 | 36 | ||
| 32 | unsigned int irq; | 37 | unsigned int irq; |
| 33 | spinlock_t irq_lock; /* Protects hardware LDINTR register */ | 38 | spinlock_t irq_lock; /* Protects hardware LDINTR register */ |
| 34 | 39 | ||
| 35 | struct drm_device *ddev; | 40 | struct drm_device ddev; |
| 36 | 41 | ||
| 37 | struct shmob_drm_crtc crtc; | 42 | struct shmob_drm_crtc crtc; |
| 38 | struct shmob_drm_encoder encoder; | 43 | struct drm_encoder encoder; |
| 39 | struct shmob_drm_connector connector; | 44 | struct drm_connector *connector; |
| 40 | }; | 45 | }; |
| 41 | 46 | ||
| 47 | static inline struct shmob_drm_device *to_shmob_device(struct drm_device *dev) | ||
| 48 | { | ||
| 49 | return container_of(dev, struct shmob_drm_device, ddev); | ||
| 50 | } | ||
| 51 | |||
| 42 | #endif /* __SHMOB_DRM_DRV_H__ */ | 52 | #endif /* __SHMOB_DRM_DRV_H__ */ |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | |||
| 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <drm/drm_atomic_helper.h> | ||
| 10 | #include <drm/drm_crtc.h> | 11 | #include <drm/drm_crtc.h> |
| 11 | #include <drm/drm_crtc_helper.h> | 12 | #include <drm/drm_crtc_helper.h> |
| 12 | #include <drm/drm_fourcc.h> | 13 | #include <drm/drm_fourcc.h> |
| @@ -17,6 +18,7 @@ · 4 unmodified lines | |||
| 17 | #include "shmob_drm_crtc.h" | 18 | #include "shmob_drm_crtc.h" |
| 18 | #include "shmob_drm_drv.h" | 19 | #include "shmob_drm_drv.h" |
| 19 | #include "shmob_drm_kms.h" | 20 | #include "shmob_drm_kms.h" |
| 21 | #include "shmob_drm_plane.h" | ||
| 20 | #include "shmob_drm_regs.h" | 22 | #include "shmob_drm_regs.h" |
| 21 | 23 | ||
| 22 | /* ----------------------------------------------------------------------------- | 24 | /* ----------------------------------------------------------------------------- |
| @@ -27,53 +29,73 @@ static const struct shmob_drm_format_info shmob_drm_format_infos[] = { · 4 unmodified lines | |||
| 27 | { | 29 | { |
| 28 | .fourcc = DRM_FORMAT_RGB565, | 30 | .fourcc = DRM_FORMAT_RGB565, |
| 29 | .bpp = 16, | 31 | .bpp = 16, |
| 30 | .yuv = false, | ||
| 31 | .lddfr = LDDFR_PKF_RGB16, | 32 | .lddfr = LDDFR_PKF_RGB16, |
| 33 | .ldddsr = LDDDSR_LS | LDDDSR_WS, | ||
| 34 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 35 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16, | ||
| 32 | }, { | 36 | }, { |
| 33 | .fourcc = DRM_FORMAT_RGB888, | 37 | .fourcc = DRM_FORMAT_RGB888, |
| 34 | .bpp = 24, | 38 | .bpp = 24, |
| 35 | .yuv = false, | ||
| 36 | .lddfr = LDDFR_PKF_RGB24, | 39 | .lddfr = LDDFR_PKF_RGB24, |
| 40 | .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, | ||
| 41 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 42 | LDBBSIFR_SWPB | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24, | ||
| 37 | }, { | 43 | }, { |
| 38 | .fourcc = DRM_FORMAT_ARGB8888, | 44 | .fourcc = DRM_FORMAT_ARGB8888, |
| 39 | .bpp = 32, | 45 | .bpp = 32, |
| 40 | .yuv = false, | ||
| 41 | .lddfr = LDDFR_PKF_ARGB32, | 46 | .lddfr = LDDFR_PKF_ARGB32, |
| 47 | .ldddsr = LDDDSR_LS, | ||
| 48 | .ldbbsifr = LDBBSIFR_AL_PK | LDBBSIFR_SWPL | LDBBSIFR_RY | | ||
| 49 | LDBBSIFR_RPKF_ARGB32, | ||
| 42 | }, { | 50 | }, { |
| 43 | .fourcc = DRM_FORMAT_XRGB8888, | 51 | .fourcc = DRM_FORMAT_XRGB8888, |
| 44 | .bpp = 32, | 52 | .bpp = 32, |
| 45 | .yuv = false, | ||
| 46 | .lddfr = LDDFR_PKF_ARGB32, | 53 | .lddfr = LDDFR_PKF_ARGB32, |
| 54 | .ldddsr = LDDDSR_LS, | ||
| 55 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_RY | | ||
| 56 | LDBBSIFR_RPKF_ARGB32, | ||
| 47 | }, { | 57 | }, { |
| 48 | .fourcc = DRM_FORMAT_NV12, | 58 | .fourcc = DRM_FORMAT_NV12, |
| 49 | .bpp = 12, | 59 | .bpp = 12, |
| 50 | .yuv = true, | ||
| 51 | .lddfr = LDDFR_CC | LDDFR_YF_420, | 60 | .lddfr = LDDFR_CC | LDDFR_YF_420, |
| 61 | .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, | ||
| 62 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 63 | LDBBSIFR_SWPB | LDBBSIFR_CHRR_420, | ||
| 52 | }, { | 64 | }, { |
| 53 | .fourcc = DRM_FORMAT_NV21, | 65 | .fourcc = DRM_FORMAT_NV21, |
| 54 | .bpp = 12, | 66 | .bpp = 12, |
| 55 | .yuv = true, | ||
| 56 | .lddfr = LDDFR_CC | LDDFR_YF_420, | 67 | .lddfr = LDDFR_CC | LDDFR_YF_420, |
| 68 | .ldddsr = LDDDSR_LS | LDDDSR_WS, | ||
| 69 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 70 | LDBBSIFR_CHRR_420, | ||
| 57 | }, { | 71 | }, { |
| 58 | .fourcc = DRM_FORMAT_NV16, | 72 | .fourcc = DRM_FORMAT_NV16, |
| 59 | .bpp = 16, | 73 | .bpp = 16, |
| 60 | .yuv = true, | ||
| 61 | .lddfr = LDDFR_CC | LDDFR_YF_422, | 74 | .lddfr = LDDFR_CC | LDDFR_YF_422, |
| 75 | .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, | ||
| 76 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 77 | LDBBSIFR_SWPB | LDBBSIFR_CHRR_422, | ||
| 62 | }, { | 78 | }, { |
| 63 | .fourcc = DRM_FORMAT_NV61, | 79 | .fourcc = DRM_FORMAT_NV61, |
| 64 | .bpp = 16, | 80 | .bpp = 16, |
| 65 | .yuv = true, | ||
| 66 | .lddfr = LDDFR_CC | LDDFR_YF_422, | 81 | .lddfr = LDDFR_CC | LDDFR_YF_422, |
| 82 | .ldddsr = LDDDSR_LS | LDDDSR_WS, | ||
| 83 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 84 | LDBBSIFR_CHRR_422, | ||
| 67 | }, { | 85 | }, { |
| 68 | .fourcc = DRM_FORMAT_NV24, | 86 | .fourcc = DRM_FORMAT_NV24, |
| 69 | .bpp = 24, | 87 | .bpp = 24, |
| 70 | .yuv = true, | ||
| 71 | .lddfr = LDDFR_CC | LDDFR_YF_444, | 88 | .lddfr = LDDFR_CC | LDDFR_YF_444, |
| 89 | .ldddsr = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS, | ||
| 90 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 91 | LDBBSIFR_SWPB | LDBBSIFR_CHRR_444, | ||
| 72 | }, { | 92 | }, { |
| 73 | .fourcc = DRM_FORMAT_NV42, | 93 | .fourcc = DRM_FORMAT_NV42, |
| 74 | .bpp = 24, | 94 | .bpp = 24, |
| 75 | .yuv = true, | ||
| 76 | .lddfr = LDDFR_CC | LDDFR_YF_444, | 95 | .lddfr = LDDFR_CC | LDDFR_YF_444, |
| 96 | .ldddsr = LDDDSR_LS | LDDDSR_WS, | ||
| 97 | .ldbbsifr = LDBBSIFR_AL_1 | LDBBSIFR_SWPL | LDBBSIFR_SWPW | | ||
| 98 | LDBBSIFR_CHRR_444, | ||
| 77 | }, | 99 | }, |
| 78 | }; | 100 | }; |
| 79 | 101 | ||
| @@ -112,7 +134,7 @@ shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv, · 32 unmodified lines | |||
| 112 | return ERR_PTR(-EINVAL); | 134 | return ERR_PTR(-EINVAL); |
| 113 | } | 135 | } |
| 114 | 136 | ||
| 115 | if (format->yuv) { | 137 | if (shmob_drm_format_is_yuv(format)) { |
| 116 | unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1; | 138 | unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1; |
| 117 | 139 | ||
| 118 | if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) { | 140 | if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) { |
| @@ -127,29 +149,40 @@ shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv, · 8 unmodified lines | |||
| 127 | 149 | ||
| 128 | static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = { | 150 | static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = { |
| 129 | .fb_create = shmob_drm_fb_create, | 151 | .fb_create = shmob_drm_fb_create, |
| 152 | .atomic_check = drm_atomic_helper_check, | ||
| 153 | .atomic_commit = drm_atomic_helper_commit, | ||
| 130 | }; | 154 | }; |
| 131 | 155 | ||
| 132 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev) | 156 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev) |
| 133 | { | 157 | { |
| 158 | struct drm_device *dev = &sdev->ddev; | ||
| 134 | int ret; | 159 | int ret; |
| 135 | 160 | ||
| 136 | ret = drmm_mode_config_init(sdev->ddev); | 161 | ret = drmm_mode_config_init(dev); |
| 137 | if (ret) | 162 | if (ret) |
| 138 | return ret; | 163 | return ret; |
| 139 | 164 | ||
| 140 | shmob_drm_crtc_create(sdev); | 165 | ret = shmob_drm_crtc_create(sdev); |
| 141 | shmob_drm_encoder_create(sdev); | 166 | if (ret < 0) |
| 142 | shmob_drm_connector_create(sdev, &sdev->encoder.encoder); | 167 | return ret; |
| 168 | |||
| 169 | ret = shmob_drm_encoder_create(sdev); | ||
| 170 | if (ret < 0) | ||
| 171 | return ret; | ||
| 172 | |||
| 173 | ret = shmob_drm_connector_create(sdev, &sdev->encoder); | ||
| 174 | if (ret < 0) | ||
| 175 | return ret; | ||
| 143 | 176 | ||
| 144 | drm_kms_helper_poll_init(sdev->ddev); | 177 | drm_mode_config_reset(dev); |
| 145 | 178 | ||
| 146 | sdev->ddev->mode_config.min_width = 0; | 179 | drm_kms_helper_poll_init(dev); |
| 147 | sdev->ddev->mode_config.min_height = 0; | ||
| 148 | sdev->ddev->mode_config.max_width = 4095; | ||
| 149 | sdev->ddev->mode_config.max_height = 4095; | ||
| 150 | sdev->ddev->mode_config.funcs = &shmob_drm_mode_config_funcs; | ||
| 151 | 180 | ||
| 152 | drm_helper_disable_unused_functions(sdev->ddev); | 181 | sdev->ddev.mode_config.min_width = 0; |
| 182 | sdev->ddev.mode_config.min_height = 0; | ||
| 183 | sdev->ddev.mode_config.max_width = 4095; | ||
| 184 | sdev->ddev.mode_config.max_height = 4095; | ||
| 185 | sdev->ddev.mode_config.funcs = &shmob_drm_mode_config_funcs; | ||
| 153 | 186 | ||
| 154 | return 0; | 187 | return 0; |
| 155 | } | 188 | } |
| @@ -17,11 +17,14 @@ struct shmob_drm_device; · 16 unmodified lines | |||
| 17 | 17 | ||
| 18 | struct shmob_drm_format_info { | 18 | struct shmob_drm_format_info { |
| 19 | u32 fourcc; | 19 | u32 fourcc; |
| 20 | unsigned int bpp; | 20 | u32 lddfr; /* LCD Data Format Register */ |
| 21 | bool yuv; | 21 | u16 ldbbsifr; /* CHn Source Image Format Register low bits */ |
| 22 | u32 lddfr; | 22 | u8 ldddsr; /* LCDC Input Image Data Swap Register low bits */ |
| 23 | u8 bpp; | ||
| 23 | }; | 24 | }; |
| 24 | 25 | ||
| 26 | #define shmob_drm_format_is_yuv(format) ((format)->lddfr & LDDFR_CC) | ||
| 27 | |||
| 25 | const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc); | 28 | const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc); |
| 26 | 29 | ||
| 27 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev); | 30 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev); |
| @@ -7,11 +7,14 @@ · 6 unmodified lines | |||
| 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <drm/drm_atomic.h> | ||
| 11 | #include <drm/drm_atomic_helper.h> | ||
| 10 | #include <drm/drm_crtc.h> | 12 | #include <drm/drm_crtc.h> |
| 11 | #include <drm/drm_fb_dma_helper.h> | 13 | #include <drm/drm_fb_dma_helper.h> |
| 12 | #include <drm/drm_fourcc.h> | 14 | #include <drm/drm_fourcc.h> |
| 13 | #include <drm/drm_framebuffer.h> | 15 | #include <drm/drm_framebuffer.h> |
| 14 | #include <drm/drm_gem_dma_helper.h> | 16 | #include <drm/drm_gem_dma_helper.h> |
| 17 | #include <drm/drm_plane_helper.h> | ||
| 15 | 18 | ||
| 16 | #include "shmob_drm_drv.h" | 19 | #include "shmob_drm_drv.h" |
| 17 | #include "shmob_drm_kms.h" | 20 | #include "shmob_drm_kms.h" |
| @@ -19,102 +22,84 @@ · 1 unmodified line | |||
| 19 | #include "shmob_drm_regs.h" | 22 | #include "shmob_drm_regs.h" |
| 20 | 23 | ||
| 21 | struct shmob_drm_plane { | 24 | struct shmob_drm_plane { |
| 22 | struct drm_plane plane; | 25 | struct drm_plane base; |
| 23 | unsigned int index; | 26 | unsigned int index; |
| 24 | unsigned int alpha; | 27 | }; |
| 28 | |||
| 29 | struct shmob_drm_plane_state { | ||
| 30 | struct drm_plane_state base; | ||
| 25 | 31 | ||
| 26 | const struct shmob_drm_format_info *format; | 32 | const struct shmob_drm_format_info *format; |
| 27 | unsigned long dma[2]; | 33 | u32 dma[2]; |
| 28 | |||
| 29 | unsigned int src_x; | ||
| 30 | unsigned int src_y; | ||
| 31 | unsigned int crtc_x; | ||
| 32 | unsigned int crtc_y; | ||
| 33 | unsigned int crtc_w; | ||
| 34 | unsigned int crtc_h; | ||
| 35 | }; | 34 | }; |
| 36 | 35 | ||
| 37 | #define to_shmob_plane(p) container_of(p, struct shmob_drm_plane, plane) | 36 | static inline struct shmob_drm_plane *to_shmob_plane(struct drm_plane *plane) |
| 37 | { | ||
| 38 | return container_of(plane, struct shmob_drm_plane, base); | ||
| 39 | } | ||
| 40 | |||
| 41 | static inline struct shmob_drm_plane_state *to_shmob_plane_state(struct drm_plane_state *state) | ||
| 42 | { | ||
| 43 | return container_of(state, struct shmob_drm_plane_state, base); | ||
| 44 | } | ||
| 38 | 45 | ||
| 39 | static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane, | 46 | static void shmob_drm_plane_compute_base(struct shmob_drm_plane_state *sstate) |
| 40 | struct drm_framebuffer *fb, | ||
| 41 | int x, int y) | ||
| 42 | { | 47 | { |
| 48 | struct drm_framebuffer *fb = sstate->base.fb; | ||
| 49 | unsigned int x = sstate->base.src_x >> 16; | ||
| 50 | unsigned int y = sstate->base.src_y >> 16; | ||
| 43 | struct drm_gem_dma_object *gem; | 51 | struct drm_gem_dma_object *gem; |
| 44 | unsigned int bpp; | 52 | unsigned int bpp; |
| 45 | 53 | ||
| 46 | bpp = splane->format->yuv ? 8 : splane->format->bpp; | 54 | bpp = shmob_drm_format_is_yuv(sstate->format) ? 8 : sstate->format->bpp; |
| 47 | gem = drm_fb_dma_get_gem_obj(fb, 0); | 55 | gem = drm_fb_dma_get_gem_obj(fb, 0); |
| 48 | splane->dma[0] = gem->dma_addr + fb->offsets[0] | 56 | sstate->dma[0] = gem->dma_addr + fb->offsets[0] |
| 49 | + y * fb->pitches[0] + x * bpp / 8; | 57 | + y * fb->pitches[0] + x * bpp / 8; |
| 50 | 58 | ||
| 51 | if (splane->format->yuv) { | 59 | if (shmob_drm_format_is_yuv(sstate->format)) { |
| 52 | bpp = splane->format->bpp - 8; | 60 | bpp = sstate->format->bpp - 8; |
| 53 | gem = drm_fb_dma_get_gem_obj(fb, 1); | 61 | gem = drm_fb_dma_get_gem_obj(fb, 1); |
| 54 | splane->dma[1] = gem->dma_addr + fb->offsets[1] | 62 | sstate->dma[1] = gem->dma_addr + fb->offsets[1] |
| 55 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] | 63 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] |
| 56 | + x * (bpp == 16 ? 2 : 1); | 64 | + x * (bpp == 16 ? 2 : 1); |
| 57 | } | 65 | } |
| 58 | } | 66 | } |
| 59 | 67 | ||
| 60 | static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, | 68 | static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane, |
| 61 | struct drm_framebuffer *fb) | 69 | struct drm_plane_state *state) |
| 62 | { | 70 | { |
| 63 | struct shmob_drm_device *sdev = splane->plane.dev->dev_private; | 71 | struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state); |
| 72 | struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev); | ||
| 73 | struct drm_framebuffer *fb = state->fb; | ||
| 74 | |||
| 75 | /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */ | ||
| 76 | lcdc_write(sdev, LDDFR, sstate->format->lddfr | LDDFR_CF1); | ||
| 77 | lcdc_write(sdev, LDMLSR, fb->pitches[0]); | ||
| 78 | |||
| 79 | /* Word and long word swap. */ | ||
| 80 | lcdc_write(sdev, LDDDSR, sstate->format->ldddsr); | ||
| 81 | |||
| 82 | lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]); | ||
| 83 | if (shmob_drm_format_is_yuv(sstate->format)) | ||
| 84 | lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]); | ||
| 85 | |||
| 86 | lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS); | ||
| 87 | } | ||
| 88 | |||
| 89 | static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane, | ||
| 90 | struct drm_plane_state *state) | ||
| 91 | { | ||
| 92 | struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state); | ||
| 93 | struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev); | ||
| 94 | struct drm_framebuffer *fb = state->fb; | ||
| 64 | u32 format; | 95 | u32 format; |
| 65 | 96 | ||
| 66 | /* TODO: Support ROP3 mode */ | 97 | /* TODO: Support ROP3 mode */ |
| 67 | format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT); | 98 | format = LDBBSIFR_EN | ((state->alpha >> 8) << LDBBSIFR_LAY_SHIFT) | |
| 68 | 99 | sstate->format->ldbbsifr; | |
| 69 | switch (splane->format->fourcc) { | ||
| 70 | case DRM_FORMAT_RGB565: | ||
| 71 | case DRM_FORMAT_NV21: | ||
| 72 | case DRM_FORMAT_NV61: | ||
| 73 | case DRM_FORMAT_NV42: | ||
| 74 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; | ||
| 75 | break; | ||
| 76 | case DRM_FORMAT_RGB888: | ||
| 77 | case DRM_FORMAT_NV12: | ||
| 78 | case DRM_FORMAT_NV16: | ||
| 79 | case DRM_FORMAT_NV24: | ||
| 80 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; | ||
| 81 | break; | ||
| 82 | case DRM_FORMAT_ARGB8888: | ||
| 83 | case DRM_FORMAT_XRGB8888: | ||
| 84 | default: | ||
| 85 | format |= LDBBSIFR_SWPL; | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | |||
| 89 | switch (splane->format->fourcc) { | ||
| 90 | case DRM_FORMAT_RGB565: | ||
| 91 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; | ||
| 92 | break; | ||
| 93 | case DRM_FORMAT_RGB888: | ||
| 94 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; | ||
| 95 | break; | ||
| 96 | case DRM_FORMAT_ARGB8888: | ||
| 97 | format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; | ||
| 98 | break; | ||
| 99 | case DRM_FORMAT_XRGB8888: | ||
| 100 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDDFR_PKF_ARGB32; | ||
| 101 | break; | ||
| 102 | case DRM_FORMAT_NV12: | ||
| 103 | case DRM_FORMAT_NV21: | ||
| 104 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; | ||
| 105 | break; | ||
| 106 | case DRM_FORMAT_NV16: | ||
| 107 | case DRM_FORMAT_NV61: | ||
| 108 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; | ||
| 109 | break; | ||
| 110 | case DRM_FORMAT_NV24: | ||
| 111 | case DRM_FORMAT_NV42: | ||
| 112 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; | ||
| 113 | break; | ||
| 114 | } | ||
| 115 | 100 | ||
| 116 | #define plane_reg_dump(sdev, splane, reg) \ | 101 | #define plane_reg_dump(sdev, splane, reg) \ |
| 117 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \ | 102 | dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \ |
| 118 | splane->index, #reg, \ | 103 | splane->index, #reg, \ |
| 119 | lcdc_read(sdev, reg(splane->index)), \ | 104 | lcdc_read(sdev, reg(splane->index)), \ |
| 120 | lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET)) | 105 | lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET)) |
| @@ -127,29 +112,27 @@ static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, · 6 unmodified lines | |||
| 127 | plane_reg_dump(sdev, splane, LDBnBSACR); | 112 | plane_reg_dump(sdev, splane, LDBnBSACR); |
| 128 | 113 | ||
| 129 | lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); | 114 | lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); |
| 130 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | 115 | dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, |
| 131 | "LDBCR", lcdc_read(sdev, LDBCR)); | 116 | "LDBCR", lcdc_read(sdev, LDBCR)); |
| 132 | 117 | ||
| 133 | lcdc_write(sdev, LDBnBSIFR(splane->index), format); | 118 | lcdc_write(sdev, LDBnBSIFR(splane->index), format); |
| 134 | 119 | ||
| 135 | lcdc_write(sdev, LDBnBSSZR(splane->index), | 120 | lcdc_write(sdev, LDBnBSSZR(splane->index), |
| 136 | (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) | | 121 | (state->crtc_h << LDBBSSZR_BVSS_SHIFT) | |
| 137 | (splane->crtc_w << LDBBSSZR_BHSS_SHIFT)); | 122 | (state->crtc_w << LDBBSSZR_BHSS_SHIFT)); |
| 138 | lcdc_write(sdev, LDBnBLOCR(splane->index), | 123 | lcdc_write(sdev, LDBnBLOCR(splane->index), |
| 139 | (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) | | 124 | (state->crtc_y << LDBBLOCR_CVLC_SHIFT) | |
| 140 | (splane->crtc_x << LDBBLOCR_CHLC_SHIFT)); | 125 | (state->crtc_x << LDBBLOCR_CHLC_SHIFT)); |
| 141 | lcdc_write(sdev, LDBnBSMWR(splane->index), | 126 | lcdc_write(sdev, LDBnBSMWR(splane->index), |
| 142 | fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); | 127 | fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); |
| 143 | 128 | ||
| 144 | shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y); | 129 | lcdc_write(sdev, LDBnBSAYR(splane->index), sstate->dma[0]); |
| 145 | 130 | if (shmob_drm_format_is_yuv(sstate->format)) | |
| 146 | lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]); | 131 | lcdc_write(sdev, LDBnBSACR(splane->index), sstate->dma[1]); |
| 147 | if (splane->format->yuv) | ||
| 148 | lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]); | ||
| 149 | 132 | ||
| 150 | lcdc_write(sdev, LDBCR, | 133 | lcdc_write(sdev, LDBCR, |
| 151 | LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); | 134 | LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); |
| 152 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | 135 | dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, |
| 153 | "LDBCR", lcdc_read(sdev, LDBCR)); | 136 | "LDBCR", lcdc_read(sdev, LDBCR)); |
| 154 | 137 | ||
| 155 | plane_reg_dump(sdev, splane, LDBnBSIFR); | 138 | plane_reg_dump(sdev, splane, LDBnBSIFR); |
| @@ -160,75 +143,143 @@ static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, · 4 unmodified lines | |||
| 160 | plane_reg_dump(sdev, splane, LDBnBSACR); | 143 | plane_reg_dump(sdev, splane, LDBnBSACR); |
| 161 | } | 144 | } |
| 162 | 145 | ||
| 163 | void shmob_drm_plane_setup(struct drm_plane *plane) | 146 | static int shmob_drm_plane_atomic_check(struct drm_plane *plane, |
| 147 | struct drm_atomic_state *state) | ||
| 164 | { | 148 | { |
| 165 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | 149 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
| 150 | struct shmob_drm_plane_state *sstate = to_shmob_plane_state(new_plane_state); | ||
| 151 | struct drm_crtc_state *crtc_state; | ||
| 152 | bool is_primary = plane->type == DRM_PLANE_TYPE_PRIMARY; | ||
| 153 | int ret; | ||
| 166 | 154 | ||
| 167 | if (plane->fb == NULL) | 155 | if (!new_plane_state->crtc) { |
| 168 | return; | 156 | /* |
| 157 | * The visible field is not reset by the DRM core but only | ||
| 158 | * updated by drm_atomic_helper_check_plane_state(), set it | ||
| 159 | * manually. | ||
| 160 | */ | ||
| 161 | new_plane_state->visible = false; | ||
| 162 | sstate->format = NULL; | ||
| 163 | return 0; | ||
| 164 | } | ||
| 169 | 165 | ||
| 170 | __shmob_drm_plane_setup(splane, plane->fb); | 166 | crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); |
| 171 | } | 167 | if (IS_ERR(crtc_state)) |
| 168 | return PTR_ERR(crtc_state); | ||
| 172 | 169 | ||
| 173 | static int | 170 | ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, |
| 174 | shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | 171 | DRM_PLANE_NO_SCALING, |
| 175 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | 172 | DRM_PLANE_NO_SCALING, |
| 176 | unsigned int crtc_w, unsigned int crtc_h, | 173 | !is_primary, true); |
| 177 | uint32_t src_x, uint32_t src_y, | 174 | if (ret < 0) |
| 178 | uint32_t src_w, uint32_t src_h, | 175 | return ret; |
| 179 | struct drm_modeset_acquire_ctx *ctx) | ||
| 180 | { | ||
| 181 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
| 182 | struct shmob_drm_device *sdev = plane->dev->dev_private; | ||
| 183 | const struct shmob_drm_format_info *format; | ||
| 184 | 176 | ||
| 185 | format = shmob_drm_format_info(fb->format->format); | 177 | if (!new_plane_state->visible) { |
| 186 | if (format == NULL) { | 178 | sstate->format = NULL; |
| 187 | dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n", | 179 | return 0; |
| 188 | fb->format->format); | ||
| 189 | return -EINVAL; | ||
| 190 | } | 180 | } |
| 191 | 181 | ||
| 192 | if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { | 182 | sstate->format = shmob_drm_format_info(new_plane_state->fb->format->format); |
| 193 | dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__); | 183 | if (!sstate->format) { |
| 184 | dev_dbg(plane->dev->dev, | ||
| 185 | "plane_atomic_check: unsupported format %p4cc\n", | ||
| 186 | &new_plane_state->fb->format->format); | ||
| 194 | return -EINVAL; | 187 | return -EINVAL; |
| 195 | } | 188 | } |
| 196 | 189 | ||
| 197 | splane->format = format; | 190 | shmob_drm_plane_compute_base(sstate); |
| 198 | 191 | ||
| 199 | splane->src_x = src_x >> 16; | ||
| 200 | splane->src_y = src_y >> 16; | ||
| 201 | splane->crtc_x = crtc_x; | ||
| 202 | splane->crtc_y = crtc_y; | ||
| 203 | splane->crtc_w = crtc_w; | ||
| 204 | splane->crtc_h = crtc_h; | ||
| 205 | |||
| 206 | __shmob_drm_plane_setup(splane, fb); | ||
| 207 | return 0; | 192 | return 0; |
| 208 | } | 193 | } |
| 209 | 194 | ||
| 210 | static int shmob_drm_plane_disable(struct drm_plane *plane, | 195 | static void shmob_drm_plane_atomic_update(struct drm_plane *plane, |
| 211 | struct drm_modeset_acquire_ctx *ctx) | 196 | struct drm_atomic_state *state) |
| 212 | { | 197 | { |
| 198 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); | ||
| 213 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | 199 | struct shmob_drm_plane *splane = to_shmob_plane(plane); |
| 214 | struct shmob_drm_device *sdev = plane->dev->dev_private; | ||
| 215 | 200 | ||
| 216 | splane->format = NULL; | 201 | if (!new_plane_state->visible) |
| 202 | return; | ||
| 217 | 203 | ||
| 204 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) | ||
| 205 | shmob_drm_primary_plane_setup(splane, new_plane_state); | ||
| 206 | else | ||
| 207 | shmob_drm_overlay_plane_setup(splane, new_plane_state); | ||
| 208 | } | ||
| 209 | |||
| 210 | static void shmob_drm_plane_atomic_disable(struct drm_plane *plane, | ||
| 211 | struct drm_atomic_state *state) | ||
| 212 | { | ||
| 213 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); | ||
| 214 | struct shmob_drm_device *sdev = to_shmob_device(plane->dev); | ||
| 215 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | ||
| 216 | |||
| 217 | if (!old_state->crtc) | ||
| 218 | return; | ||
| 219 | |||
| 220 | if (plane->type != DRM_PLANE_TYPE_OVERLAY) | ||
| 221 | return; | ||
| 222 | |||
| 223 | lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); | ||
| 218 | lcdc_write(sdev, LDBnBSIFR(splane->index), 0); | 224 | lcdc_write(sdev, LDBnBSIFR(splane->index), 0); |
| 219 | return 0; | 225 | lcdc_write(sdev, LDBCR, |
| 226 | LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); | ||
| 220 | } | 227 | } |
| 221 | 228 | ||
| 222 | static void shmob_drm_plane_destroy(struct drm_plane *plane) | 229 | static struct drm_plane_state * |
| 230 | shmob_drm_plane_atomic_duplicate_state(struct drm_plane *plane) | ||
| 223 | { | 231 | { |
| 224 | drm_plane_force_disable(plane); | 232 | struct shmob_drm_plane_state *state; |
| 225 | drm_plane_cleanup(plane); | 233 | struct shmob_drm_plane_state *copy; |
| 234 | |||
| 235 | if (WARN_ON(!plane->state)) | ||
| 236 | return NULL; | ||
| 237 | |||
| 238 | state = to_shmob_plane_state(plane->state); | ||
| 239 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); | ||
| 240 | if (copy == NULL) | ||
| 241 | return NULL; | ||
| 242 | |||
| 243 | __drm_atomic_helper_plane_duplicate_state(plane, ©->base); | ||
| 244 | |||
| 245 | return ©->base; | ||
| 226 | } | 246 | } |
| 227 | 247 | ||
| 248 | static void shmob_drm_plane_atomic_destroy_state(struct drm_plane *plane, | ||
| 249 | struct drm_plane_state *state) | ||
| 250 | { | ||
| 251 | __drm_atomic_helper_plane_destroy_state(state); | ||
| 252 | kfree(to_shmob_plane_state(state)); | ||
| 253 | } | ||
| 254 | |||
| 255 | static void shmob_drm_plane_reset(struct drm_plane *plane) | ||
| 256 | { | ||
| 257 | struct shmob_drm_plane_state *state; | ||
| 258 | |||
| 259 | if (plane->state) { | ||
| 260 | shmob_drm_plane_atomic_destroy_state(plane, plane->state); | ||
| 261 | plane->state = NULL; | ||
| 262 | } | ||
| 263 | |||
| 264 | state = kzalloc(sizeof(*state), GFP_KERNEL); | ||
| 265 | if (state == NULL) | ||
| 266 | return; | ||
| 267 | |||
| 268 | __drm_atomic_helper_plane_reset(plane, &state->base); | ||
| 269 | } | ||
| 270 | |||
| 271 | static const struct drm_plane_helper_funcs shmob_drm_plane_helper_funcs = { | ||
| 272 | .atomic_check = shmob_drm_plane_atomic_check, | ||
| 273 | .atomic_update = shmob_drm_plane_atomic_update, | ||
| 274 | .atomic_disable = shmob_drm_plane_atomic_disable, | ||
| 275 | }; | ||
| 276 | |||
| 228 | static const struct drm_plane_funcs shmob_drm_plane_funcs = { | 277 | static const struct drm_plane_funcs shmob_drm_plane_funcs = { |
| 229 | .update_plane = shmob_drm_plane_update, | 278 | .update_plane = drm_atomic_helper_update_plane, |
| 230 | .disable_plane = shmob_drm_plane_disable, | 279 | .disable_plane = drm_atomic_helper_disable_plane, |
| 231 | .destroy = shmob_drm_plane_destroy, | 280 | .reset = shmob_drm_plane_reset, |
| 281 | .atomic_duplicate_state = shmob_drm_plane_atomic_duplicate_state, | ||
| 282 | .atomic_destroy_state = shmob_drm_plane_atomic_destroy_state, | ||
| 232 | }; | 283 | }; |
| 233 | 284 | ||
| 234 | static const uint32_t formats[] = { | 285 | static const uint32_t formats[] = { |
| @@ -244,22 +295,23 @@ static const uint32_t formats[] = { · 9 unmodified lines | |||
| 244 | DRM_FORMAT_NV42, | 295 | DRM_FORMAT_NV42, |
| 245 | }; | 296 | }; |
| 246 | 297 | ||
| 247 | int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index) | 298 | struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev, |
| 299 | enum drm_plane_type type, | ||
| 300 | unsigned int index) | ||
| 248 | { | 301 | { |
| 249 | struct shmob_drm_plane *splane; | 302 | struct shmob_drm_plane *splane; |
| 250 | int ret; | ||
| 251 | 303 | ||
| 252 | splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL); | 304 | splane = drmm_universal_plane_alloc(&sdev->ddev, |
| 253 | if (splane == NULL) | 305 | struct shmob_drm_plane, base, 1, |
| 254 | return -ENOMEM; | 306 | &shmob_drm_plane_funcs, formats, |
| 307 | ARRAY_SIZE(formats), NULL, type, | ||
| 308 | NULL); | ||
| 309 | if (IS_ERR(splane)) | ||
| 310 | return ERR_CAST(splane); | ||
| 255 | 311 | ||
| 256 | splane->index = index; | 312 | splane->index = index; |
| 257 | splane->alpha = 255; | ||
| 258 | 313 | ||
| 259 | ret = drm_universal_plane_init(sdev->ddev, &splane->plane, 1, | 314 | drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs); |
| 260 | &shmob_drm_plane_funcs, | ||
| 261 | formats, ARRAY_SIZE(formats), NULL, | ||
| 262 | DRM_PLANE_TYPE_OVERLAY, NULL); | ||
| 263 | 315 | ||
| 264 | return ret; | 316 | return &splane->base; |
| 265 | } | 317 | } |
| @@ -13,7 +13,8 @@ · 12 unmodified lines | |||
| 13 | struct drm_plane; | 13 | struct drm_plane; |
| 14 | struct shmob_drm_device; | 14 | struct shmob_drm_device; |
| 15 | 15 | ||
| 16 | int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index); | 16 | struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev, |
| 17 | void shmob_drm_plane_setup(struct drm_plane *plane); | 17 | enum drm_plane_type type, |
| 18 | unsigned int index); | ||
| 18 | 19 | ||
| 19 | #endif /* __SHMOB_DRM_PLANE_H__ */ | 20 | #endif /* __SHMOB_DRM_PLANE_H__ */ |
| @@ -10,7 +10,7 @@ · 9 unmodified lines | |||
| 10 | #ifndef __SHMOB_DRM_H__ | 10 | #ifndef __SHMOB_DRM_H__ |
| 11 | #define __SHMOB_DRM_H__ | 11 | #define __SHMOB_DRM_H__ |
| 12 | 12 | ||
| 13 | #include <drm/drm_mode.h> | 13 | #include <video/videomode.h> |
| 14 | 14 | ||
| 15 | enum shmob_drm_clk_source { | 15 | enum shmob_drm_clk_source { |
| 16 | SHMOB_DRM_CLK_BUS, | 16 | SHMOB_DRM_CLK_BUS, |
| @@ -18,72 +18,21 @@ enum shmob_drm_clk_source { · 1 unmodified line | |||
| 18 | SHMOB_DRM_CLK_EXTERNAL, | 18 | SHMOB_DRM_CLK_EXTERNAL, |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | enum shmob_drm_interface { | ||
| 22 | SHMOB_DRM_IFACE_RGB8, /* 24bpp, 8:8:8 */ | ||
| 23 | SHMOB_DRM_IFACE_RGB9, /* 18bpp, 9:9 */ | ||
| 24 | SHMOB_DRM_IFACE_RGB12A, /* 24bpp, 12:12 */ | ||
| 25 | SHMOB_DRM_IFACE_RGB12B, /* 12bpp */ | ||
| 26 | SHMOB_DRM_IFACE_RGB16, /* 16bpp */ | ||
| 27 | SHMOB_DRM_IFACE_RGB18, /* 18bpp */ | ||
| 28 | SHMOB_DRM_IFACE_RGB24, /* 24bpp */ | ||
| 29 | SHMOB_DRM_IFACE_YUV422, /* 16bpp */ | ||
| 30 | SHMOB_DRM_IFACE_SYS8A, /* 24bpp, 8:8:8 */ | ||
| 31 | SHMOB_DRM_IFACE_SYS8B, /* 18bpp, 8:8:2 */ | ||
| 32 | SHMOB_DRM_IFACE_SYS8C, /* 18bpp, 2:8:8 */ | ||
| 33 | SHMOB_DRM_IFACE_SYS8D, /* 16bpp, 8:8 */ | ||
| 34 | SHMOB_DRM_IFACE_SYS9, /* 18bpp, 9:9 */ | ||
| 35 | SHMOB_DRM_IFACE_SYS12, /* 24bpp, 12:12 */ | ||
| 36 | SHMOB_DRM_IFACE_SYS16A, /* 16bpp */ | ||
| 37 | SHMOB_DRM_IFACE_SYS16B, /* 18bpp, 16:2 */ | ||
| 38 | SHMOB_DRM_IFACE_SYS16C, /* 18bpp, 2:16 */ | ||
| 39 | SHMOB_DRM_IFACE_SYS18, /* 18bpp */ | ||
| 40 | SHMOB_DRM_IFACE_SYS24, /* 24bpp */ | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct shmob_drm_backlight_data { | ||
| 44 | const char *name; | ||
| 45 | int max_brightness; | ||
| 46 | int (*get_brightness)(void); | ||
| 47 | int (*set_brightness)(int brightness); | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct shmob_drm_panel_data { | 21 | struct shmob_drm_panel_data { |
| 51 | unsigned int width_mm; /* Panel width in mm */ | 22 | unsigned int width_mm; /* Panel width in mm */ |
| 52 | unsigned int height_mm; /* Panel height in mm */ | 23 | unsigned int height_mm; /* Panel height in mm */ |
| 53 | struct drm_mode_modeinfo mode; | 24 | struct videomode mode; |
| 54 | }; | 25 | }; |
| 55 | 26 | ||
| 56 | struct shmob_drm_sys_interface_data { | ||
| 57 | unsigned int read_latch:6; | ||
| 58 | unsigned int read_setup:8; | ||
| 59 | unsigned int read_cycle:8; | ||
| 60 | unsigned int read_strobe:8; | ||
| 61 | unsigned int write_setup:8; | ||
| 62 | unsigned int write_cycle:8; | ||
| 63 | unsigned int write_strobe:8; | ||
| 64 | unsigned int cs_setup:3; | ||
| 65 | unsigned int vsync_active_high:1; | ||
| 66 | unsigned int vsync_dir_input:1; | ||
| 67 | }; | ||
| 68 | |||
| 69 | #define SHMOB_DRM_IFACE_FL_DWPOL (1 << 0) /* Rising edge dot clock data latch */ | ||
| 70 | #define SHMOB_DRM_IFACE_FL_DIPOL (1 << 1) /* Active low display enable */ | ||
| 71 | #define SHMOB_DRM_IFACE_FL_DAPOL (1 << 2) /* Active low display data */ | ||
| 72 | #define SHMOB_DRM_IFACE_FL_HSCNT (1 << 3) /* Disable HSYNC during VBLANK */ | ||
| 73 | #define SHMOB_DRM_IFACE_FL_DWCNT (1 << 4) /* Disable dotclock during blanking */ | ||
| 74 | |||
| 75 | struct shmob_drm_interface_data { | 27 | struct shmob_drm_interface_data { |
| 76 | enum shmob_drm_interface interface; | 28 | unsigned int bus_fmt; /* MEDIA_BUS_FMT_* */ |
| 77 | struct shmob_drm_sys_interface_data sys; | ||
| 78 | unsigned int clk_div; | 29 | unsigned int clk_div; |
| 79 | unsigned int flags; | ||
| 80 | }; | 30 | }; |
| 81 | 31 | ||
| 82 | struct shmob_drm_platform_data { | 32 | struct shmob_drm_platform_data { |
| 83 | enum shmob_drm_clk_source clk_source; | 33 | enum shmob_drm_clk_source clk_source; |
| 84 | struct shmob_drm_interface_data iface; | 34 | struct shmob_drm_interface_data iface; |
| 85 | struct shmob_drm_panel_data panel; | 35 | struct shmob_drm_panel_data panel; |
| 86 | struct shmob_drm_backlight_data backlight; | ||
| 87 | }; | 36 | }; |
| 88 | 37 | ||
| 89 | #endif /* __SHMOB_DRM_H__ */ | 38 | #endif /* __SHMOB_DRM_H__ */ |
| @@ -34,7 +34,7 @@ · 33 unmodified lines | |||
| 34 | 34 | ||
| 35 | #define MEDIA_BUS_FMT_FIXED 0x0001 | 35 | #define MEDIA_BUS_FMT_FIXED 0x0001 |
| 36 | 36 | ||
| 37 | /* RGB - next is 0x1025 */ | 37 | /* RGB - next is 0x1026 */ |
| 38 | #define MEDIA_BUS_FMT_RGB444_1X12 0x1016 | 38 | #define MEDIA_BUS_FMT_RGB444_1X12 0x1016 |
| 39 | #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 | 39 | #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 |
| 40 | #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 | 40 | #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 |
| @@ -46,6 +46,7 @@ · 5 unmodified lines | |||
| 46 | #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 | 46 | #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 |
| 47 | #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 | 47 | #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 |
| 48 | #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 | 48 | #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 |
| 49 | #define MEDIA_BUS_FMT_RGB666_2X9_BE 0x1025 | ||
| 49 | #define MEDIA_BUS_FMT_BGR666_1X18 0x1023 | 50 | #define MEDIA_BUS_FMT_BGR666_1X18 0x1023 |
| 50 | #define MEDIA_BUS_FMT_RBG888_1X24 0x100e | 51 | #define MEDIA_BUS_FMT_RBG888_1X24 0x100e |
| 51 | #define MEDIA_BUS_FMT_RGB666_1X24_CPADHI 0x1015 | 52 | #define MEDIA_BUS_FMT_RGB666_1X24_CPADHI 0x1015 |