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 | 949 | - b\ :sub:`2` |
| 950 | 950 | - b\ :sub:`1` |
| 951 | 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 | 1024 | * .. _MEDIA-BUS-FMT-BGR666-1X18: |
| 953 | 1025 | |
| 954 | 1026 | - MEDIA_BUS_FMT_BGR666_1X18 |
| @@ -7053,7 +7053,7 @@ F: drivers/gpu/host1x/ · 7052 unmodified lines | ||
| 7053 | 7053 | F: include/linux/host1x.h |
| 7054 | 7054 | F: include/uapi/drm/tegra_drm.h |
| 7055 | 7055 | |
| 7056 | −DRM DRIVERS FOR RENESAS | |
| 7056 | +DRM DRIVERS FOR RENESAS R-CAR | |
| 7057 | 7057 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| 7058 | 7058 | M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> |
| 7059 | 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 | 7064 | F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.yaml |
| 7065 | 7065 | F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml |
| 7066 | 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 | 7077 | F: include/linux/platform_data/shmob_drm.h |
| 7069 | 7078 | |
| 7070 | 7079 | DRM DRIVERS FOR ROCKCHIP |
| @@ -1,11 +1,12 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | config DRM_SHMOBILE |
| 3 | 3 | tristate "DRM Support for SH Mobile" |
| 4 | − depends on DRM | |
| 4 | + depends on DRM && PM | |
| 5 | 5 | depends on ARCH_RENESAS || ARCH_SHMOBILE || COMPILE_TEST |
| 6 | 6 | select BACKLIGHT_CLASS_DEVICE |
| 7 | 7 | select DRM_KMS_HELPER |
| 8 | 8 | select DRM_GEM_DMA_HELPER |
| 9 | + select VIDEOMODE_HELPERS | |
| 9 | 10 | help |
| 10 | 11 | Choose this option if you have an SH Mobile chipset. |
| 11 | 12 | If M is selected the module will be called shmob-drm. |
| @@ -1,6 +1,5 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | −shmob-drm-y := shmob_drm_backlight.o \ | |
| 3 | − shmob_drm_crtc.o \ | |
| 2 | +shmob-drm-y := shmob_drm_crtc.o \ | |
| 4 | 3 | shmob_drm_drv.o \ |
| 5 | 4 | shmob_drm_kms.o \ |
| 6 | 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 | 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | −#include <linux/backlight.h> | |
| 11 | 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 | 22 | #include <drm/drm_crtc.h> |
| 14 | 23 | #include <drm/drm_crtc_helper.h> |
| 15 | 24 | #include <drm/drm_fb_dma_helper.h> |
| @@ -18,85 +27,123 @@ · 2 unmodified lines | ||
| 18 | 27 | #include <drm/drm_gem_dma_helper.h> |
| 19 | 28 | #include <drm/drm_modeset_helper.h> |
| 20 | 29 | #include <drm/drm_modeset_helper_vtables.h> |
| 21 | −#include <drm/drm_plane_helper.h> | |
| 30 | +#include <drm/drm_panel.h> | |
| 22 | 31 | #include <drm/drm_probe_helper.h> |
| 23 | 32 | #include <drm/drm_simple_kms_helper.h> |
| 24 | 33 | #include <drm/drm_vblank.h> |
| 25 | 34 | |
| 26 | −#include "shmob_drm_backlight.h" | |
| 35 | +#include <video/videomode.h> | |
| 36 | + | |
| 27 | 37 | #include "shmob_drm_crtc.h" |
| 28 | 38 | #include "shmob_drm_drv.h" |
| 29 | 39 | #include "shmob_drm_kms.h" |
| 30 | 40 | #include "shmob_drm_plane.h" |
| 31 | 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) { | |
| 46 | − ret = clk_prepare_enable(sdev->clock); | |
| 47 | − if (ret < 0) | |
| 48 | − return ret; | |
| 53 | + spin_lock_irqsave(&dev->event_lock, flags); | |
| 54 | + event = scrtc->event; | |
| 55 | + scrtc->event = NULL; | |
| 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) | |
| 57 | − clk_disable_unprepare(sdev->clock); | |
| 79 | + struct drm_crtc *crtc = &scrtc->base; | |
| 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 | 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 | 110 | static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc) |
| 65 | 111 | { |
| 66 | − struct drm_crtc *crtc = &scrtc->crtc; | |
| 67 | − struct shmob_drm_device *sdev = crtc->dev->dev_private; | |
| 68 | − const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; | |
| 112 | + struct drm_crtc *crtc = &scrtc->base; | |
| 113 | + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); | |
| 114 | + const struct drm_display_info *info = &sdev->connector->display_info; | |
| 69 | 115 | const struct drm_display_mode *mode = &crtc->mode; |
| 116 | + unsigned int i; | |
| 70 | 117 | u32 value; |
| 71 | 118 | |
| 72 | − value = sdev->ldmt1r | |
| 73 | − | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL) | |
| 74 | − | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL) | |
| 75 | − | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0) | |
| 76 | − | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0) | |
| 77 | − | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0) | |
| 78 | − | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0) | |
| 79 | − | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0); | |
| 80 | − lcdc_write(sdev, LDMT1R, value); | |
| 81 | − | |
| 82 | − if (idata->interface >= SHMOB_DRM_IFACE_SYS8A && | |
| 83 | − idata->interface <= SHMOB_DRM_IFACE_SYS24) { | |
| 84 | − /* Setup SYS bus. */ | |
| 85 | − value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT) | |
| 86 | − | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0) | |
| 87 | − | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0) | |
| 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); | |
| 119 | + if (!info->num_bus_formats || !info->bus_formats) { | |
| 120 | + dev_warn(sdev->dev, "No bus format reported, using RGB888\n"); | |
| 121 | + value = LDMT1R_MIFTYP_RGB24; | |
| 122 | + } else { | |
| 123 | + for (i = 0; i < ARRAY_SIZE(shmob_drm_bus_fmts); i++) { | |
| 124 | + if (shmob_drm_bus_fmts[i].fmt == info->bus_formats[0]) | |
| 125 | + break; | |
| 126 | + } | |
| 127 | + if (i < ARRAY_SIZE(shmob_drm_bus_fmts)) { | |
| 128 | + value = shmob_drm_bus_fmts[i].ldmt1r; | |
| 129 | + } else { | |
| 130 | + dev_warn(sdev->dev, | |
| 131 | + "unsupported bus format 0x%x, using RGB888\n", | |
| 132 | + info->bus_formats[0]); | |
| 133 | + value = LDMT1R_MIFTYP_RGB24; | |
| 134 | + } | |
| 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 | 147 | value = ((mode->hdisplay / 8) << 16) /* HDCN */ |
| 101 | 148 | | (mode->htotal / 8); /* HTCN */ |
| 102 | 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 | 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 | 172 | u32 value; |
| 126 | 173 | |
| 127 | 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 | −/* | |
| 149 | − * shmob_drm_crtc_start - Configure and start the LCDC | |
| 150 | − * @scrtc: the SH Mobile CRTC | |
| 151 | − * | |
| 152 | − * Configure and start the LCDC device. External devices (clocks, MERAM, panels, | |
| 153 | − * ...) are not touched by this function. | |
| 154 | − */ | |
| 155 | −static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) | |
| 195 | +static inline struct shmob_drm_crtc *to_shmob_crtc(struct drm_crtc *crtc) | |
| 196 | +{ | |
| 197 | + return container_of(crtc, struct shmob_drm_crtc, base); | |
| 198 | +} | |
| 199 | + | |
| 200 | +static void shmob_drm_crtc_atomic_enable(struct drm_crtc *crtc, | |
| 201 | + struct drm_atomic_state *state) | |
| 156 | 202 | { |
| 157 | − struct drm_crtc *crtc = &scrtc->crtc; | |
| 158 | − struct shmob_drm_device *sdev = crtc->dev->dev_private; | |
| 159 | − const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; | |
| 160 | − const struct shmob_drm_format_info *format; | |
| 161 | − struct drm_device *dev = sdev->ddev; | |
| 162 | − struct drm_plane *plane; | |
| 203 | + struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | |
| 204 | + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); | |
| 205 | + unsigned int clk_div = sdev->config.clk_div; | |
| 206 | + struct device *dev = sdev->dev; | |
| 163 | 207 | u32 value; |
| 164 | 208 | int ret; |
| 165 | 209 | |
| 166 | − if (scrtc->started) | |
| 167 | − return; | |
| 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) | |
| 210 | + ret = pm_runtime_resume_and_get(dev); | |
| 211 | + if (ret) | |
| 176 | 212 | return; |
| 177 | 213 | |
| 178 | 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 | 224 | lcdc_write(sdev, LDPMR, 0); |
| 189 | 225 | |
| 190 | 226 | value = sdev->lddckr; |
| 191 | − if (idata->clk_div) { | |
| 227 | + if (clk_div) { | |
| 192 | 228 | /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider |
| 193 | 229 | * denominator. |
| 194 | 230 | */ |
| 195 | 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 | 235 | value |= LDDCKR_MOSEL; |
| 200 | 236 | else |
| 201 | − value |= idata->clk_div; | |
| 237 | + value |= clk_div; | |
| 202 | 238 | } |
| 203 | 239 | |
| 204 | 240 | lcdc_write(sdev, LDDCKR, value); |
| 205 | 241 | lcdc_write(sdev, LDDCKSTPR, 0); |
| 206 | 242 | lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0); |
| 207 | 243 | |
| 208 | − /* TODO: Setup SYS panel */ | |
| 209 | − | |
| 210 | 244 | /* Setup geometry, format, frame buffer memory and operation mode. */ |
| 211 | 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 | 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 | 249 | /* Enable the display output. */ |
| 250 | 250 | lcdc_write(sdev, LDCNT1R, LDCNT1R_DE); |
| 251 | 251 | |
| 252 | 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; | |
| 260 | − struct shmob_drm_device *sdev = crtc->dev->dev_private; | |
| 261 | + struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc); | |
| 262 | + struct shmob_drm_device *sdev = to_shmob_device(crtc->dev); | |
| 261 | 263 | |
| 262 | − if (!scrtc->started) | |
| 263 | − return; | |
| 264 | + /* | |
| 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 | 272 | /* Stop the LCDC. */ |
| 266 | 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 | 275 | /* Disable the display output. */ |
| 269 | 276 | lcdc_write(sdev, LDCNT1R, 0); |
| 270 | 277 | |
| 271 | − /* Stop clocks. */ | |
| 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); | |
| 278 | + pm_runtime_put(sdev->dev); | |
| 288 | 279 | } |
| 289 | 280 | |
| 290 | −static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc, | |
| 291 | − int x, int y) | |
| 281 | +static void shmob_drm_crtc_atomic_flush(struct drm_crtc *crtc, | |
| 282 | + struct drm_atomic_state *state) | |
| 292 | 283 | { |
| 293 | − struct drm_crtc *crtc = &scrtc->crtc; | |
| 294 | − struct drm_framebuffer *fb = crtc->primary->fb; | |
| 295 | − struct drm_gem_dma_object *gem; | |
| 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 | −} | |
| 284 | + struct drm_pending_vblank_event *event; | |
| 285 | + struct drm_device *dev = crtc->dev; | |
| 286 | + unsigned long flags; | |
| 347 | 287 | |
| 348 | −static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc, | |
| 349 | − struct drm_display_mode *mode, | |
| 350 | − struct drm_display_mode *adjusted_mode, | |
| 351 | − int x, int y, | |
| 352 | − struct drm_framebuffer *old_fb) | |
| 353 | −{ | |
| 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; | |
| 288 | + if (crtc->state->event) { | |
| 289 | + spin_lock_irqsave(&dev->event_lock, flags); | |
| 290 | + event = crtc->state->event; | |
| 291 | + crtc->state->event = NULL; | |
| 292 | + drm_crtc_send_vblank_event(crtc, event); | |
| 293 | + spin_unlock_irqrestore(&dev->event_lock, flags); | |
| 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 | 297 | static const struct drm_crtc_helper_funcs crtc_helper_funcs = { |
| 387 | − .dpms = shmob_drm_crtc_dpms, | |
| 388 | − .prepare = shmob_drm_crtc_mode_prepare, | |
| 389 | − .commit = shmob_drm_crtc_mode_commit, | |
| 390 | − .mode_set = shmob_drm_crtc_mode_set, | |
| 391 | − .mode_set_base = shmob_drm_crtc_mode_set_base, | |
| 298 | + .atomic_flush = shmob_drm_crtc_atomic_flush, | |
| 299 | + .atomic_enable = shmob_drm_crtc_atomic_enable, | |
| 300 | + .atomic_disable = shmob_drm_crtc_atomic_disable, | |
| 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 | 303 | static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, |
| 411 | 304 | struct drm_framebuffer *fb, |
| 412 | 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 | 307 | struct drm_modeset_acquire_ctx *ctx) |
| 415 | 308 | { |
| 416 | 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 | 311 | unsigned long flags; |
| 419 | 312 | |
| 420 | 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 | 318 | spin_unlock_irqrestore(&dev->event_lock, flags); |
| 426 | 319 | |
| 427 | − crtc->primary->fb = fb; | |
| 428 | − shmob_drm_crtc_update_base(scrtc); | |
| 320 | + drm_atomic_set_fb_for_plane(crtc->primary->state, fb); | |
| 429 | 321 | |
| 430 | 322 | if (event) { |
| 431 | 323 | event->pipe = 0; |
| 432 | − drm_crtc_vblank_get(&scrtc->crtc); | |
| 324 | + drm_crtc_vblank_get(&scrtc->base); | |
| 433 | 325 | spin_lock_irqsave(&dev->event_lock, flags); |
| 434 | 326 | scrtc->event = event; |
| 435 | 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 | 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 | 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 | 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 | 363 | shmob_drm_crtc_enable_vblank(sdev, false); |
| 472 | 364 | } |
| 473 | 365 | |
| 474 | 366 | static const struct drm_crtc_funcs crtc_funcs = { |
| 367 | + .reset = drm_atomic_helper_crtc_reset, | |
| 475 | 368 | .destroy = drm_crtc_cleanup, |
| 476 | − .set_config = drm_crtc_helper_set_config, | |
| 369 | + .set_config = drm_atomic_helper_set_config, | |
| 477 | 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 | 373 | .enable_vblank = shmob_drm_enable_vblank, |
| 479 | 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 | 377 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev) |
| 494 | 378 | { |
| 495 | − struct drm_crtc *crtc = &sdev->crtc.crtc; | |
| 496 | − struct drm_plane *primary; | |
| 379 | + struct drm_crtc *crtc = &sdev->crtc.base; | |
| 380 | + struct drm_plane *primary, *plane; | |
| 381 | + unsigned int i; | |
| 497 | 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, | |
| 502 | − 0, &primary_plane_funcs, | |
| 503 | − modeset_formats, | |
| 504 | − ARRAY_SIZE(modeset_formats), | |
| 505 | − NULL, DRM_PLANE_TYPE_PRIMARY, | |
| 506 | − NULL); | |
| 386 | + primary = shmob_drm_plane_create(sdev, DRM_PLANE_TYPE_PRIMARY, 0); | |
| 507 | 387 | if (IS_ERR(primary)) |
| 508 | 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 | 397 | &crtc_funcs, NULL); |
| 512 | − if (ret < 0) { | |
| 513 | − drm_plane_cleanup(primary); | |
| 514 | − kfree(primary); | |
| 398 | + if (ret < 0) | |
| 515 | 399 | return ret; |
| 516 | − } | |
| 517 | 400 | |
| 518 | 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 | 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 | 413 | static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder, |
| 545 | 414 | const struct drm_display_mode *mode, |
| 546 | 415 | struct drm_display_mode *adjusted_mode) |
| 547 | 416 | { |
| 548 | 417 | struct drm_device *dev = encoder->dev; |
| 549 | − struct shmob_drm_device *sdev = dev->dev_private; | |
| 550 | − struct drm_connector *connector = &sdev->connector.connector; | |
| 418 | + struct shmob_drm_device *sdev = to_shmob_device(dev); | |
| 419 | + struct drm_connector *connector = sdev->connector; | |
| 551 | 420 | const struct drm_display_mode *panel_mode; |
| 552 | 421 | |
| 553 | 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 | 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 | 435 | static const struct drm_encoder_helper_funcs encoder_helper_funcs = { |
| 584 | − .dpms = shmob_drm_encoder_dpms, | |
| 585 | 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 | 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 | 447 | int ret; |
| 595 | 448 | |
| 596 | − sdev->encoder.dpms = DRM_MODE_DPMS_OFF; | |
| 597 | − | |
| 598 | 449 | encoder->possible_crtcs = 1; |
| 599 | 450 | |
| 600 | − ret = drm_simple_encoder_init(sdev->ddev, encoder, | |
| 601 | − DRM_MODE_ENCODER_LVDS); | |
| 451 | + ret = drm_simple_encoder_init(&sdev->ddev, encoder, | |
| 452 | + DRM_MODE_ENCODER_DPI); | |
| 602 | 453 | if (ret < 0) |
| 603 | 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 | 475 | return 0; |
| 608 | 476 | } |
| 609 | 477 | |
| 610 | 478 | /* ----------------------------------------------------------------------------- |
| 611 | − * Connector | |
| 479 | + * Legacy Connector | |
| 612 | 480 | */ |
| 613 | 481 | |
| 614 | −#define to_shmob_connector(c) \ | |
| 615 | − container_of(c, struct shmob_drm_connector, connector) | |
| 482 | +static inline struct shmob_drm_connector *to_shmob_connector(struct drm_connector *connector) | |
| 483 | +{ | |
| 484 | + return container_of(connector, struct shmob_drm_connector, base); | |
| 485 | +} | |
| 616 | 486 | |
| 617 | 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 | 490 | struct drm_display_mode *mode; |
| 621 | 491 | |
| 622 | 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 | 494 | return 0; |
| 625 | 495 | |
| 626 | 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; | |
| 642 | − connector->display_info.height_mm = sdev->pdata->panel.height_mm; | |
| 498 | + drm_display_mode_from_videomode(scon->mode, mode); | |
| 499 | + | |
| 500 | + drm_mode_probed_add(connector, mode); | |
| 643 | 501 | |
| 644 | 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 | 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 | 520 | drm_connector_unregister(connector); |
| 666 | 521 | drm_connector_cleanup(connector); |
| 522 | + | |
| 523 | + kfree(connector); | |
| 667 | 524 | } |
| 668 | 525 | |
| 669 | 526 | static const struct drm_connector_funcs connector_funcs = { |
| 670 | − .dpms = drm_helper_connector_dpms, | |
| 527 | + .reset = drm_atomic_helper_connector_reset, | |
| 671 | 528 | .fill_modes = drm_helper_probe_single_connector_modes, |
| 672 | 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, | |
| 676 | − struct drm_encoder *encoder) | |
| 534 | +static struct drm_connector * | |
| 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 | 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; | |
| 684 | − connector->display_info.height_mm = sdev->pdata->panel.height_mm; | |
| 554 | + scon = kzalloc(sizeof(*scon), GFP_KERNEL); | |
| 555 | + if (!scon) | |
| 556 | + return ERR_PTR(-ENOMEM); | |
| 685 | 557 | |
| 686 | − ret = drm_connector_init(sdev->ddev, connector, &connector_funcs, | |
| 687 | − DRM_MODE_CONNECTOR_LVDS); | |
| 688 | − if (ret < 0) | |
| 689 | − return ret; | |
| 558 | + connector = &scon->base; | |
| 559 | + scon->encoder = encoder; | |
| 560 | + scon->mode = &sdev->pdata->panel.mode; | |
| 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 | 584 | drm_connector_helper_add(connector, &connector_helper_funcs); |
| 692 | 585 | |
| 693 | − ret = shmob_drm_backlight_init(&sdev->connector); | |
| 694 | − if (ret < 0) | |
| 695 | − goto err_cleanup; | |
| 586 | + return connector; | |
| 587 | +} | |
| 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 | 609 | ret = drm_connector_attach_encoder(connector, encoder); |
| 698 | 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); | |
| 702 | − drm_object_property_set_value(&connector->base, | |
| 703 | − sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); | |
| 615 | + sdev->connector = connector; | |
| 704 | 616 | |
| 705 | 617 | return 0; |
| 706 | 618 | |
| 707 | −err_backlight: | |
| 708 | − shmob_drm_backlight_exit(&sdev->connector); | |
| 709 | −err_cleanup: | |
| 619 | +error: | |
| 710 | 620 | drm_connector_cleanup(connector); |
| 711 | 621 | return ret; |
| 712 | 622 | } |
| @@ -14,39 +14,30 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <drm/drm_connector.h> |
| 15 | 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 | 21 | struct drm_pending_vblank_event; |
| 19 | 22 | struct shmob_drm_device; |
| 20 | 23 | struct shmob_drm_format_info; |
| 21 | 24 | |
| 22 | 25 | struct shmob_drm_crtc { |
| 23 | − struct drm_crtc crtc; | |
| 26 | + struct drm_crtc base; | |
| 24 | 27 | |
| 25 | 28 | struct drm_pending_vblank_event *event; |
| 26 | − int dpms; | |
| 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; | |
| 29 | + wait_queue_head_t flip_wait; | |
| 37 | 30 | }; |
| 38 | 31 | |
| 32 | +/* Legacy connector */ | |
| 39 | 33 | struct shmob_drm_connector { |
| 40 | − struct drm_connector connector; | |
| 34 | + struct drm_connector base; | |
| 41 | 35 | struct drm_encoder *encoder; |
| 42 | − | |
| 43 | − struct backlight_device *backlight; | |
| 36 | + const struct videomode *mode; | |
| 44 | 37 | }; |
| 45 | 38 | |
| 46 | 39 | int shmob_drm_crtc_create(struct shmob_drm_device *sdev); |
| 47 | 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 | 42 | int shmob_drm_encoder_create(struct shmob_drm_device *sdev); |
| 52 | 43 | int shmob_drm_connector_create(struct shmob_drm_device *sdev, |
| @@ -11,13 +11,17 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <linux/io.h> |
| 12 | 12 | #include <linux/mm.h> |
| 13 | 13 | #include <linux/module.h> |
| 14 | +#include <linux/of.h> | |
| 14 | 15 | #include <linux/platform_device.h> |
| 15 | 16 | #include <linux/pm.h> |
| 17 | +#include <linux/pm_runtime.h> | |
| 16 | 18 | #include <linux/slab.h> |
| 17 | 19 | |
| 20 | +#include <drm/drm_atomic_helper.h> | |
| 18 | 21 | #include <drm/drm_drv.h> |
| 19 | 22 | #include <drm/drm_fbdev_generic.h> |
| 20 | 23 | #include <drm/drm_gem_dma_helper.h> |
| 24 | +#include <drm/drm_modeset_helper.h> | |
| 21 | 25 | #include <drm/drm_module.h> |
| 22 | 26 | #include <drm/drm_probe_helper.h> |
| 23 | 27 | #include <drm/drm_vblank.h> |
| @@ -31,57 +35,23 @@ · 7 unmodified lines | ||
| 31 | 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 | 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 | 41 | struct clk *clk; |
| 72 | 42 | char *clkname; |
| 73 | 43 | |
| 74 | 44 | switch (clksrc) { |
| 75 | 45 | case SHMOB_DRM_CLK_BUS: |
| 76 | − clkname = "bus_clk"; | |
| 46 | + clkname = "fck"; | |
| 77 | 47 | sdev->lddckr = LDDCKR_ICKSEL_BUS; |
| 78 | 48 | break; |
| 79 | 49 | case SHMOB_DRM_CLK_PERIPHERAL: |
| 80 | − clkname = "peripheral_clk"; | |
| 50 | + clkname = "media"; | |
| 81 | 51 | sdev->lddckr = LDDCKR_ICKSEL_MIPI; |
| 82 | 52 | break; |
| 83 | 53 | case SHMOB_DRM_CLK_EXTERNAL: |
| 84 | − clkname = NULL; | |
| 54 | + clkname = "lclk"; | |
| 85 | 55 | sdev->lddckr = LDDCKR_ICKSEL_HDMI; |
| 86 | 56 | break; |
| 87 | 57 | default: |
| @@ -105,7 +75,7 @@ static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev, · 17 unmodified lines | ||
| 105 | 75 | static irqreturn_t shmob_drm_irq(int irq, void *arg) |
| 106 | 76 | { |
| 107 | 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 | 79 | unsigned long flags; |
| 110 | 80 | u32 status; |
| 111 | 81 | |
| @@ -119,7 +89,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg) · 7 unmodified lines | ||
| 119 | 89 | spin_unlock_irqrestore(&sdev->irq_lock, flags); |
| 120 | 90 | |
| 121 | 91 | if (status & LDINTR_VES) { |
| 122 | − drm_handle_vblank(dev, 0); | |
| 92 | + drm_crtc_handle_vblank(&sdev->crtc.base); | |
| 123 | 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 | 99 | DEFINE_DRM_GEM_DMA_FOPS(shmob_drm_fops); |
| 130 | 100 | |
| 131 | 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 | 103 | DRM_GEM_DMA_DRIVER_OPS, |
| 134 | 104 | .fops = &shmob_drm_fops, |
| 135 | 105 | .name = "shmob-drm", |
| @@ -147,26 +117,45 @@ static int shmob_drm_pm_suspend(struct device *dev) · 11 unmodified lines | ||
| 147 | 117 | { |
| 148 | 118 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); |
| 149 | 119 | |
| 150 | − drm_kms_helper_poll_disable(sdev->ddev); | |
| 151 | − shmob_drm_crtc_suspend(&sdev->crtc); | |
| 120 | + return drm_mode_config_helper_suspend(&sdev->ddev); | |
| 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 | 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 | 142 | struct shmob_drm_device *sdev = dev_get_drvdata(dev); |
| 143 | + int ret; | |
| 159 | 144 | |
| 160 | − drm_modeset_lock_all(sdev->ddev); | |
| 161 | − shmob_drm_crtc_resume(&sdev->crtc); | |
| 162 | − drm_modeset_unlock_all(sdev->ddev); | |
| 145 | + if (sdev->clock) { | |
| 146 | + ret = clk_prepare_enable(sdev->clock); | |
| 147 | + if (ret < 0) | |
| 148 | + return ret; | |
| 149 | + } | |
| 163 | 150 | |
| 164 | − drm_kms_helper_poll_enable(sdev->ddev); | |
| 165 | 151 | return 0; |
| 166 | 152 | } |
| 167 | 153 | |
| 168 | −static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops, | |
| 169 | − shmob_drm_pm_suspend, shmob_drm_pm_resume); | |
| 154 | +static const struct dev_pm_ops shmob_drm_pm_ops = { | |
| 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 | 161 | * Platform driver |
| @@ -175,37 +164,45 @@ static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops, · 2 unmodified lines | ||
| 175 | 164 | static void shmob_drm_remove(struct platform_device *pdev) |
| 176 | 165 | { |
| 177 | 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 | 169 | drm_dev_unregister(ddev); |
| 170 | + drm_atomic_helper_shutdown(ddev); | |
| 181 | 171 | drm_kms_helper_poll_fini(ddev); |
| 182 | − free_irq(sdev->irq, ddev); | |
| 183 | − drm_dev_put(ddev); | |
| 184 | 172 | } |
| 185 | 173 | |
| 186 | 174 | static int shmob_drm_probe(struct platform_device *pdev) |
| 187 | 175 | { |
| 188 | 176 | struct shmob_drm_platform_data *pdata = pdev->dev.platform_data; |
| 177 | + const struct shmob_drm_config *config; | |
| 189 | 178 | struct shmob_drm_device *sdev; |
| 190 | 179 | struct drm_device *ddev; |
| 191 | − unsigned int i; | |
| 192 | 180 | int ret; |
| 193 | 181 | |
| 194 | − if (pdata == NULL) { | |
| 182 | + config = of_device_get_match_data(&pdev->dev); | |
| 183 | + if (!config && !pdata) { | |
| 195 | 184 | dev_err(&pdev->dev, "no platform data\n"); |
| 196 | 185 | return -EINVAL; |
| 197 | 186 | } |
| 198 | 187 | |
| 199 | 188 | /* |
| 200 | − * Allocate and initialize the driver private data, I/O resources and | |
| 201 | − * clocks. | |
| 189 | + * Allocate and initialize the DRM device, driver private data, I/O | |
| 190 | + * resources and clocks. | |
| 202 | 191 | */ |
| 203 | − sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); | |
| 204 | − if (sdev == NULL) | |
| 205 | − return -ENOMEM; | |
| 192 | + sdev = devm_drm_dev_alloc(&pdev->dev, &shmob_drm_driver, | |
| 193 | + struct shmob_drm_device, ddev); | |
| 194 | + if (IS_ERR(sdev)) | |
| 195 | + return PTR_ERR(sdev); | |
| 206 | 196 | |
| 197 | + ddev = &sdev->ddev; | |
| 207 | 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 | 206 | spin_lock_init(&sdev->irq_lock); |
| 210 | 207 | |
| 211 | 208 | platform_set_drvdata(pdev, sdev); |
| @@ -214,49 +211,32 @@ static int shmob_drm_probe(struct platform_device *pdev) · 2 unmodified lines | ||
| 214 | 211 | if (IS_ERR(sdev->mmio)) |
| 215 | 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 | 215 | if (ret < 0) |
| 219 | 216 | return ret; |
| 220 | 217 | |
| 221 | − ret = shmob_drm_init_interface(sdev); | |
| 222 | − if (ret < 0) | |
| 218 | + ret = devm_pm_runtime_enable(&pdev->dev); | |
| 219 | + if (ret) | |
| 223 | 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 | 222 | ret = drm_vblank_init(ddev, 1); |
| 248 | 223 | if (ret < 0) { |
| 249 | 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 | 233 | ret = platform_get_irq(pdev, 0); |
| 254 | 234 | if (ret < 0) |
| 255 | 235 | goto err_modeset_cleanup; |
| 256 | 236 | sdev->irq = ret; |
| 257 | 237 | |
| 258 | − ret = request_irq(sdev->irq, shmob_drm_irq, 0, ddev->driver->name, | |
| 259 | − ddev); | |
| 238 | + ret = devm_request_irq(&pdev->dev, sdev->irq, shmob_drm_irq, 0, | |
| 239 | + ddev->driver->name, ddev); | |
| 260 | 240 | if (ret < 0) { |
| 261 | 241 | dev_err(&pdev->dev, "failed to install IRQ handler\n"); |
| 262 | 242 | goto err_modeset_cleanup; |
| @@ -268,28 +248,35 @@ static int shmob_drm_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 268 | 248 | */ |
| 269 | 249 | ret = drm_dev_register(ddev, 0); |
| 270 | 250 | if (ret < 0) |
| 271 | − goto err_irq_uninstall; | |
| 251 | + goto err_modeset_cleanup; | |
| 272 | 252 | |
| 273 | 253 | drm_fbdev_generic_setup(ddev, 16); |
| 274 | 254 | |
| 275 | 255 | return 0; |
| 276 | 256 | |
| 277 | −err_irq_uninstall: | |
| 278 | − free_irq(sdev->irq, ddev); | |
| 279 | 257 | err_modeset_cleanup: |
| 280 | 258 | drm_kms_helper_poll_fini(ddev); |
| 281 | −err_free_drm_dev: | |
| 282 | − drm_dev_put(ddev); | |
| 283 | − | |
| 284 | 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 | 273 | static struct platform_driver shmob_drm_platform_driver = { |
| 288 | 274 | .probe = shmob_drm_probe, |
| 289 | 275 | .remove_new = shmob_drm_remove, |
| 290 | 276 | .driver = { |
| 291 | 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 | 20 | struct device; |
| 21 | 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 | 28 | struct shmob_drm_device { |
| 24 | 29 | struct device *dev; |
| 25 | 30 | const struct shmob_drm_platform_data *pdata; |
| 31 | + struct shmob_drm_config config; | |
| 26 | 32 | |
| 27 | 33 | void __iomem *mmio; |
| 28 | 34 | struct clk *clock; |
| 29 | 35 | u32 lddckr; |
| 30 | − u32 ldmt1r; | |
| 31 | 36 | |
| 32 | 37 | unsigned int irq; |
| 33 | 38 | spinlock_t irq_lock; /* Protects hardware LDINTR register */ |
| 34 | 39 | |
| 35 | − struct drm_device *ddev; | |
| 40 | + struct drm_device ddev; | |
| 36 | 41 | |
| 37 | 42 | struct shmob_drm_crtc crtc; |
| 38 | − struct shmob_drm_encoder encoder; | |
| 39 | − struct shmob_drm_connector connector; | |
| 43 | + struct drm_encoder encoder; | |
| 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 | 52 | #endif /* __SHMOB_DRM_DRV_H__ */ |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | +#include <drm/drm_atomic_helper.h> | |
| 10 | 11 | #include <drm/drm_crtc.h> |
| 11 | 12 | #include <drm/drm_crtc_helper.h> |
| 12 | 13 | #include <drm/drm_fourcc.h> |
| @@ -17,6 +18,7 @@ · 4 unmodified lines | ||
| 17 | 18 | #include "shmob_drm_crtc.h" |
| 18 | 19 | #include "shmob_drm_drv.h" |
| 19 | 20 | #include "shmob_drm_kms.h" |
| 21 | +#include "shmob_drm_plane.h" | |
| 20 | 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 | 30 | .fourcc = DRM_FORMAT_RGB565, |
| 29 | 31 | .bpp = 16, |
| 30 | − .yuv = false, | |
| 31 | 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 | 37 | .fourcc = DRM_FORMAT_RGB888, |
| 34 | 38 | .bpp = 24, |
| 35 | − .yuv = false, | |
| 36 | 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 | 44 | .fourcc = DRM_FORMAT_ARGB8888, |
| 39 | 45 | .bpp = 32, |
| 40 | − .yuv = false, | |
| 41 | 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 | 51 | .fourcc = DRM_FORMAT_XRGB8888, |
| 44 | 52 | .bpp = 32, |
| 45 | − .yuv = false, | |
| 46 | 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 | 58 | .fourcc = DRM_FORMAT_NV12, |
| 49 | 59 | .bpp = 12, |
| 50 | − .yuv = true, | |
| 51 | 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 | 65 | .fourcc = DRM_FORMAT_NV21, |
| 54 | 66 | .bpp = 12, |
| 55 | − .yuv = true, | |
| 56 | 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 | 72 | .fourcc = DRM_FORMAT_NV16, |
| 59 | 73 | .bpp = 16, |
| 60 | − .yuv = true, | |
| 61 | 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 | 79 | .fourcc = DRM_FORMAT_NV61, |
| 64 | 80 | .bpp = 16, |
| 65 | − .yuv = true, | |
| 66 | 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 | 86 | .fourcc = DRM_FORMAT_NV24, |
| 69 | 87 | .bpp = 24, |
| 70 | − .yuv = true, | |
| 71 | 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 | 93 | .fourcc = DRM_FORMAT_NV42, |
| 74 | 94 | .bpp = 24, |
| 75 | − .yuv = true, | |
| 76 | 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 | 134 | return ERR_PTR(-EINVAL); |
| 113 | 135 | } |
| 114 | 136 | |
| 115 | − if (format->yuv) { | |
| 137 | + if (shmob_drm_format_is_yuv(format)) { | |
| 116 | 138 | unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1; |
| 117 | 139 | |
| 118 | 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 | 150 | static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = { |
| 129 | 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 | 156 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev) |
| 133 | 157 | { |
| 158 | + struct drm_device *dev = &sdev->ddev; | |
| 134 | 159 | int ret; |
| 135 | 160 | |
| 136 | − ret = drmm_mode_config_init(sdev->ddev); | |
| 161 | + ret = drmm_mode_config_init(dev); | |
| 137 | 162 | if (ret) |
| 138 | 163 | return ret; |
| 139 | 164 | |
| 140 | − shmob_drm_crtc_create(sdev); | |
| 141 | − shmob_drm_encoder_create(sdev); | |
| 142 | − shmob_drm_connector_create(sdev, &sdev->encoder.encoder); | |
| 165 | + ret = shmob_drm_crtc_create(sdev); | |
| 166 | + if (ret < 0) | |
| 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; | |
| 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; | |
| 179 | + drm_kms_helper_poll_init(dev); | |
| 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 | 187 | return 0; |
| 155 | 188 | } |
| @@ -17,11 +17,14 @@ struct shmob_drm_device; · 16 unmodified lines | ||
| 17 | 17 | |
| 18 | 18 | struct shmob_drm_format_info { |
| 19 | 19 | u32 fourcc; |
| 20 | − unsigned int bpp; | |
| 21 | − bool yuv; | |
| 22 | − u32 lddfr; | |
| 20 | + u32 lddfr; /* LCD Data Format Register */ | |
| 21 | + u16 ldbbsifr; /* CHn Source Image Format Register low bits */ | |
| 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 | 28 | const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc); |
| 26 | 29 | |
| 27 | 30 | int shmob_drm_modeset_init(struct shmob_drm_device *sdev); |
| @@ -7,11 +7,14 @@ · 6 unmodified lines | ||
| 7 | 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 | 12 | #include <drm/drm_crtc.h> |
| 11 | 13 | #include <drm/drm_fb_dma_helper.h> |
| 12 | 14 | #include <drm/drm_fourcc.h> |
| 13 | 15 | #include <drm/drm_framebuffer.h> |
| 14 | 16 | #include <drm/drm_gem_dma_helper.h> |
| 17 | +#include <drm/drm_plane_helper.h> | |
| 15 | 18 | |
| 16 | 19 | #include "shmob_drm_drv.h" |
| 17 | 20 | #include "shmob_drm_kms.h" |
| @@ -19,102 +22,84 @@ · 1 unmodified line | ||
| 19 | 22 | #include "shmob_drm_regs.h" |
| 20 | 23 | |
| 21 | 24 | struct shmob_drm_plane { |
| 22 | − struct drm_plane plane; | |
| 25 | + struct drm_plane base; | |
| 23 | 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 | 32 | const struct shmob_drm_format_info *format; |
| 27 | − unsigned long 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; | |
| 33 | + u32 dma[2]; | |
| 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, | |
| 40 | − struct drm_framebuffer *fb, | |
| 41 | − int x, int y) | |
| 46 | +static void shmob_drm_plane_compute_base(struct shmob_drm_plane_state *sstate) | |
| 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 | 51 | struct drm_gem_dma_object *gem; |
| 44 | 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 | 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 | 57 | + y * fb->pitches[0] + x * bpp / 8; |
| 50 | 58 | |
| 51 | − if (splane->format->yuv) { | |
| 52 | − bpp = splane->format->bpp - 8; | |
| 59 | + if (shmob_drm_format_is_yuv(sstate->format)) { | |
| 60 | + bpp = sstate->format->bpp - 8; | |
| 53 | 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 | 63 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] |
| 56 | 64 | + x * (bpp == 16 ? 2 : 1); |
| 57 | 65 | } |
| 58 | 66 | } |
| 59 | 67 | |
| 60 | −static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, | |
| 61 | − struct drm_framebuffer *fb) | |
| 68 | +static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane, | |
| 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 | 95 | u32 format; |
| 65 | 96 | |
| 66 | 97 | /* TODO: Support ROP3 mode */ |
| 67 | − format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT); | |
| 68 | − | |
| 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 | − } | |
| 98 | + format = LDBBSIFR_EN | ((state->alpha >> 8) << LDBBSIFR_LAY_SHIFT) | | |
| 99 | + sstate->format->ldbbsifr; | |
| 115 | 100 | |
| 116 | 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 | 103 | splane->index, #reg, \ |
| 119 | 104 | lcdc_read(sdev, reg(splane->index)), \ |
| 120 | 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 | 112 | plane_reg_dump(sdev, splane, LDBnBSACR); |
| 128 | 113 | |
| 129 | 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 | 116 | "LDBCR", lcdc_read(sdev, LDBCR)); |
| 132 | 117 | |
| 133 | 118 | lcdc_write(sdev, LDBnBSIFR(splane->index), format); |
| 134 | 119 | |
| 135 | 120 | lcdc_write(sdev, LDBnBSSZR(splane->index), |
| 136 | − (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) | | |
| 137 | − (splane->crtc_w << LDBBSSZR_BHSS_SHIFT)); | |
| 121 | + (state->crtc_h << LDBBSSZR_BVSS_SHIFT) | | |
| 122 | + (state->crtc_w << LDBBSSZR_BHSS_SHIFT)); | |
| 138 | 123 | lcdc_write(sdev, LDBnBLOCR(splane->index), |
| 139 | − (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) | | |
| 140 | − (splane->crtc_x << LDBBLOCR_CHLC_SHIFT)); | |
| 124 | + (state->crtc_y << LDBBLOCR_CVLC_SHIFT) | | |
| 125 | + (state->crtc_x << LDBBLOCR_CHLC_SHIFT)); | |
| 141 | 126 | lcdc_write(sdev, LDBnBSMWR(splane->index), |
| 142 | 127 | fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); |
| 143 | 128 | |
| 144 | − shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y); | |
| 145 | − | |
| 146 | − lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]); | |
| 147 | − if (splane->format->yuv) | |
| 148 | − lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]); | |
| 129 | + lcdc_write(sdev, LDBnBSAYR(splane->index), sstate->dma[0]); | |
| 130 | + if (shmob_drm_format_is_yuv(sstate->format)) | |
| 131 | + lcdc_write(sdev, LDBnBSACR(splane->index), sstate->dma[1]); | |
| 149 | 132 | |
| 150 | 133 | lcdc_write(sdev, LDBCR, |
| 151 | 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 | 136 | "LDBCR", lcdc_read(sdev, LDBCR)); |
| 154 | 137 | |
| 155 | 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 | 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) | |
| 168 | − return; | |
| 155 | + if (!new_plane_state->crtc) { | |
| 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); | |
| 171 | −} | |
| 166 | + crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); | |
| 167 | + if (IS_ERR(crtc_state)) | |
| 168 | + return PTR_ERR(crtc_state); | |
| 172 | 169 | |
| 173 | −static int | |
| 174 | −shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | |
| 175 | − struct drm_framebuffer *fb, int crtc_x, int crtc_y, | |
| 176 | − unsigned int crtc_w, unsigned int crtc_h, | |
| 177 | − uint32_t src_x, uint32_t src_y, | |
| 178 | − uint32_t src_w, uint32_t src_h, | |
| 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; | |
| 170 | + ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, | |
| 171 | + DRM_PLANE_NO_SCALING, | |
| 172 | + DRM_PLANE_NO_SCALING, | |
| 173 | + !is_primary, true); | |
| 174 | + if (ret < 0) | |
| 175 | + return ret; | |
| 184 | 176 | |
| 185 | − format = shmob_drm_format_info(fb->format->format); | |
| 186 | − if (format == NULL) { | |
| 187 | − dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n", | |
| 188 | − fb->format->format); | |
| 189 | − return -EINVAL; | |
| 177 | + if (!new_plane_state->visible) { | |
| 178 | + sstate->format = NULL; | |
| 179 | + return 0; | |
| 190 | 180 | } |
| 191 | 181 | |
| 192 | − if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { | |
| 193 | − dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__); | |
| 182 | + sstate->format = shmob_drm_format_info(new_plane_state->fb->format->format); | |
| 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 | 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 | 192 | return 0; |
| 208 | 193 | } |
| 209 | 194 | |
| 210 | −static int shmob_drm_plane_disable(struct drm_plane *plane, | |
| 211 | − struct drm_modeset_acquire_ctx *ctx) | |
| 195 | +static void shmob_drm_plane_atomic_update(struct drm_plane *plane, | |
| 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 | 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 | 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); | |
| 225 | − drm_plane_cleanup(plane); | |
| 232 | + struct shmob_drm_plane_state *state; | |
| 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 | 277 | static const struct drm_plane_funcs shmob_drm_plane_funcs = { |
| 229 | − .update_plane = shmob_drm_plane_update, | |
| 230 | − .disable_plane = shmob_drm_plane_disable, | |
| 231 | − .destroy = shmob_drm_plane_destroy, | |
| 278 | + .update_plane = drm_atomic_helper_update_plane, | |
| 279 | + .disable_plane = drm_atomic_helper_disable_plane, | |
| 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 | 285 | static const uint32_t formats[] = { |
| @@ -244,22 +295,23 @@ static const uint32_t formats[] = { · 9 unmodified lines | ||
| 244 | 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 | 302 | struct shmob_drm_plane *splane; |
| 250 | − int ret; | |
| 251 | 303 | |
| 252 | − splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL); | |
| 253 | − if (splane == NULL) | |
| 254 | − return -ENOMEM; | |
| 304 | + splane = drmm_universal_plane_alloc(&sdev->ddev, | |
| 305 | + struct shmob_drm_plane, base, 1, | |
| 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 | 312 | splane->index = index; |
| 257 | − splane->alpha = 255; | |
| 258 | 313 | |
| 259 | − ret = drm_universal_plane_init(sdev->ddev, &splane->plane, 1, | |
| 260 | − &shmob_drm_plane_funcs, | |
| 261 | − formats, ARRAY_SIZE(formats), NULL, | |
| 262 | − DRM_PLANE_TYPE_OVERLAY, NULL); | |
| 314 | + drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs); | |
| 263 | 315 | |
| 264 | − return ret; | |
| 316 | + return &splane->base; | |
| 265 | 317 | } |
| @@ -13,7 +13,8 @@ · 12 unmodified lines | ||
| 13 | 13 | struct drm_plane; |
| 14 | 14 | struct shmob_drm_device; |
| 15 | 15 | |
| 16 | −int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index); | |
| 17 | −void shmob_drm_plane_setup(struct drm_plane *plane); | |
| 16 | +struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev, | |
| 17 | + enum drm_plane_type type, | |
| 18 | + unsigned int index); | |
| 18 | 19 | |
| 19 | 20 | #endif /* __SHMOB_DRM_PLANE_H__ */ |
| @@ -10,7 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | #ifndef __SHMOB_DRM_H__ |
| 11 | 11 | #define __SHMOB_DRM_H__ |
| 12 | 12 | |
| 13 | −#include <drm/drm_mode.h> | |
| 13 | +#include <video/videomode.h> | |
| 14 | 14 | |
| 15 | 15 | enum shmob_drm_clk_source { |
| 16 | 16 | SHMOB_DRM_CLK_BUS, |
| @@ -18,72 +18,21 @@ enum shmob_drm_clk_source { · 1 unmodified line | ||
| 18 | 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 | 21 | struct shmob_drm_panel_data { |
| 51 | 22 | unsigned int width_mm; /* Panel width in mm */ |
| 52 | 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 | 27 | struct shmob_drm_interface_data { |
| 76 | − enum shmob_drm_interface interface; | |
| 77 | − struct shmob_drm_sys_interface_data sys; | |
| 28 | + unsigned int bus_fmt; /* MEDIA_BUS_FMT_* */ | |
| 78 | 29 | unsigned int clk_div; |
| 79 | − unsigned int flags; | |
| 80 | 30 | }; |
| 81 | 31 | |
| 82 | 32 | struct shmob_drm_platform_data { |
| 83 | 33 | enum shmob_drm_clk_source clk_source; |
| 84 | 34 | struct shmob_drm_interface_data iface; |
| 85 | 35 | struct shmob_drm_panel_data panel; |
| 86 | − struct shmob_drm_backlight_data backlight; | |
| 87 | 36 | }; |
| 88 | 37 | |
| 89 | 38 | #endif /* __SHMOB_DRM_H__ */ |
| @@ -34,7 +34,7 @@ · 33 unmodified lines | ||
| 34 | 34 | |
| 35 | 35 | #define MEDIA_BUS_FMT_FIXED 0x0001 |
| 36 | 36 | |
| 37 | −/* RGB - next is 0x1025 */ | |
| 37 | +/* RGB - next is 0x1026 */ | |
| 38 | 38 | #define MEDIA_BUS_FMT_RGB444_1X12 0x1016 |
| 39 | 39 | #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 |
| 40 | 40 | #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 |
| @@ -46,6 +46,7 @@ · 5 unmodified lines | ||
| 46 | 46 | #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 |
| 47 | 47 | #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 |
| 48 | 48 | #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 |
| 49 | +#define MEDIA_BUS_FMT_RGB666_2X9_BE 0x1025 | |
| 49 | 50 | #define MEDIA_BUS_FMT_BGR666_1X18 0x1023 |
| 50 | 51 | #define MEDIA_BUS_FMT_RBG888_1X24 0x100e |
| 51 | 52 | #define MEDIA_BUS_FMT_RGB666_1X24_CPADHI 0x1015 |