Cross-subsystem Changes: - dt-bindings: add vendor prefix for NLT Technologies, Ltd. (Lucas) - dt-bindings: Add support for samsung s6e3hf2 panel (Hoegeun) Core Changes: - Add drm_panel_bridge to avoid connector boilerplate in drivers (Eric) - Trival fixes for dupe forward decl and reduce scope of variable (Dawid) Driver Changes: - dw-hdmi: Use mode_valid hook on bridge instead of connector (Jose) - vc4,atmel-hlcdc: Use drm_panel_bridge where appropriate (Eric) - panel: Add Innolux P079ZCA panel driver (Chris) - panel-simple: Add NL12880B20-05, NL192108AC18-02D, P320HVN03 panels (Lucas) - panel-samsung-s6e3ha2: Add s6e3hf2 panel support (Hoegeun) - zte,vc4,pl111,panel,mxsfb: Miscellaneous fixes Cc: Jose Abreu <Jose.Abreu@synopsys.com> Cc: Eric Anholt <eric@anholt.net> Cc: Chris Zhong <zyw@rock-chips.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Hoegeun Kwon <hoegeun.kwon@samsung.com> Cc: Dawid Kurek <dawikur@gmail.com> * tag 'drm-misc-next-2017-06-15' of git://anongit.freedesktop.org/git/drm-misc: (26 commits) drm: Reduce scope of 'state' variable drm: mxsfb_crtc: Reset the eLCDIF controller drm: Remove duplicate forward declaration drm/panel: s6e3ha2: Add support for s6e3hf2 panel on TM2e board dt-bindings: Add support for samsung s6e3hf2 panel drm/panel: add backlight dependency for sitronix-st7789v drm/panel: S6E3HA2 needs backlight code drm/panel: simple: add support for AUO P320HVN03 drm/panel: simple: add support for NLT NL192108AC18-02D dt-bindings: add vendor prefix for NLT Technologies, Ltd. drm/panel: simple: add support for NEC NL12880B20-05 drm/panel: add Innolux P079ZCA panel driver dt-bindings: Add INNOLUX P079ZCA panel bindings drm/vc4: Fix resource leak in 'vc4_get_hang_state_ioctl()' in error handling path drm/vc4/vc4_bo.c: always set bo->resv drm: Add const to name field declaration in struct drm_prop_enum_list drm/pl111: Fix offset calculation for the primary plane. drm/atmel-hlcdc: Fix panel registration drm/bridge: Build the panel wrapper in drm_kms_helper drm/atmel-hlcdc: Replace the panel usage with drm_panel_bridge. ...
Unified · Split
| @@ -0,0 +1,8 @@ | ||
| 1 | +AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: should be "auo,p320hvn03" | |
| 5 | +- power-supply: as specified in the base binding | |
| 6 | + | |
| 7 | +This binding is compatible with the simple-panel binding, which is specified | |
| 8 | +in simple-panel.txt in this directory. | |
| @@ -0,0 +1,23 @@ | ||
| 1 | +Innolux P079ZCA 7.85" 768x1024 TFT LCD panel | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: should be "innolux,p079zca" | |
| 5 | +- reg: DSI virtual channel of the peripheral | |
| 6 | +- power-supply: phandle of the regulator that provides the supply voltage | |
| 7 | +- enable-gpios: panel enable gpio | |
| 8 | + | |
| 9 | +Optional properties: | |
| 10 | +- backlight: phandle of the backlight device attached to the panel | |
| 11 | + | |
| 12 | +Example: | |
| 13 | + | |
| 14 | + &mipi_dsi { | |
| 15 | + panel { | |
| 16 | + compatible = "innolux,p079zca"; | |
| 17 | + reg = <0>; | |
| 18 | + power-supply = <...>; | |
| 19 | + backlight = <&backlight>; | |
| 20 | + enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; | |
| 21 | + status = "okay"; | |
| 22 | + }; | |
| 23 | + }; | |
| @@ -0,0 +1,8 @@ | ||
| 1 | +NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: should be "nec,nl12880bc20-05" | |
| 5 | +- power-supply: as specified in the base binding | |
| 6 | + | |
| 7 | +This binding is compatible with the simple-panel binding, which is specified | |
| 8 | +in simple-panel.txt in this directory. | |
| @@ -0,0 +1,8 @@ | ||
| 1 | +NLT Technologies, Ltd. 15.6" FHD (1920x1080) LVDS TFT LCD panel | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: should be "nlt,nl192108ac18-02d" | |
| 5 | +- power-supply: as specified in the base binding | |
| 6 | + | |
| 7 | +This binding is compatible with the simple-panel binding, which is specified | |
| 8 | +in simple-panel.txt in this directory. | |
| @@ -1,7 +1,10 @@ | ||
| 1 | 1 | Samsung S6E3HA2 5.7" 1440x2560 AMOLED panel |
| 2 | +Samsung S6E3HF2 5.65" 1600x2560 AMOLED panel | |
| 2 | 3 | |
| 3 | 4 | Required properties: |
| 4 | − - compatible: "samsung,s6e3ha2" | |
| 5 | + - compatible: should be one of: | |
| 6 | + "samsung,s6e3ha2", | |
| 7 | + "samsung,s6e3hf2". | |
| 5 | 8 | - reg: the virtual channel number of a DSI peripheral |
| 6 | 9 | - vdd3-supply: I/O voltage supply |
| 7 | 10 | - vci-supply: voltage supply for analog circuits |
| @@ -219,6 +219,7 @@ nexbox Nexbox · 218 unmodified lines | ||
| 219 | 219 | newhaven Newhaven Display International |
| 220 | 220 | ni National Instruments |
| 221 | 221 | nintendo Nintendo |
| 222 | +nlt NLT Technologies, Ltd. | |
| 222 | 223 | nokia Nokia |
| 223 | 224 | nordic Nordic Semiconductor |
| 224 | 225 | nuvoton Nuvoton Technology Corporation |
| @@ -143,6 +143,12 @@ Bridge Helper Reference · 142 unmodified lines | ||
| 143 | 143 | .. kernel-doc:: drivers/gpu/drm/drm_bridge.c |
| 144 | 144 | :export: |
| 145 | 145 | |
| 146 | +Panel-Bridge Helper Reference | |
| 147 | +----------------------------- | |
| 148 | + | |
| 149 | +.. kernel-doc:: drivers/gpu/drm/bridge/panel.c | |
| 150 | + :export: | |
| 151 | + | |
| 146 | 152 | .. _drm_panel_helper: |
| 147 | 153 | |
| 148 | 154 | Panel Helper Reference |
| @@ -35,6 +35,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ · 34 unmodified lines | ||
| 35 | 35 | drm_simple_kms_helper.o drm_modeset_helper.o \ |
| 36 | 36 | drm_scdc_helper.o |
| 37 | 37 | |
| 38 | +drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o | |
| 38 | 39 | drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o |
| 39 | 40 | drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o |
| 40 | 41 | drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o |
| @@ -23,139 +23,17 @@ · 22 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | #include <drm/drmP.h> |
| 25 | 25 | #include <drm/drm_of.h> |
| 26 | +#include <drm/drm_bridge.h> | |
| 26 | 27 | |
| 27 | 28 | #include "atmel_hlcdc_dc.h" |
| 28 | 29 | |
| 29 | −/** | |
| 30 | − * Atmel HLCDC RGB connector structure | |
| 31 | − * | |
| 32 | − * This structure stores RGB slave device information. | |
| 33 | − * | |
| 34 | − * @connector: DRM connector | |
| 35 | − * @encoder: DRM encoder | |
| 36 | − * @dc: pointer to the atmel_hlcdc_dc structure | |
| 37 | − * @panel: panel connected on the RGB output | |
| 38 | − */ | |
| 39 | −struct atmel_hlcdc_rgb_output { | |
| 40 | − struct drm_connector connector; | |
| 41 | − struct drm_encoder encoder; | |
| 42 | − struct atmel_hlcdc_dc *dc; | |
| 43 | − struct drm_panel *panel; | |
| 44 | −}; | |
| 45 | − | |
| 46 | −static inline struct atmel_hlcdc_rgb_output * | |
| 47 | −drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector) | |
| 48 | −{ | |
| 49 | − return container_of(connector, struct atmel_hlcdc_rgb_output, | |
| 50 | − connector); | |
| 51 | −} | |
| 52 | − | |
| 53 | −static inline struct atmel_hlcdc_rgb_output * | |
| 54 | −drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder) | |
| 55 | −{ | |
| 56 | − return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); | |
| 57 | −} | |
| 58 | − | |
| 59 | −static void atmel_hlcdc_rgb_encoder_enable(struct drm_encoder *encoder) | |
| 60 | −{ | |
| 61 | − struct atmel_hlcdc_rgb_output *rgb = | |
| 62 | − drm_encoder_to_atmel_hlcdc_rgb_output(encoder); | |
| 63 | − | |
| 64 | − if (rgb->panel) { | |
| 65 | − drm_panel_prepare(rgb->panel); | |
| 66 | − drm_panel_enable(rgb->panel); | |
| 67 | − } | |
| 68 | −} | |
| 69 | − | |
| 70 | −static void atmel_hlcdc_rgb_encoder_disable(struct drm_encoder *encoder) | |
| 71 | −{ | |
| 72 | − struct atmel_hlcdc_rgb_output *rgb = | |
| 73 | − drm_encoder_to_atmel_hlcdc_rgb_output(encoder); | |
| 74 | − | |
| 75 | − if (rgb->panel) { | |
| 76 | − drm_panel_disable(rgb->panel); | |
| 77 | − drm_panel_unprepare(rgb->panel); | |
| 78 | − } | |
| 79 | −} | |
| 80 | − | |
| 81 | −static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { | |
| 82 | − .disable = atmel_hlcdc_rgb_encoder_disable, | |
| 83 | − .enable = atmel_hlcdc_rgb_encoder_enable, | |
| 84 | −}; | |
| 85 | − | |
| 86 | −static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder) | |
| 87 | −{ | |
| 88 | − drm_encoder_cleanup(encoder); | |
| 89 | − memset(encoder, 0, sizeof(*encoder)); | |
| 90 | −} | |
| 91 | − | |
| 92 | 30 | static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { |
| 93 | − .destroy = atmel_hlcdc_rgb_encoder_destroy, | |
| 94 | −}; | |
| 95 | − | |
| 96 | −static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector) | |
| 97 | −{ | |
| 98 | − struct atmel_hlcdc_rgb_output *rgb = | |
| 99 | − drm_connector_to_atmel_hlcdc_rgb_output(connector); | |
| 100 | − | |
| 101 | − if (rgb->panel) | |
| 102 | − return rgb->panel->funcs->get_modes(rgb->panel); | |
| 103 | − | |
| 104 | − return 0; | |
| 105 | −} | |
| 106 | − | |
| 107 | −static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector, | |
| 108 | − struct drm_display_mode *mode) | |
| 109 | −{ | |
| 110 | − struct atmel_hlcdc_rgb_output *rgb = | |
| 111 | − drm_connector_to_atmel_hlcdc_rgb_output(connector); | |
| 112 | − | |
| 113 | − return atmel_hlcdc_dc_mode_valid(rgb->dc, mode); | |
| 114 | −} | |
| 115 | − | |
| 116 | −static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = { | |
| 117 | − .get_modes = atmel_hlcdc_panel_get_modes, | |
| 118 | − .mode_valid = atmel_hlcdc_rgb_mode_valid, | |
| 119 | −}; | |
| 120 | − | |
| 121 | −static enum drm_connector_status | |
| 122 | −atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force) | |
| 123 | −{ | |
| 124 | − struct atmel_hlcdc_rgb_output *rgb = | |
| 125 | − drm_connector_to_atmel_hlcdc_rgb_output(connector); | |
| 126 | − | |
| 127 | − if (rgb->panel) | |
| 128 | − return connector_status_connected; | |
| 129 | − | |
| 130 | − return connector_status_disconnected; | |
| 131 | −} | |
| 132 | − | |
| 133 | −static void | |
| 134 | −atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector) | |
| 135 | −{ | |
| 136 | − struct atmel_hlcdc_rgb_output *rgb = | |
| 137 | − drm_connector_to_atmel_hlcdc_rgb_output(connector); | |
| 138 | − | |
| 139 | − if (rgb->panel) | |
| 140 | − drm_panel_detach(rgb->panel); | |
| 141 | − | |
| 142 | − drm_connector_cleanup(connector); | |
| 143 | −} | |
| 144 | − | |
| 145 | −static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = { | |
| 146 | − .dpms = drm_atomic_helper_connector_dpms, | |
| 147 | − .detect = atmel_hlcdc_panel_connector_detect, | |
| 148 | − .fill_modes = drm_helper_probe_single_connector_modes, | |
| 149 | − .destroy = atmel_hlcdc_panel_connector_destroy, | |
| 150 | − .reset = drm_atomic_helper_connector_reset, | |
| 151 | − .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 152 | − .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 31 | + .destroy = drm_encoder_cleanup, | |
| 153 | 32 | }; |
| 154 | 33 | |
| 155 | 34 | static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) |
| 156 | 35 | { |
| 157 | − struct atmel_hlcdc_dc *dc = dev->dev_private; | |
| 158 | − struct atmel_hlcdc_rgb_output *output; | |
| 36 | + struct drm_encoder *encoder; | |
| 159 | 37 | struct drm_panel *panel; |
| 160 | 38 | struct drm_bridge *bridge; |
| 161 | 39 | int ret; |
| @@ -165,55 +43,34 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) · 3 unmodified lines | ||
| 165 | 43 | if (ret) |
| 166 | 44 | return ret; |
| 167 | 45 | |
| 168 | − output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); | |
| 169 | − if (!output) | |
| 46 | + encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL); | |
| 47 | + if (!encoder) | |
| 170 | 48 | return -EINVAL; |
| 171 | 49 | |
| 172 | − output->dc = dc; | |
| 173 | − | |
| 174 | − drm_encoder_helper_add(&output->encoder, | |
| 175 | − &atmel_hlcdc_panel_encoder_helper_funcs); | |
| 176 | − ret = drm_encoder_init(dev, &output->encoder, | |
| 50 | + ret = drm_encoder_init(dev, encoder, | |
| 177 | 51 | &atmel_hlcdc_panel_encoder_funcs, |
| 178 | 52 | DRM_MODE_ENCODER_NONE, NULL); |
| 179 | 53 | if (ret) |
| 180 | 54 | return ret; |
| 181 | 55 | |
| 182 | − output->encoder.possible_crtcs = 0x1; | |
| 56 | + encoder->possible_crtcs = 0x1; | |
| 183 | 57 | |
| 184 | 58 | if (panel) { |
| 185 | − output->connector.dpms = DRM_MODE_DPMS_OFF; | |
| 186 | − output->connector.polled = DRM_CONNECTOR_POLL_CONNECT; | |
| 187 | − drm_connector_helper_add(&output->connector, | |
| 188 | − &atmel_hlcdc_panel_connector_helper_funcs); | |
| 189 | − ret = drm_connector_init(dev, &output->connector, | |
| 190 | − &atmel_hlcdc_panel_connector_funcs, | |
| 191 | − DRM_MODE_CONNECTOR_Unknown); | |
| 192 | − if (ret) | |
| 193 | − goto err_encoder_cleanup; | |
| 194 | − | |
| 195 | − drm_mode_connector_attach_encoder(&output->connector, | |
| 196 | − &output->encoder); | |
| 197 | − | |
| 198 | − ret = drm_panel_attach(panel, &output->connector); | |
| 199 | − if (ret) { | |
| 200 | − drm_connector_cleanup(&output->connector); | |
| 201 | − goto err_encoder_cleanup; | |
| 202 | − } | |
| 203 | − | |
| 204 | − output->panel = panel; | |
| 205 | − | |
| 206 | − return 0; | |
| 59 | + bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); | |
| 60 | + if (IS_ERR(bridge)) | |
| 61 | + return PTR_ERR(bridge); | |
| 207 | 62 | } |
| 208 | 63 | |
| 209 | 64 | if (bridge) { |
| 210 | − ret = drm_bridge_attach(&output->encoder, bridge, NULL); | |
| 65 | + ret = drm_bridge_attach(encoder, bridge, NULL); | |
| 211 | 66 | if (!ret) |
| 212 | 67 | return 0; |
| 68 | + | |
| 69 | + if (panel) | |
| 70 | + drm_panel_bridge_remove(bridge); | |
| 213 | 71 | } |
| 214 | 72 | |
| 215 | −err_encoder_cleanup: | |
| 216 | − drm_encoder_cleanup(&output->encoder); | |
| 73 | + drm_encoder_cleanup(encoder); | |
| 217 | 74 | |
| 218 | 75 | return ret; |
| 219 | 76 | } |
| @@ -4,6 +4,14 @@ config DRM_BRIDGE · 3 unmodified lines | ||
| 4 | 4 | help |
| 5 | 5 | Bridge registration and lookup framework. |
| 6 | 6 | |
| 7 | +config DRM_PANEL_BRIDGE | |
| 8 | + def_bool y | |
| 9 | + depends on DRM_BRIDGE | |
| 10 | + depends on DRM_KMS_HELPER | |
| 11 | + select DRM_PANEL | |
| 12 | + help | |
| 13 | + DRM bridge wrapper of DRM panels | |
| 14 | + | |
| 7 | 15 | menu "Display Interface Bridges" |
| 8 | 16 | depends on DRM && DRM_BRIDGE |
| 9 | 17 | |
| @@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC · 17 unmodified lines | ||
| 27 | 35 | config DRM_LVDS_ENCODER |
| 28 | 36 | tristate "Transparent parallel to LVDS encoder support" |
| 29 | 37 | depends on OF |
| 30 | − select DRM_KMS_HELPER | |
| 31 | − select DRM_PANEL | |
| 38 | + select DRM_PANEL_BRIDGE | |
| 32 | 39 | help |
| 33 | 40 | Support for transparent parallel to LVDS encoders that don't require |
| 34 | 41 | any configuration. |
| @@ -8,144 +8,18 @@ · 7 unmodified lines | ||
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | 10 | #include <drm/drmP.h> |
| 11 | −#include <drm/drm_atomic_helper.h> | |
| 12 | −#include <drm/drm_connector.h> | |
| 13 | −#include <drm/drm_crtc_helper.h> | |
| 14 | −#include <drm/drm_encoder.h> | |
| 15 | −#include <drm/drm_modeset_helper_vtables.h> | |
| 11 | +#include <drm/drm_bridge.h> | |
| 16 | 12 | #include <drm/drm_panel.h> |
| 17 | 13 | |
| 18 | 14 | #include <linux/of_graph.h> |
| 19 | 15 | |
| 20 | −struct lvds_encoder { | |
| 21 | − struct device *dev; | |
| 22 | − | |
| 23 | − struct drm_bridge bridge; | |
| 24 | − struct drm_connector connector; | |
| 25 | − struct drm_panel *panel; | |
| 26 | −}; | |
| 27 | − | |
| 28 | −static inline struct lvds_encoder * | |
| 29 | −drm_bridge_to_lvds_encoder(struct drm_bridge *bridge) | |
| 30 | −{ | |
| 31 | − return container_of(bridge, struct lvds_encoder, bridge); | |
| 32 | −} | |
| 33 | − | |
| 34 | −static inline struct lvds_encoder * | |
| 35 | −drm_connector_to_lvds_encoder(struct drm_connector *connector) | |
| 36 | −{ | |
| 37 | − return container_of(connector, struct lvds_encoder, connector); | |
| 38 | −} | |
| 39 | − | |
| 40 | −static int lvds_connector_get_modes(struct drm_connector *connector) | |
| 41 | −{ | |
| 42 | − struct lvds_encoder *lvds = drm_connector_to_lvds_encoder(connector); | |
| 43 | − | |
| 44 | − return drm_panel_get_modes(lvds->panel); | |
| 45 | −} | |
| 46 | − | |
| 47 | −static const struct drm_connector_helper_funcs lvds_connector_helper_funcs = { | |
| 48 | − .get_modes = lvds_connector_get_modes, | |
| 49 | −}; | |
| 50 | − | |
| 51 | −static const struct drm_connector_funcs lvds_connector_funcs = { | |
| 52 | − .dpms = drm_atomic_helper_connector_dpms, | |
| 53 | − .reset = drm_atomic_helper_connector_reset, | |
| 54 | − .fill_modes = drm_helper_probe_single_connector_modes, | |
| 55 | − .destroy = drm_connector_cleanup, | |
| 56 | − .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 57 | − .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 58 | −}; | |
| 59 | − | |
| 60 | −static int lvds_encoder_attach(struct drm_bridge *bridge) | |
| 61 | −{ | |
| 62 | − struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); | |
| 63 | − struct drm_connector *connector = &lvds->connector; | |
| 64 | − int ret; | |
| 65 | − | |
| 66 | − if (!bridge->encoder) { | |
| 67 | − DRM_ERROR("Missing encoder\n"); | |
| 68 | − return -ENODEV; | |
| 69 | − } | |
| 70 | − | |
| 71 | − drm_connector_helper_add(connector, &lvds_connector_helper_funcs); | |
| 72 | − | |
| 73 | − ret = drm_connector_init(bridge->dev, connector, &lvds_connector_funcs, | |
| 74 | − DRM_MODE_CONNECTOR_LVDS); | |
| 75 | − if (ret) { | |
| 76 | − DRM_ERROR("Failed to initialize connector\n"); | |
| 77 | − return ret; | |
| 78 | − } | |
| 79 | − | |
| 80 | − drm_mode_connector_attach_encoder(&lvds->connector, bridge->encoder); | |
| 81 | − | |
| 82 | − ret = drm_panel_attach(lvds->panel, &lvds->connector); | |
| 83 | − if (ret < 0) | |
| 84 | − return ret; | |
| 85 | − | |
| 86 | − return 0; | |
| 87 | −} | |
| 88 | − | |
| 89 | −static void lvds_encoder_detach(struct drm_bridge *bridge) | |
| 90 | −{ | |
| 91 | − struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); | |
| 92 | − | |
| 93 | − drm_panel_detach(lvds->panel); | |
| 94 | −} | |
| 95 | − | |
| 96 | −static void lvds_encoder_pre_enable(struct drm_bridge *bridge) | |
| 97 | −{ | |
| 98 | − struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); | |
| 99 | − | |
| 100 | − drm_panel_prepare(lvds->panel); | |
| 101 | −} | |
| 102 | − | |
| 103 | −static void lvds_encoder_enable(struct drm_bridge *bridge) | |
| 104 | −{ | |
| 105 | − struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); | |
| 106 | − | |
| 107 | − drm_panel_enable(lvds->panel); | |
| 108 | −} | |
| 109 | − | |
| 110 | −static void lvds_encoder_disable(struct drm_bridge *bridge) | |
| 111 | −{ | |
| 112 | − struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); | |
| 113 | − | |
| 114 | − drm_panel_disable(lvds->panel); | |
| 115 | −} | |
| 116 | − | |
| 117 | −static void lvds_encoder_post_disable(struct drm_bridge *bridge) | |
| 118 | −{ | |
| 119 | − struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); | |
| 120 | − | |
| 121 | − drm_panel_unprepare(lvds->panel); | |
| 122 | −} | |
| 123 | − | |
| 124 | −static const struct drm_bridge_funcs lvds_encoder_bridge_funcs = { | |
| 125 | − .attach = lvds_encoder_attach, | |
| 126 | − .detach = lvds_encoder_detach, | |
| 127 | − .pre_enable = lvds_encoder_pre_enable, | |
| 128 | − .enable = lvds_encoder_enable, | |
| 129 | − .disable = lvds_encoder_disable, | |
| 130 | − .post_disable = lvds_encoder_post_disable, | |
| 131 | −}; | |
| 132 | − | |
| 133 | 16 | static int lvds_encoder_probe(struct platform_device *pdev) |
| 134 | 17 | { |
| 135 | − struct lvds_encoder *lvds; | |
| 136 | 18 | struct device_node *port; |
| 137 | 19 | struct device_node *endpoint; |
| 138 | − struct device_node *panel; | |
| 139 | − | |
| 140 | − lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); | |
| 141 | − if (!lvds) | |
| 142 | − return -ENOMEM; | |
| 143 | − | |
| 144 | − lvds->dev = &pdev->dev; | |
| 145 | − platform_set_drvdata(pdev, lvds); | |
| 146 | − | |
| 147 | − lvds->bridge.funcs = &lvds_encoder_bridge_funcs; | |
| 148 | − lvds->bridge.of_node = pdev->dev.of_node; | |
| 20 | + struct device_node *panel_node; | |
| 21 | + struct drm_panel *panel; | |
| 22 | + struct drm_bridge *bridge; | |
| 149 | 23 | |
| 150 | 24 | /* Locate the panel DT node. */ |
| 151 | 25 | port = of_graph_get_port_by_id(pdev->dev.of_node, 1); |
| @@ -161,29 +35,34 @@ static int lvds_encoder_probe(struct platform_device *pdev) · 9 unmodified lines | ||
| 161 | 35 | return -ENXIO; |
| 162 | 36 | } |
| 163 | 37 | |
| 164 | − panel = of_graph_get_remote_port_parent(endpoint); | |
| 38 | + panel_node = of_graph_get_remote_port_parent(endpoint); | |
| 165 | 39 | of_node_put(endpoint); |
| 166 | − if (!panel) { | |
| 40 | + if (!panel_node) { | |
| 167 | 41 | dev_dbg(&pdev->dev, "no remote endpoint for port 1\n"); |
| 168 | 42 | return -ENXIO; |
| 169 | 43 | } |
| 170 | 44 | |
| 171 | − lvds->panel = of_drm_find_panel(panel); | |
| 172 | − of_node_put(panel); | |
| 173 | − if (!lvds->panel) { | |
| 45 | + panel = of_drm_find_panel(panel_node); | |
| 46 | + of_node_put(panel_node); | |
| 47 | + if (!panel) { | |
| 174 | 48 | dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); |
| 175 | 49 | return -EPROBE_DEFER; |
| 176 | 50 | } |
| 177 | 51 | |
| 178 | − /* Register the bridge. */ | |
| 179 | − return drm_bridge_add(&lvds->bridge); | |
| 52 | + bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS); | |
| 53 | + if (IS_ERR(bridge)) | |
| 54 | + return PTR_ERR(bridge); | |
| 55 | + | |
| 56 | + platform_set_drvdata(pdev, bridge); | |
| 57 | + | |
| 58 | + return 0; | |
| 180 | 59 | } |
| 181 | 60 | |
| 182 | 61 | static int lvds_encoder_remove(struct platform_device *pdev) |
| 183 | 62 | { |
| 184 | − struct lvds_encoder *encoder = platform_get_drvdata(pdev); | |
| 63 | + struct drm_bridge *bridge = platform_get_drvdata(pdev); | |
| 185 | 64 | |
| 186 | − drm_bridge_remove(&encoder->bridge); | |
| 65 | + drm_bridge_remove(bridge); | |
| 187 | 66 | |
| 188 | 67 | return 0; |
| 189 | 68 | } |
| @@ -0,0 +1,200 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
| 3 | + * Copyright (C) 2017 Broadcom | |
| 4 | + * | |
| 5 | + * This program is free software; you can redistribute it and/or | |
| 6 | + * modify it under the terms of the GNU General Public License as | |
| 7 | + * published by the Free Software Foundation; either version 2 of | |
| 8 | + * the License, or (at your option) any later version. | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#include <drm/drmP.h> | |
| 12 | +#include <drm/drm_panel.h> | |
| 13 | +#include <drm/drm_atomic_helper.h> | |
| 14 | +#include <drm/drm_connector.h> | |
| 15 | +#include <drm/drm_crtc_helper.h> | |
| 16 | +#include <drm/drm_encoder.h> | |
| 17 | +#include <drm/drm_modeset_helper_vtables.h> | |
| 18 | +#include <drm/drm_panel.h> | |
| 19 | + | |
| 20 | +struct panel_bridge { | |
| 21 | + struct drm_bridge bridge; | |
| 22 | + struct drm_connector connector; | |
| 23 | + struct drm_panel *panel; | |
| 24 | + u32 connector_type; | |
| 25 | +}; | |
| 26 | + | |
| 27 | +static inline struct panel_bridge * | |
| 28 | +drm_bridge_to_panel_bridge(struct drm_bridge *bridge) | |
| 29 | +{ | |
| 30 | + return container_of(bridge, struct panel_bridge, bridge); | |
| 31 | +} | |
| 32 | + | |
| 33 | +static inline struct panel_bridge * | |
| 34 | +drm_connector_to_panel_bridge(struct drm_connector *connector) | |
| 35 | +{ | |
| 36 | + return container_of(connector, struct panel_bridge, connector); | |
| 37 | +} | |
| 38 | + | |
| 39 | +static int panel_bridge_connector_get_modes(struct drm_connector *connector) | |
| 40 | +{ | |
| 41 | + struct panel_bridge *panel_bridge = | |
| 42 | + drm_connector_to_panel_bridge(connector); | |
| 43 | + | |
| 44 | + return drm_panel_get_modes(panel_bridge->panel); | |
| 45 | +} | |
| 46 | + | |
| 47 | +static const struct drm_connector_helper_funcs | |
| 48 | +panel_bridge_connector_helper_funcs = { | |
| 49 | + .get_modes = panel_bridge_connector_get_modes, | |
| 50 | +}; | |
| 51 | + | |
| 52 | +static const struct drm_connector_funcs panel_bridge_connector_funcs = { | |
| 53 | + .dpms = drm_atomic_helper_connector_dpms, | |
| 54 | + .reset = drm_atomic_helper_connector_reset, | |
| 55 | + .fill_modes = drm_helper_probe_single_connector_modes, | |
| 56 | + .destroy = drm_connector_cleanup, | |
| 57 | + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 58 | + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 59 | +}; | |
| 60 | + | |
| 61 | +static int panel_bridge_attach(struct drm_bridge *bridge) | |
| 62 | +{ | |
| 63 | + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); | |
| 64 | + struct drm_connector *connector = &panel_bridge->connector; | |
| 65 | + int ret; | |
| 66 | + | |
| 67 | + if (!bridge->encoder) { | |
| 68 | + DRM_ERROR("Missing encoder\n"); | |
| 69 | + return -ENODEV; | |
| 70 | + } | |
| 71 | + | |
| 72 | + drm_connector_helper_add(connector, | |
| 73 | + &panel_bridge_connector_helper_funcs); | |
| 74 | + | |
| 75 | + ret = drm_connector_init(bridge->dev, connector, | |
| 76 | + &panel_bridge_connector_funcs, | |
| 77 | + panel_bridge->connector_type); | |
| 78 | + if (ret) { | |
| 79 | + DRM_ERROR("Failed to initialize connector\n"); | |
| 80 | + return ret; | |
| 81 | + } | |
| 82 | + | |
| 83 | + drm_mode_connector_attach_encoder(&panel_bridge->connector, | |
| 84 | + bridge->encoder); | |
| 85 | + | |
| 86 | + ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector); | |
| 87 | + if (ret < 0) | |
| 88 | + return ret; | |
| 89 | + | |
| 90 | + return 0; | |
| 91 | +} | |
| 92 | + | |
| 93 | +static void panel_bridge_detach(struct drm_bridge *bridge) | |
| 94 | +{ | |
| 95 | + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); | |
| 96 | + | |
| 97 | + drm_panel_detach(panel_bridge->panel); | |
| 98 | +} | |
| 99 | + | |
| 100 | +static void panel_bridge_pre_enable(struct drm_bridge *bridge) | |
| 101 | +{ | |
| 102 | + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); | |
| 103 | + | |
| 104 | + drm_panel_prepare(panel_bridge->panel); | |
| 105 | +} | |
| 106 | + | |
| 107 | +static void panel_bridge_enable(struct drm_bridge *bridge) | |
| 108 | +{ | |
| 109 | + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); | |
| 110 | + | |
| 111 | + drm_panel_enable(panel_bridge->panel); | |
| 112 | +} | |
| 113 | + | |
| 114 | +static void panel_bridge_disable(struct drm_bridge *bridge) | |
| 115 | +{ | |
| 116 | + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); | |
| 117 | + | |
| 118 | + drm_panel_disable(panel_bridge->panel); | |
| 119 | +} | |
| 120 | + | |
| 121 | +static void panel_bridge_post_disable(struct drm_bridge *bridge) | |
| 122 | +{ | |
| 123 | + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); | |
| 124 | + | |
| 125 | + drm_panel_unprepare(panel_bridge->panel); | |
| 126 | +} | |
| 127 | + | |
| 128 | +static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { | |
| 129 | + .attach = panel_bridge_attach, | |
| 130 | + .detach = panel_bridge_detach, | |
| 131 | + .pre_enable = panel_bridge_pre_enable, | |
| 132 | + .enable = panel_bridge_enable, | |
| 133 | + .disable = panel_bridge_disable, | |
| 134 | + .post_disable = panel_bridge_post_disable, | |
| 135 | +}; | |
| 136 | + | |
| 137 | +/** | |
| 138 | + * drm_panel_bridge_add - Creates a drm_bridge and drm_connector that | |
| 139 | + * just calls the appropriate functions from drm_panel. | |
| 140 | + * | |
| 141 | + * @panel: The drm_panel being wrapped. Must be non-NULL. | |
| 142 | + * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be | |
| 143 | + * created. | |
| 144 | + * | |
| 145 | + * For drivers converting from directly using drm_panel: The expected | |
| 146 | + * usage pattern is that during either encoder module probe or DSI | |
| 147 | + * host attach, a drm_panel will be looked up through | |
| 148 | + * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to | |
| 149 | + * wrap that panel in the new bridge, and the result can then be | |
| 150 | + * passed to drm_bridge_attach(). The drm_panel_prepare() and related | |
| 151 | + * functions can be dropped from the encoder driver (they're now | |
| 152 | + * called by the KMS helpers before calling into the encoder), along | |
| 153 | + * with connector creation. When done with the bridge, | |
| 154 | + * drm_bridge_detach() should be called as normal, then | |
| 155 | + * drm_panel_bridge_remove() to free it. | |
| 156 | + */ | |
| 157 | +struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, | |
| 158 | + u32 connector_type) | |
| 159 | +{ | |
| 160 | + struct panel_bridge *panel_bridge; | |
| 161 | + int ret; | |
| 162 | + | |
| 163 | + if (!panel) | |
| 164 | + return ERR_PTR(EINVAL); | |
| 165 | + | |
| 166 | + panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), | |
| 167 | + GFP_KERNEL); | |
| 168 | + if (!panel_bridge) | |
| 169 | + return ERR_PTR(-ENOMEM); | |
| 170 | + | |
| 171 | + panel_bridge->connector_type = connector_type; | |
| 172 | + panel_bridge->panel = panel; | |
| 173 | + | |
| 174 | + panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; | |
| 175 | +#ifdef CONFIG_OF | |
| 176 | + panel_bridge->bridge.of_node = panel->dev->of_node; | |
| 177 | +#endif | |
| 178 | + | |
| 179 | + ret = drm_bridge_add(&panel_bridge->bridge); | |
| 180 | + if (ret) | |
| 181 | + return ERR_PTR(ret); | |
| 182 | + | |
| 183 | + return &panel_bridge->bridge; | |
| 184 | +} | |
| 185 | +EXPORT_SYMBOL(drm_panel_bridge_add); | |
| 186 | + | |
| 187 | +/** | |
| 188 | + * drm_panel_bridge_remove - Unregisters and frees a drm_bridge | |
| 189 | + * created by drm_panel_bridge_add(). | |
| 190 | + * | |
| 191 | + * @bridge: The drm_bridge being freed. | |
| 192 | + */ | |
| 193 | +void drm_panel_bridge_remove(struct drm_bridge *bridge) | |
| 194 | +{ | |
| 195 | + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); | |
| 196 | + | |
| 197 | + drm_bridge_remove(bridge); | |
| 198 | + devm_kfree(panel_bridge->panel->dev, bridge); | |
| 199 | +} | |
| 200 | +EXPORT_SYMBOL(drm_panel_bridge_remove); | |
| @@ -1907,24 +1907,6 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) · 1906 unmodified lines | ||
| 1907 | 1907 | return ret; |
| 1908 | 1908 | } |
| 1909 | 1909 | |
| 1910 | −static enum drm_mode_status | |
| 1911 | −dw_hdmi_connector_mode_valid(struct drm_connector *connector, | |
| 1912 | − struct drm_display_mode *mode) | |
| 1913 | −{ | |
| 1914 | − struct dw_hdmi *hdmi = container_of(connector, | |
| 1915 | − struct dw_hdmi, connector); | |
| 1916 | − enum drm_mode_status mode_status = MODE_OK; | |
| 1917 | − | |
| 1918 | − /* We don't support double-clocked modes */ | |
| 1919 | − if (mode->flags & DRM_MODE_FLAG_DBLCLK) | |
| 1920 | − return MODE_BAD; | |
| 1921 | − | |
| 1922 | − if (hdmi->plat_data->mode_valid) | |
| 1923 | − mode_status = hdmi->plat_data->mode_valid(connector, mode); | |
| 1924 | − | |
| 1925 | − return mode_status; | |
| 1926 | −} | |
| 1927 | − | |
| 1928 | 1910 | static void dw_hdmi_connector_force(struct drm_connector *connector) |
| 1929 | 1911 | { |
| 1930 | 1912 | struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, |
| @@ -1950,7 +1932,6 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = { · 19 unmodified lines | ||
| 1950 | 1932 | |
| 1951 | 1933 | static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { |
| 1952 | 1934 | .get_modes = dw_hdmi_connector_get_modes, |
| 1953 | − .mode_valid = dw_hdmi_connector_mode_valid, | |
| 1954 | 1935 | .best_encoder = drm_atomic_helper_best_encoder, |
| 1955 | 1936 | }; |
| 1956 | 1937 | |
| @@ -1973,18 +1954,22 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) · 16 unmodified lines | ||
| 1973 | 1954 | return 0; |
| 1974 | 1955 | } |
| 1975 | 1956 | |
| 1976 | −static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, | |
| 1977 | − const struct drm_display_mode *orig_mode, | |
| 1978 | − struct drm_display_mode *mode) | |
| 1957 | +static enum drm_mode_status | |
| 1958 | +dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge, | |
| 1959 | + const struct drm_display_mode *mode) | |
| 1979 | 1960 | { |
| 1980 | 1961 | struct dw_hdmi *hdmi = bridge->driver_private; |
| 1981 | 1962 | struct drm_connector *connector = &hdmi->connector; |
| 1982 | − enum drm_mode_status status; | |
| 1963 | + enum drm_mode_status mode_status = MODE_OK; | |
| 1983 | 1964 | |
| 1984 | − status = dw_hdmi_connector_mode_valid(connector, mode); | |
| 1985 | − if (status != MODE_OK) | |
| 1986 | − return false; | |
| 1987 | − return true; | |
| 1965 | + /* We don't support double-clocked modes */ | |
| 1966 | + if (mode->flags & DRM_MODE_FLAG_DBLCLK) | |
| 1967 | + return MODE_BAD; | |
| 1968 | + | |
| 1969 | + if (hdmi->plat_data->mode_valid) | |
| 1970 | + mode_status = hdmi->plat_data->mode_valid(connector, mode); | |
| 1971 | + | |
| 1972 | + return mode_status; | |
| 1988 | 1973 | } |
| 1989 | 1974 | |
| 1990 | 1975 | static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, |
| @@ -2028,7 +2013,7 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { · 37 unmodified lines | ||
| 2028 | 2013 | .enable = dw_hdmi_bridge_enable, |
| 2029 | 2014 | .disable = dw_hdmi_bridge_disable, |
| 2030 | 2015 | .mode_set = dw_hdmi_bridge_mode_set, |
| 2031 | − .mode_fixup = dw_hdmi_bridge_mode_fixup, | |
| 2016 | + .mode_valid = dw_hdmi_bridge_mode_valid, | |
| 2032 | 2017 | }; |
| 2033 | 2018 | |
| 2034 | 2019 | static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi) |
| @@ -109,9 +109,10 @@ struct drm_atomic_state * · 108 unmodified lines | ||
| 109 | 109 | drm_atomic_state_alloc(struct drm_device *dev) |
| 110 | 110 | { |
| 111 | 111 | struct drm_mode_config *config = &dev->mode_config; |
| 112 | − struct drm_atomic_state *state; | |
| 113 | 112 | |
| 114 | 113 | if (!config->funcs->atomic_state_alloc) { |
| 114 | + struct drm_atomic_state *state; | |
| 115 | + | |
| 115 | 116 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
| 116 | 117 | if (!state) |
| 117 | 118 | return NULL; |
| @@ -147,8 +147,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { · 146 unmodified lines | ||
| 147 | 147 | .destroy = drm_encoder_cleanup, |
| 148 | 148 | }; |
| 149 | 149 | |
| 150 | −static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con, | |
| 151 | − struct drm_display_mode *mode) | |
| 150 | +static enum drm_mode_status | |
| 151 | +imx6q_hdmi_mode_valid(struct drm_connector *con, | |
| 152 | + const struct drm_display_mode *mode) | |
| 152 | 153 | { |
| 153 | 154 | if (mode->clock < 13500) |
| 154 | 155 | return MODE_CLOCK_LOW; |
| @@ -159,8 +160,9 @@ static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con, · 4 unmodified lines | ||
| 159 | 160 | return MODE_OK; |
| 160 | 161 | } |
| 161 | 162 | |
| 162 | −static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con, | |
| 163 | − struct drm_display_mode *mode) | |
| 163 | +static enum drm_mode_status | |
| 164 | +imx6dl_hdmi_mode_valid(struct drm_connector *con, | |
| 165 | + const struct drm_display_mode *mode) | |
| 164 | 166 | { |
| 165 | 167 | if (mode->clock < 13500) |
| 166 | 168 | return MODE_CLOCK_LOW; |
| @@ -536,8 +536,9 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id) · 535 unmodified lines | ||
| 536 | 536 | } |
| 537 | 537 | |
| 538 | 538 | /* TOFIX Enable support for non-vic modes */ |
| 539 | −static enum drm_mode_status dw_hdmi_mode_valid(struct drm_connector *connector, | |
| 540 | − struct drm_display_mode *mode) | |
| 539 | +static enum drm_mode_status | |
| 540 | +dw_hdmi_mode_valid(struct drm_connector *connector, | |
| 541 | + const struct drm_display_mode *mode) | |
| 541 | 542 | { |
| 542 | 543 | unsigned int vclk_freq; |
| 543 | 544 | unsigned int venc_freq; |
| @@ -35,6 +35,13 @@ · 34 unmodified lines | ||
| 35 | 35 | #include "mxsfb_drv.h" |
| 36 | 36 | #include "mxsfb_regs.h" |
| 37 | 37 | |
| 38 | +#define MXS_SET_ADDR 0x4 | |
| 39 | +#define MXS_CLR_ADDR 0x8 | |
| 40 | +#define MODULE_CLKGATE BIT(30) | |
| 41 | +#define MODULE_SFTRST BIT(31) | |
| 42 | +/* 1 second delay should be plenty of time for block reset */ | |
| 43 | +#define RESET_TIMEOUT 1000000 | |
| 44 | + | |
| 38 | 45 | static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) |
| 39 | 46 | { |
| 40 | 47 | return (val & mxsfb->devdata->hs_wdth_mask) << |
| @@ -159,6 +166,36 @@ static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb) · 118 unmodified lines | ||
| 159 | 166 | clk_disable_unprepare(mxsfb->clk_disp_axi); |
| 160 | 167 | } |
| 161 | 168 | |
| 169 | +/* | |
| 170 | + * Clear the bit and poll it cleared. This is usually called with | |
| 171 | + * a reset address and mask being either SFTRST(bit 31) or CLKGATE | |
| 172 | + * (bit 30). | |
| 173 | + */ | |
| 174 | +static int clear_poll_bit(void __iomem *addr, u32 mask) | |
| 175 | +{ | |
| 176 | + u32 reg; | |
| 177 | + | |
| 178 | + writel(mask, addr + MXS_CLR_ADDR); | |
| 179 | + return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT); | |
| 180 | +} | |
| 181 | + | |
| 182 | +static int mxsfb_reset_block(void __iomem *reset_addr) | |
| 183 | +{ | |
| 184 | + int ret; | |
| 185 | + | |
| 186 | + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); | |
| 187 | + if (ret) | |
| 188 | + return ret; | |
| 189 | + | |
| 190 | + writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); | |
| 191 | + | |
| 192 | + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); | |
| 193 | + if (ret) | |
| 194 | + return ret; | |
| 195 | + | |
| 196 | + return clear_poll_bit(reset_addr, MODULE_CLKGATE); | |
| 197 | +} | |
| 198 | + | |
| 162 | 199 | static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) |
| 163 | 200 | { |
| 164 | 201 | struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode; |
| @@ -173,6 +210,11 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) · 8 unmodified lines | ||
| 173 | 210 | */ |
| 174 | 211 | mxsfb_enable_axi_clk(mxsfb); |
| 175 | 212 | |
| 213 | + /* Mandatory eLCDIF reset as per the Reference Manual */ | |
| 214 | + err = mxsfb_reset_block(mxsfb->base); | |
| 215 | + if (err) | |
| 216 | + return; | |
| 217 | + | |
| 176 | 218 | /* Clear the FIFOs */ |
| 177 | 219 | writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET); |
| 178 | 220 | |
| @@ -28,6 +28,17 @@ config DRM_PANEL_SIMPLE · 27 unmodified lines | ||
| 28 | 28 | that it can be automatically turned off when the panel goes into a |
| 29 | 29 | low power state. |
| 30 | 30 | |
| 31 | +config DRM_PANEL_INNOLUX_P079ZCA | |
| 32 | + tristate "Innolux P079ZCA panel" | |
| 33 | + depends on OF | |
| 34 | + depends on DRM_MIPI_DSI | |
| 35 | + depends on BACKLIGHT_CLASS_DEVICE | |
| 36 | + help | |
| 37 | + Say Y here if you want to enable support for Innolux P079ZCA | |
| 38 | + TFT-LCD modules. The panel has a 1024x768 resolution and uses | |
| 39 | + 24 bit RGB per pixel. It provides a MIPI DSI interface to | |
| 40 | + the host and has a built-in LED backlight. | |
| 41 | + | |
| 31 | 42 | config DRM_PANEL_JDI_LT070ME05000 |
| 32 | 43 | tristate "JDI LT070ME05000 WUXGA DSI panel" |
| 33 | 44 | depends on OF |
| @@ -66,6 +77,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA2 · 32 unmodified lines | ||
| 66 | 77 | tristate "Samsung S6E3HA2 DSI video mode panel" |
| 67 | 78 | depends on OF |
| 68 | 79 | depends on DRM_MIPI_DSI |
| 80 | + depends on BACKLIGHT_CLASS_DEVICE | |
| 69 | 81 | select VIDEOMODE_HELPERS |
| 70 | 82 | |
| 71 | 83 | config DRM_PANEL_SAMSUNG_S6E8AA0 |
| @@ -100,6 +112,7 @@ config DRM_PANEL_SHARP_LS043T1LE01 · 28 unmodified lines | ||
| 100 | 112 | config DRM_PANEL_SITRONIX_ST7789V |
| 101 | 113 | tristate "Sitronix ST7789V panel" |
| 102 | 114 | depends on OF && SPI |
| 115 | + depends on BACKLIGHT_CLASS_DEVICE | |
| 103 | 116 | help |
| 104 | 117 | Say Y here if you want to enable support for the Sitronix |
| 105 | 118 | ST7789V controller for 240x320 LCD panels |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o |
| 2 | 2 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o |
| 3 | +obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o | |
| 3 | 4 | obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o |
| 4 | 5 | obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o |
| 5 | 6 | obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o |
| @@ -0,0 +1,340 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd | |
| 3 | + * | |
| 4 | + * This program is free software; you can redistribute it and/or modify | |
| 5 | + * it under the terms of the GNU General Public License as published by | |
| 6 | + * the Free Software Foundation; either version 2 of the License, or | |
| 7 | + * (at your option) any later version. | |
| 8 | + */ | |
| 9 | + | |
| 10 | +#include <linux/backlight.h> | |
| 11 | +#include <linux/gpio/consumer.h> | |
| 12 | +#include <linux/module.h> | |
| 13 | +#include <linux/of.h> | |
| 14 | +#include <linux/regulator/consumer.h> | |
| 15 | + | |
| 16 | +#include <drm/drmP.h> | |
| 17 | +#include <drm/drm_crtc.h> | |
| 18 | +#include <drm/drm_mipi_dsi.h> | |
| 19 | +#include <drm/drm_panel.h> | |
| 20 | + | |
| 21 | +#include <video/mipi_display.h> | |
| 22 | + | |
| 23 | +struct innolux_panel { | |
| 24 | + struct drm_panel base; | |
| 25 | + struct mipi_dsi_device *link; | |
| 26 | + | |
| 27 | + struct backlight_device *backlight; | |
| 28 | + struct regulator *supply; | |
| 29 | + struct gpio_desc *enable_gpio; | |
| 30 | + | |
| 31 | + bool prepared; | |
| 32 | + bool enabled; | |
| 33 | +}; | |
| 34 | + | |
| 35 | +static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel) | |
| 36 | +{ | |
| 37 | + return container_of(panel, struct innolux_panel, base); | |
| 38 | +} | |
| 39 | + | |
| 40 | +static int innolux_panel_disable(struct drm_panel *panel) | |
| 41 | +{ | |
| 42 | + struct innolux_panel *innolux = to_innolux_panel(panel); | |
| 43 | + int err; | |
| 44 | + | |
| 45 | + if (!innolux->enabled) | |
| 46 | + return 0; | |
| 47 | + | |
| 48 | + innolux->backlight->props.power = FB_BLANK_POWERDOWN; | |
| 49 | + backlight_update_status(innolux->backlight); | |
| 50 | + | |
| 51 | + err = mipi_dsi_dcs_set_display_off(innolux->link); | |
| 52 | + if (err < 0) | |
| 53 | + DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n", | |
| 54 | + err); | |
| 55 | + | |
| 56 | + innolux->enabled = false; | |
| 57 | + | |
| 58 | + return 0; | |
| 59 | +} | |
| 60 | + | |
| 61 | +static int innolux_panel_unprepare(struct drm_panel *panel) | |
| 62 | +{ | |
| 63 | + struct innolux_panel *innolux = to_innolux_panel(panel); | |
| 64 | + int err; | |
| 65 | + | |
| 66 | + if (!innolux->prepared) | |
| 67 | + return 0; | |
| 68 | + | |
| 69 | + err = mipi_dsi_dcs_enter_sleep_mode(innolux->link); | |
| 70 | + if (err < 0) { | |
| 71 | + DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n", | |
| 72 | + err); | |
| 73 | + return err; | |
| 74 | + } | |
| 75 | + | |
| 76 | + gpiod_set_value_cansleep(innolux->enable_gpio, 0); | |
| 77 | + | |
| 78 | + /* T8: 80ms - 1000ms */ | |
| 79 | + msleep(80); | |
| 80 | + | |
| 81 | + err = regulator_disable(innolux->supply); | |
| 82 | + if (err < 0) | |
| 83 | + return err; | |
| 84 | + | |
| 85 | + innolux->prepared = false; | |
| 86 | + | |
| 87 | + return 0; | |
| 88 | +} | |
| 89 | + | |
| 90 | +static int innolux_panel_prepare(struct drm_panel *panel) | |
| 91 | +{ | |
| 92 | + struct innolux_panel *innolux = to_innolux_panel(panel); | |
| 93 | + int err, regulator_err; | |
| 94 | + | |
| 95 | + if (innolux->prepared) | |
| 96 | + return 0; | |
| 97 | + | |
| 98 | + gpiod_set_value_cansleep(innolux->enable_gpio, 0); | |
| 99 | + | |
| 100 | + err = regulator_enable(innolux->supply); | |
| 101 | + if (err < 0) | |
| 102 | + return err; | |
| 103 | + | |
| 104 | + /* T2: 15ms - 1000ms */ | |
| 105 | + usleep_range(15000, 16000); | |
| 106 | + | |
| 107 | + gpiod_set_value_cansleep(innolux->enable_gpio, 1); | |
| 108 | + | |
| 109 | + /* T4: 15ms - 1000ms */ | |
| 110 | + usleep_range(15000, 16000); | |
| 111 | + | |
| 112 | + err = mipi_dsi_dcs_exit_sleep_mode(innolux->link); | |
| 113 | + if (err < 0) { | |
| 114 | + DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n", | |
| 115 | + err); | |
| 116 | + goto poweroff; | |
| 117 | + } | |
| 118 | + | |
| 119 | + /* T6: 120ms - 1000ms*/ | |
| 120 | + msleep(120); | |
| 121 | + | |
| 122 | + err = mipi_dsi_dcs_set_display_on(innolux->link); | |
| 123 | + if (err < 0) { | |
| 124 | + DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n", | |
| 125 | + err); | |
| 126 | + goto poweroff; | |
| 127 | + } | |
| 128 | + | |
| 129 | + /* T7: 5ms */ | |
| 130 | + usleep_range(5000, 6000); | |
| 131 | + | |
| 132 | + innolux->prepared = true; | |
| 133 | + | |
| 134 | + return 0; | |
| 135 | + | |
| 136 | +poweroff: | |
| 137 | + regulator_err = regulator_disable(innolux->supply); | |
| 138 | + if (regulator_err) | |
| 139 | + DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", | |
| 140 | + regulator_err); | |
| 141 | + | |
| 142 | + gpiod_set_value_cansleep(innolux->enable_gpio, 0); | |
| 143 | + return err; | |
| 144 | +} | |
| 145 | + | |
| 146 | +static int innolux_panel_enable(struct drm_panel *panel) | |
| 147 | +{ | |
| 148 | + struct innolux_panel *innolux = to_innolux_panel(panel); | |
| 149 | + int ret; | |
| 150 | + | |
| 151 | + if (innolux->enabled) | |
| 152 | + return 0; | |
| 153 | + | |
| 154 | + innolux->backlight->props.power = FB_BLANK_UNBLANK; | |
| 155 | + ret = backlight_update_status(innolux->backlight); | |
| 156 | + if (ret) { | |
| 157 | + DRM_DEV_ERROR(panel->drm->dev, | |
| 158 | + "Failed to enable backlight %d\n", ret); | |
| 159 | + return ret; | |
| 160 | + } | |
| 161 | + | |
| 162 | + innolux->enabled = true; | |
| 163 | + | |
| 164 | + return 0; | |
| 165 | +} | |
| 166 | + | |
| 167 | +static const struct drm_display_mode default_mode = { | |
| 168 | + .clock = 56900, | |
| 169 | + .hdisplay = 768, | |
| 170 | + .hsync_start = 768 + 40, | |
| 171 | + .hsync_end = 768 + 40 + 40, | |
| 172 | + .htotal = 768 + 40 + 40 + 40, | |
| 173 | + .vdisplay = 1024, | |
| 174 | + .vsync_start = 1024 + 20, | |
| 175 | + .vsync_end = 1024 + 20 + 4, | |
| 176 | + .vtotal = 1024 + 20 + 4 + 20, | |
| 177 | + .vrefresh = 60, | |
| 178 | +}; | |
| 179 | + | |
| 180 | +static int innolux_panel_get_modes(struct drm_panel *panel) | |
| 181 | +{ | |
| 182 | + struct drm_display_mode *mode; | |
| 183 | + | |
| 184 | + mode = drm_mode_duplicate(panel->drm, &default_mode); | |
| 185 | + if (!mode) { | |
| 186 | + DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n", | |
| 187 | + default_mode.hdisplay, default_mode.vdisplay, | |
| 188 | + default_mode.vrefresh); | |
| 189 | + return -ENOMEM; | |
| 190 | + } | |
| 191 | + | |
| 192 | + drm_mode_set_name(mode); | |
| 193 | + | |
| 194 | + drm_mode_probed_add(panel->connector, mode); | |
| 195 | + | |
| 196 | + panel->connector->display_info.width_mm = 120; | |
| 197 | + panel->connector->display_info.height_mm = 160; | |
| 198 | + panel->connector->display_info.bpc = 8; | |
| 199 | + | |
| 200 | + return 1; | |
| 201 | +} | |
| 202 | + | |
| 203 | +static const struct drm_panel_funcs innolux_panel_funcs = { | |
| 204 | + .disable = innolux_panel_disable, | |
| 205 | + .unprepare = innolux_panel_unprepare, | |
| 206 | + .prepare = innolux_panel_prepare, | |
| 207 | + .enable = innolux_panel_enable, | |
| 208 | + .get_modes = innolux_panel_get_modes, | |
| 209 | +}; | |
| 210 | + | |
| 211 | +static const struct of_device_id innolux_of_match[] = { | |
| 212 | + { .compatible = "innolux,p079zca", }, | |
| 213 | + { } | |
| 214 | +}; | |
| 215 | +MODULE_DEVICE_TABLE(of, innolux_of_match); | |
| 216 | + | |
| 217 | +static int innolux_panel_add(struct innolux_panel *innolux) | |
| 218 | +{ | |
| 219 | + struct device *dev = &innolux->link->dev; | |
| 220 | + struct device_node *np; | |
| 221 | + int err; | |
| 222 | + | |
| 223 | + innolux->supply = devm_regulator_get(dev, "power"); | |
| 224 | + if (IS_ERR(innolux->supply)) | |
| 225 | + return PTR_ERR(innolux->supply); | |
| 226 | + | |
| 227 | + innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable", | |
| 228 | + GPIOD_OUT_HIGH); | |
| 229 | + if (IS_ERR(innolux->enable_gpio)) { | |
| 230 | + err = PTR_ERR(innolux->enable_gpio); | |
| 231 | + dev_dbg(dev, "failed to get enable gpio: %d\n", err); | |
| 232 | + innolux->enable_gpio = NULL; | |
| 233 | + } | |
| 234 | + | |
| 235 | + np = of_parse_phandle(dev->of_node, "backlight", 0); | |
| 236 | + if (np) { | |
| 237 | + innolux->backlight = of_find_backlight_by_node(np); | |
| 238 | + of_node_put(np); | |
| 239 | + | |
| 240 | + if (!innolux->backlight) | |
| 241 | + return -EPROBE_DEFER; | |
| 242 | + } | |
| 243 | + | |
| 244 | + drm_panel_init(&innolux->base); | |
| 245 | + innolux->base.funcs = &innolux_panel_funcs; | |
| 246 | + innolux->base.dev = &innolux->link->dev; | |
| 247 | + | |
| 248 | + err = drm_panel_add(&innolux->base); | |
| 249 | + if (err < 0) | |
| 250 | + goto put_backlight; | |
| 251 | + | |
| 252 | + return 0; | |
| 253 | + | |
| 254 | +put_backlight: | |
| 255 | + put_device(&innolux->backlight->dev); | |
| 256 | + | |
| 257 | + return err; | |
| 258 | +} | |
| 259 | + | |
| 260 | +static void innolux_panel_del(struct innolux_panel *innolux) | |
| 261 | +{ | |
| 262 | + if (innolux->base.dev) | |
| 263 | + drm_panel_remove(&innolux->base); | |
| 264 | + | |
| 265 | + put_device(&innolux->backlight->dev); | |
| 266 | +} | |
| 267 | + | |
| 268 | +static int innolux_panel_probe(struct mipi_dsi_device *dsi) | |
| 269 | +{ | |
| 270 | + struct innolux_panel *innolux; | |
| 271 | + int err; | |
| 272 | + | |
| 273 | + dsi->lanes = 4; | |
| 274 | + dsi->format = MIPI_DSI_FMT_RGB888; | |
| 275 | + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | | |
| 276 | + MIPI_DSI_MODE_LPM; | |
| 277 | + | |
| 278 | + innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL); | |
| 279 | + if (!innolux) | |
| 280 | + return -ENOMEM; | |
| 281 | + | |
| 282 | + mipi_dsi_set_drvdata(dsi, innolux); | |
| 283 | + | |
| 284 | + innolux->link = dsi; | |
| 285 | + | |
| 286 | + err = innolux_panel_add(innolux); | |
| 287 | + if (err < 0) | |
| 288 | + return err; | |
| 289 | + | |
| 290 | + err = mipi_dsi_attach(dsi); | |
| 291 | + return err; | |
| 292 | +} | |
| 293 | + | |
| 294 | +static int innolux_panel_remove(struct mipi_dsi_device *dsi) | |
| 295 | +{ | |
| 296 | + struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi); | |
| 297 | + int err; | |
| 298 | + | |
| 299 | + err = innolux_panel_unprepare(&innolux->base); | |
| 300 | + if (err < 0) | |
| 301 | + DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n", | |
| 302 | + err); | |
| 303 | + | |
| 304 | + err = innolux_panel_disable(&innolux->base); | |
| 305 | + if (err < 0) | |
| 306 | + DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err); | |
| 307 | + | |
| 308 | + err = mipi_dsi_detach(dsi); | |
| 309 | + if (err < 0) | |
| 310 | + DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n", | |
| 311 | + err); | |
| 312 | + | |
| 313 | + drm_panel_detach(&innolux->base); | |
| 314 | + innolux_panel_del(innolux); | |
| 315 | + | |
| 316 | + return 0; | |
| 317 | +} | |
| 318 | + | |
| 319 | +static void innolux_panel_shutdown(struct mipi_dsi_device *dsi) | |
| 320 | +{ | |
| 321 | + struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi); | |
| 322 | + | |
| 323 | + innolux_panel_unprepare(&innolux->base); | |
| 324 | + innolux_panel_disable(&innolux->base); | |
| 325 | +} | |
| 326 | + | |
| 327 | +static struct mipi_dsi_driver innolux_panel_driver = { | |
| 328 | + .driver = { | |
| 329 | + .name = "panel-innolux-p079zca", | |
| 330 | + .of_match_table = innolux_of_match, | |
| 331 | + }, | |
| 332 | + .probe = innolux_panel_probe, | |
| 333 | + .remove = innolux_panel_remove, | |
| 334 | + .shutdown = innolux_panel_shutdown, | |
| 335 | +}; | |
| 336 | +module_mipi_dsi_driver(innolux_panel_driver); | |
| 337 | + | |
| 338 | +MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); | |
| 339 | +MODULE_DESCRIPTION("Innolux P079ZCA panel driver"); | |
| 340 | +MODULE_LICENSE("GPL v2"); | |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <drm/drm_panel.h> |
| 17 | 17 | #include <linux/backlight.h> |
| 18 | 18 | #include <linux/gpio/consumer.h> |
| 19 | +#include <linux/of_device.h> | |
| 19 | 20 | #include <linux/regulator/consumer.h> |
| 20 | 21 | |
| 21 | 22 | #define S6E3HA2_MIN_BRIGHTNESS 0 |
| @@ -218,6 +219,16 @@ unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = { · 196 unmodified lines | ||
| 218 | 219 | 0x1d, 0x1e, 0x1f, 0x20, 0x21 |
| 219 | 220 | }; |
| 220 | 221 | |
| 222 | +enum s6e3ha2_type { | |
| 223 | + HA2_TYPE, | |
| 224 | + HF2_TYPE, | |
| 225 | +}; | |
| 226 | + | |
| 227 | +struct s6e3ha2_panel_desc { | |
| 228 | + const struct drm_display_mode *mode; | |
| 229 | + enum s6e3ha2_type type; | |
| 230 | +}; | |
| 231 | + | |
| 221 | 232 | struct s6e3ha2 { |
| 222 | 233 | struct device *dev; |
| 223 | 234 | struct drm_panel panel; |
| @@ -226,6 +237,8 @@ struct s6e3ha2 { · 2 unmodified lines | ||
| 226 | 237 | struct regulator_bulk_data supplies[2]; |
| 227 | 238 | struct gpio_desc *reset_gpio; |
| 228 | 239 | struct gpio_desc *enable_gpio; |
| 240 | + | |
| 241 | + const struct s6e3ha2_panel_desc *desc; | |
| 229 | 242 | }; |
| 230 | 243 | |
| 231 | 244 | static int s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len) |
| @@ -283,11 +296,21 @@ static int s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx) · 51 unmodified lines | ||
| 283 | 296 | static int s6e3ha2_freq_calibration(struct s6e3ha2 *ctx) |
| 284 | 297 | { |
| 285 | 298 | s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c); |
| 299 | + if (ctx->desc->type == HF2_TYPE) | |
| 300 | + s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67, 0x40, 0xc5); | |
| 286 | 301 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39); |
| 287 | 302 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0); |
| 288 | 303 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20); |
| 289 | − s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62, 0x40, | |
| 290 | − 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5); | |
| 304 | + | |
| 305 | + if (ctx->desc->type == HA2_TYPE) | |
| 306 | + s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62, | |
| 307 | + 0x40, 0x80, 0xc0, 0x28, 0x28, | |
| 308 | + 0x28, 0x28, 0x39, 0xc5); | |
| 309 | + else | |
| 310 | + s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x14, 0x6d, | |
| 311 | + 0x40, 0x80, 0xc0, 0x28, 0x28, | |
| 312 | + 0x28, 0x28, 0x39, 0xc5); | |
| 313 | + | |
| 291 | 314 | return 0; |
| 292 | 315 | } |
| 293 | 316 | |
| @@ -583,7 +606,7 @@ static int s6e3ha2_enable(struct drm_panel *panel) · 289 unmodified lines | ||
| 583 | 606 | return 0; |
| 584 | 607 | } |
| 585 | 608 | |
| 586 | −static const struct drm_display_mode default_mode = { | |
| 609 | +static const struct drm_display_mode s6e3ha2_mode = { | |
| 587 | 610 | .clock = 222372, |
| 588 | 611 | .hdisplay = 1440, |
| 589 | 612 | .hsync_start = 1440 + 1, |
| @@ -597,16 +620,41 @@ static const struct drm_display_mode default_mode = { · 7 unmodified lines | ||
| 597 | 620 | .flags = 0, |
| 598 | 621 | }; |
| 599 | 622 | |
| 623 | +static const struct s6e3ha2_panel_desc samsung_s6e3ha2 = { | |
| 624 | + .mode = &s6e3ha2_mode, | |
| 625 | + .type = HA2_TYPE, | |
| 626 | +}; | |
| 627 | + | |
| 628 | +static const struct drm_display_mode s6e3hf2_mode = { | |
| 629 | + .clock = 247856, | |
| 630 | + .hdisplay = 1600, | |
| 631 | + .hsync_start = 1600 + 1, | |
| 632 | + .hsync_end = 1600 + 1 + 1, | |
| 633 | + .htotal = 1600 + 1 + 1 + 1, | |
| 634 | + .vdisplay = 2560, | |
| 635 | + .vsync_start = 2560 + 1, | |
| 636 | + .vsync_end = 2560 + 1 + 1, | |
| 637 | + .vtotal = 2560 + 1 + 1 + 15, | |
| 638 | + .vrefresh = 60, | |
| 639 | + .flags = 0, | |
| 640 | +}; | |
| 641 | + | |
| 642 | +static const struct s6e3ha2_panel_desc samsung_s6e3hf2 = { | |
| 643 | + .mode = &s6e3hf2_mode, | |
| 644 | + .type = HF2_TYPE, | |
| 645 | +}; | |
| 646 | + | |
| 600 | 647 | static int s6e3ha2_get_modes(struct drm_panel *panel) |
| 601 | 648 | { |
| 602 | 649 | struct drm_connector *connector = panel->connector; |
| 650 | + struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel); | |
| 603 | 651 | struct drm_display_mode *mode; |
| 604 | 652 | |
| 605 | − mode = drm_mode_duplicate(panel->drm, &default_mode); | |
| 653 | + mode = drm_mode_duplicate(panel->drm, ctx->desc->mode); | |
| 606 | 654 | if (!mode) { |
| 607 | 655 | DRM_ERROR("failed to add mode %ux%ux@%u\n", |
| 608 | − default_mode.hdisplay, default_mode.vdisplay, | |
| 609 | − default_mode.vrefresh); | |
| 656 | + ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay, | |
| 657 | + ctx->desc->mode->vrefresh); | |
| 610 | 658 | return -ENOMEM; |
| 611 | 659 | } |
| 612 | 660 | |
| @@ -642,6 +690,7 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) · 29 unmodified lines | ||
| 642 | 690 | mipi_dsi_set_drvdata(dsi, ctx); |
| 643 | 691 | |
| 644 | 692 | ctx->dev = dev; |
| 693 | + ctx->desc = of_device_get_match_data(dev); | |
| 645 | 694 | |
| 646 | 695 | dsi->lanes = 4; |
| 647 | 696 | dsi->format = MIPI_DSI_FMT_RGB888; |
| @@ -717,7 +766,8 @@ static int s6e3ha2_remove(struct mipi_dsi_device *dsi) · 69 unmodified lines | ||
| 717 | 766 | } |
| 718 | 767 | |
| 719 | 768 | static const struct of_device_id s6e3ha2_of_match[] = { |
| 720 | − { .compatible = "samsung,s6e3ha2" }, | |
| 769 | + { .compatible = "samsung,s6e3ha2", .data = &samsung_s6e3ha2 }, | |
| 770 | + { .compatible = "samsung,s6e3hf2", .data = &samsung_s6e3hf2 }, | |
| 721 | 771 | { } |
| 722 | 772 | }; |
| 723 | 773 | MODULE_DEVICE_TABLE(of, s6e3ha2_of_match); |
| @@ -638,6 +638,34 @@ static const struct panel_desc auo_g185han01 = { · 637 unmodified lines | ||
| 638 | 638 | .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, |
| 639 | 639 | }; |
| 640 | 640 | |
| 641 | +static const struct display_timing auo_p320hvn03_timings = { | |
| 642 | + .pixelclock = { 106000000, 148500000, 164000000 }, | |
| 643 | + .hactive = { 1920, 1920, 1920 }, | |
| 644 | + .hfront_porch = { 25, 50, 130 }, | |
| 645 | + .hback_porch = { 25, 50, 130 }, | |
| 646 | + .hsync_len = { 20, 40, 105 }, | |
| 647 | + .vactive = { 1080, 1080, 1080 }, | |
| 648 | + .vfront_porch = { 8, 17, 150 }, | |
| 649 | + .vback_porch = { 8, 17, 150 }, | |
| 650 | + .vsync_len = { 4, 11, 100 }, | |
| 651 | +}; | |
| 652 | + | |
| 653 | +static const struct panel_desc auo_p320hvn03 = { | |
| 654 | + .timings = &auo_p320hvn03_timings, | |
| 655 | + .num_timings = 1, | |
| 656 | + .bpc = 8, | |
| 657 | + .size = { | |
| 658 | + .width = 698, | |
| 659 | + .height = 393, | |
| 660 | + }, | |
| 661 | + .delay = { | |
| 662 | + .prepare = 1, | |
| 663 | + .enable = 450, | |
| 664 | + .unprepare = 500, | |
| 665 | + }, | |
| 666 | + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, | |
| 667 | +}; | |
| 668 | + | |
| 641 | 669 | static const struct drm_display_mode auo_t215hvn01_mode = { |
| 642 | 670 | .clock = 148800, |
| 643 | 671 | .hdisplay = 1920, |
| @@ -1322,6 +1350,33 @@ static const struct panel_desc lg_lp129qe = { · 678 unmodified lines | ||
| 1322 | 1350 | }, |
| 1323 | 1351 | }; |
| 1324 | 1352 | |
| 1353 | +static const struct display_timing nec_nl12880bc20_05_timing = { | |
| 1354 | + .pixelclock = { 67000000, 71000000, 75000000 }, | |
| 1355 | + .hactive = { 1280, 1280, 1280 }, | |
| 1356 | + .hfront_porch = { 2, 30, 30 }, | |
| 1357 | + .hback_porch = { 6, 100, 100 }, | |
| 1358 | + .hsync_len = { 2, 30, 30 }, | |
| 1359 | + .vactive = { 800, 800, 800 }, | |
| 1360 | + .vfront_porch = { 5, 5, 5 }, | |
| 1361 | + .vback_porch = { 11, 11, 11 }, | |
| 1362 | + .vsync_len = { 7, 7, 7 }, | |
| 1363 | +}; | |
| 1364 | + | |
| 1365 | +static const struct panel_desc nec_nl12880bc20_05 = { | |
| 1366 | + .timings = &nec_nl12880bc20_05_timing, | |
| 1367 | + .num_timings = 1, | |
| 1368 | + .bpc = 8, | |
| 1369 | + .size = { | |
| 1370 | + .width = 261, | |
| 1371 | + .height = 163, | |
| 1372 | + }, | |
| 1373 | + .delay = { | |
| 1374 | + .enable = 50, | |
| 1375 | + .disable = 50, | |
| 1376 | + }, | |
| 1377 | + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, | |
| 1378 | +}; | |
| 1379 | + | |
| 1325 | 1380 | static const struct drm_display_mode nec_nl4827hc19_05b_mode = { |
| 1326 | 1381 | .clock = 10870, |
| 1327 | 1382 | .hdisplay = 480, |
| @@ -1371,6 +1426,32 @@ static const struct panel_desc netron_dy_e231732 = { · 43 unmodified lines | ||
| 1371 | 1426 | .bus_format = MEDIA_BUS_FMT_RGB666_1X18, |
| 1372 | 1427 | }; |
| 1373 | 1428 | |
| 1429 | +static const struct display_timing nlt_nl192108ac18_02d_timing = { | |
| 1430 | + .pixelclock = { 130000000, 148350000, 163000000 }, | |
| 1431 | + .hactive = { 1920, 1920, 1920 }, | |
| 1432 | + .hfront_porch = { 80, 100, 100 }, | |
| 1433 | + .hback_porch = { 100, 120, 120 }, | |
| 1434 | + .hsync_len = { 50, 60, 60 }, | |
| 1435 | + .vactive = { 1080, 1080, 1080 }, | |
| 1436 | + .vfront_porch = { 12, 30, 30 }, | |
| 1437 | + .vback_porch = { 4, 10, 10 }, | |
| 1438 | + .vsync_len = { 4, 5, 5 }, | |
| 1439 | +}; | |
| 1440 | + | |
| 1441 | +static const struct panel_desc nlt_nl192108ac18_02d = { | |
| 1442 | + .timings = &nlt_nl192108ac18_02d_timing, | |
| 1443 | + .num_timings = 1, | |
| 1444 | + .bpc = 8, | |
| 1445 | + .size = { | |
| 1446 | + .width = 344, | |
| 1447 | + .height = 194, | |
| 1448 | + }, | |
| 1449 | + .delay = { | |
| 1450 | + .unprepare = 500, | |
| 1451 | + }, | |
| 1452 | + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, | |
| 1453 | +}; | |
| 1454 | + | |
| 1374 | 1455 | static const struct drm_display_mode nvd_9128_mode = { |
| 1375 | 1456 | .clock = 29500, |
| 1376 | 1457 | .hdisplay = 800, |
| @@ -1887,6 +1968,9 @@ static const struct of_device_id platform_of_match[] = { · 510 unmodified lines | ||
| 1887 | 1968 | }, { |
| 1888 | 1969 | .compatible = "auo,g185han01", |
| 1889 | 1970 | .data = &auo_g185han01, |
| 1971 | + }, { | |
| 1972 | + .compatible = "auo,p320hvn03", | |
| 1973 | + .data = &auo_p320hvn03, | |
| 1890 | 1974 | }, { |
| 1891 | 1975 | .compatible = "auo,t215hvn01", |
| 1892 | 1976 | .data = &auo_t215hvn01, |
| @@ -1971,12 +2055,18 @@ static const struct of_device_id platform_of_match[] = { · 78 unmodified lines | ||
| 1971 | 2055 | }, { |
| 1972 | 2056 | .compatible = "lg,lp129qe", |
| 1973 | 2057 | .data = &lg_lp129qe, |
| 2058 | + }, { | |
| 2059 | + .compatible = "nec,nl12880bc20-05", | |
| 2060 | + .data = &nec_nl12880bc20_05, | |
| 1974 | 2061 | }, { |
| 1975 | 2062 | .compatible = "nec,nl4827hc19-05b", |
| 1976 | 2063 | .data = &nec_nl4827hc19_05b, |
| 1977 | 2064 | }, { |
| 1978 | 2065 | .compatible = "netron-dy,e231732", |
| 1979 | 2066 | .data = &netron_dy_e231732, |
| 2067 | + }, { | |
| 2068 | + .compatible = "nlt,nl192108ac18-02d", | |
| 2069 | + .data = &nlt_nl192108ac18_02d, | |
| 1980 | 2070 | }, { |
| 1981 | 2071 | .compatible = "nvd,9128", |
| 1982 | 2072 | .data = &nvd_9128, |
| @@ -50,17 +50,6 @@ irqreturn_t pl111_irq(int irq, void *data) · 49 unmodified lines | ||
| 50 | 50 | return status; |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | −static u32 pl111_get_fb_offset(struct drm_plane_state *pstate) | |
| 54 | −{ | |
| 55 | − struct drm_framebuffer *fb = pstate->fb; | |
| 56 | − struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0); | |
| 57 | − | |
| 58 | − return (obj->paddr + | |
| 59 | − fb->offsets[0] + | |
| 60 | − fb->format->cpp[0] * pstate->src_x + | |
| 61 | − fb->pitches[0] * pstate->src_y); | |
| 62 | −} | |
| 63 | − | |
| 64 | 53 | static int pl111_display_check(struct drm_simple_display_pipe *pipe, |
| 65 | 54 | struct drm_plane_state *pstate, |
| 66 | 55 | struct drm_crtc_state *cstate) |
| @@ -73,7 +62,7 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe, · 6 unmodified lines | ||
| 73 | 62 | return -EINVAL; |
| 74 | 63 | |
| 75 | 64 | if (fb) { |
| 76 | − u32 offset = pl111_get_fb_offset(pstate); | |
| 65 | + u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0); | |
| 77 | 66 | |
| 78 | 67 | /* FB base address must be dword aligned. */ |
| 79 | 68 | if (offset & 3) |
| @@ -249,7 +238,7 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe, · 169 unmodified lines | ||
| 249 | 238 | struct drm_framebuffer *fb = pstate->fb; |
| 250 | 239 | |
| 251 | 240 | if (fb) { |
| 252 | − u32 addr = pl111_get_fb_offset(pstate); | |
| 241 | + u32 addr = drm_fb_cma_get_gem_addr(fb, pstate, 0); | |
| 253 | 242 | |
| 254 | 243 | writel(addr, priv->regs + CLCD_UBAS); |
| 255 | 244 | } |
| @@ -155,7 +155,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) · 154 unmodified lines | ||
| 155 | 155 | |
| 156 | 156 | static enum drm_mode_status |
| 157 | 157 | dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, |
| 158 | − struct drm_display_mode *mode) | |
| 158 | + const struct drm_display_mode *mode) | |
| 159 | 159 | { |
| 160 | 160 | const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; |
| 161 | 161 | int pclk = mode->clock * 1000; |
| @@ -7,7 +7,7 @@ config DRM_VC4 · 6 unmodified lines | ||
| 7 | 7 | select DRM_KMS_HELPER |
| 8 | 8 | select DRM_KMS_CMA_HELPER |
| 9 | 9 | select DRM_GEM_CMA_HELPER |
| 10 | − select DRM_PANEL | |
| 10 | + select DRM_PANEL_BRIDGE | |
| 11 | 11 | select SND_PCM |
| 12 | 12 | select SND_PCM_ELD |
| 13 | 13 | select SND_SOC_GENERIC_DMAENGINE_PCM |
| @@ -91,8 +91,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo) · 90 unmodified lines | ||
| 91 | 91 | vc4->bo_stats.num_allocated--; |
| 92 | 92 | vc4->bo_stats.size_allocated -= obj->size; |
| 93 | 93 | |
| 94 | − if (bo->resv == &bo->_resv) | |
| 95 | − reservation_object_fini(bo->resv); | |
| 94 | + reservation_object_fini(&bo->_resv); | |
| 96 | 95 | |
| 97 | 96 | drm_gem_cma_free_object(obj); |
| 98 | 97 | } |
| @@ -212,6 +211,8 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) · 113 unmodified lines | ||
| 212 | 211 | vc4->bo_stats.num_allocated++; |
| 213 | 212 | vc4->bo_stats.size_allocated += size; |
| 214 | 213 | mutex_unlock(&vc4->bo_lock); |
| 214 | + bo->resv = &bo->_resv; | |
| 215 | + reservation_object_init(bo->resv); | |
| 215 | 216 | |
| 216 | 217 | return &bo->base.base; |
| 217 | 218 | } |
| @@ -250,12 +251,7 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, · 32 unmodified lines | ||
| 250 | 251 | return ERR_PTR(-ENOMEM); |
| 251 | 252 | } |
| 252 | 253 | } |
| 253 | − bo = to_vc4_bo(&cma_obj->base); | |
| 254 | − | |
| 255 | − bo->resv = &bo->_resv; | |
| 256 | − reservation_object_init(bo->resv); | |
| 257 | − | |
| 258 | − return bo; | |
| 254 | + return to_vc4_bo(&cma_obj->base); | |
| 259 | 255 | } |
| 260 | 256 | |
| 261 | 257 | int vc4_dumb_create(struct drm_file *file_priv, |
| @@ -23,8 +23,10 @@ · 22 unmodified lines | ||
| 23 | 23 | */ |
| 24 | 24 | |
| 25 | 25 | #include <drm/drm_atomic_helper.h> |
| 26 | +#include <drm/drm_bridge.h> | |
| 26 | 27 | #include <drm/drm_crtc_helper.h> |
| 27 | 28 | #include <drm/drm_edid.h> |
| 29 | +#include <drm/drm_of.h> | |
| 28 | 30 | #include <drm/drm_panel.h> |
| 29 | 31 | #include <linux/clk.h> |
| 30 | 32 | #include <linux/component.h> |
| @@ -95,7 +97,8 @@ struct vc4_dpi { · 64 unmodified lines | ||
| 95 | 97 | |
| 96 | 98 | struct drm_encoder *encoder; |
| 97 | 99 | struct drm_connector *connector; |
| 98 | − struct drm_panel *panel; | |
| 100 | + struct drm_bridge *bridge; | |
| 101 | + bool is_panel_bridge; | |
| 99 | 102 | |
| 100 | 103 | void __iomem *regs; |
| 101 | 104 | |
| @@ -118,24 +121,6 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder) · 16 unmodified lines | ||
| 118 | 121 | return container_of(encoder, struct vc4_dpi_encoder, base.base); |
| 119 | 122 | } |
| 120 | 123 | |
| 121 | −/* VC4 DPI connector KMS struct */ | |
| 122 | −struct vc4_dpi_connector { | |
| 123 | − struct drm_connector base; | |
| 124 | − struct vc4_dpi *dpi; | |
| 125 | − | |
| 126 | − /* Since the connector is attached to just the one encoder, | |
| 127 | − * this is the reference to it so we can do the best_encoder() | |
| 128 | − * hook. | |
| 129 | − */ | |
| 130 | − struct drm_encoder *encoder; | |
| 131 | −}; | |
| 132 | − | |
| 133 | −static inline struct vc4_dpi_connector * | |
| 134 | −to_vc4_dpi_connector(struct drm_connector *connector) | |
| 135 | −{ | |
| 136 | − return container_of(connector, struct vc4_dpi_connector, base); | |
| 137 | −} | |
| 138 | − | |
| 139 | 124 | #define DPI_REG(reg) { reg, #reg } |
| 140 | 125 | static const struct { |
| 141 | 126 | u32 reg; |
| @@ -167,80 +152,6 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused) · 25 unmodified lines | ||
| 167 | 152 | } |
| 168 | 153 | #endif |
| 169 | 154 | |
| 170 | −static enum drm_connector_status | |
| 171 | −vc4_dpi_connector_detect(struct drm_connector *connector, bool force) | |
| 172 | −{ | |
| 173 | − struct vc4_dpi_connector *vc4_connector = | |
| 174 | − to_vc4_dpi_connector(connector); | |
| 175 | − struct vc4_dpi *dpi = vc4_connector->dpi; | |
| 176 | − | |
| 177 | − if (dpi->panel) | |
| 178 | − return connector_status_connected; | |
| 179 | − else | |
| 180 | − return connector_status_disconnected; | |
| 181 | −} | |
| 182 | − | |
| 183 | −static void vc4_dpi_connector_destroy(struct drm_connector *connector) | |
| 184 | −{ | |
| 185 | − drm_connector_unregister(connector); | |
| 186 | − drm_connector_cleanup(connector); | |
| 187 | −} | |
| 188 | − | |
| 189 | −static int vc4_dpi_connector_get_modes(struct drm_connector *connector) | |
| 190 | −{ | |
| 191 | − struct vc4_dpi_connector *vc4_connector = | |
| 192 | − to_vc4_dpi_connector(connector); | |
| 193 | − struct vc4_dpi *dpi = vc4_connector->dpi; | |
| 194 | − | |
| 195 | − if (dpi->panel) | |
| 196 | − return drm_panel_get_modes(dpi->panel); | |
| 197 | − | |
| 198 | − return 0; | |
| 199 | −} | |
| 200 | − | |
| 201 | −static const struct drm_connector_funcs vc4_dpi_connector_funcs = { | |
| 202 | − .dpms = drm_atomic_helper_connector_dpms, | |
| 203 | − .detect = vc4_dpi_connector_detect, | |
| 204 | − .fill_modes = drm_helper_probe_single_connector_modes, | |
| 205 | − .destroy = vc4_dpi_connector_destroy, | |
| 206 | − .reset = drm_atomic_helper_connector_reset, | |
| 207 | − .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 208 | − .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 209 | −}; | |
| 210 | − | |
| 211 | −static const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = { | |
| 212 | − .get_modes = vc4_dpi_connector_get_modes, | |
| 213 | −}; | |
| 214 | − | |
| 215 | −static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev, | |
| 216 | − struct vc4_dpi *dpi) | |
| 217 | −{ | |
| 218 | − struct drm_connector *connector = NULL; | |
| 219 | − struct vc4_dpi_connector *dpi_connector; | |
| 220 | − | |
| 221 | − dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector), | |
| 222 | − GFP_KERNEL); | |
| 223 | − if (!dpi_connector) | |
| 224 | − return ERR_PTR(-ENOMEM); | |
| 225 | − | |
| 226 | − connector = &dpi_connector->base; | |
| 227 | − | |
| 228 | − dpi_connector->encoder = dpi->encoder; | |
| 229 | − dpi_connector->dpi = dpi; | |
| 230 | − | |
| 231 | − drm_connector_init(dev, connector, &vc4_dpi_connector_funcs, | |
| 232 | − DRM_MODE_CONNECTOR_DPI); | |
| 233 | − drm_connector_helper_add(connector, &vc4_dpi_connector_helper_funcs); | |
| 234 | − | |
| 235 | − connector->polled = 0; | |
| 236 | − connector->interlace_allowed = 0; | |
| 237 | − connector->doublescan_allowed = 0; | |
| 238 | − | |
| 239 | − drm_mode_connector_attach_encoder(connector, dpi->encoder); | |
| 240 | − | |
| 241 | − return connector; | |
| 242 | −} | |
| 243 | − | |
| 244 | 155 | static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { |
| 245 | 156 | .destroy = drm_encoder_cleanup, |
| 246 | 157 | }; |
| @@ -250,11 +161,7 @@ static void vc4_dpi_encoder_disable(struct drm_encoder *encoder) · 3 unmodified lines | ||
| 250 | 161 | struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder); |
| 251 | 162 | struct vc4_dpi *dpi = vc4_encoder->dpi; |
| 252 | 163 | |
| 253 | − drm_panel_disable(dpi->panel); | |
| 254 | − | |
| 255 | 164 | clk_disable_unprepare(dpi->pixel_clock); |
| 256 | − | |
| 257 | − drm_panel_unprepare(dpi->panel); | |
| 258 | 165 | } |
| 259 | 166 | |
| 260 | 167 | static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) |
| @@ -265,12 +172,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) · 4 unmodified lines | ||
| 265 | 172 | u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE; |
| 266 | 173 | int ret; |
| 267 | 174 | |
| 268 | − ret = drm_panel_prepare(dpi->panel); | |
| 269 | − if (ret) { | |
| 270 | − DRM_ERROR("Panel failed to prepare\n"); | |
| 271 | − return; | |
| 272 | − } | |
| 273 | − | |
| 274 | 175 | if (dpi->connector->display_info.num_bus_formats) { |
| 275 | 176 | u32 bus_format = dpi->connector->display_info.bus_formats[0]; |
| 276 | 177 | |
| @@ -321,13 +222,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) · 44 unmodified lines | ||
| 321 | 222 | ret = clk_prepare_enable(dpi->pixel_clock); |
| 322 | 223 | if (ret) |
| 323 | 224 | DRM_ERROR("Failed to set clock rate: %d\n", ret); |
| 324 | − | |
| 325 | − ret = drm_panel_enable(dpi->panel); | |
| 326 | − if (ret) { | |
| 327 | − DRM_ERROR("Panel failed to enable\n"); | |
| 328 | − drm_panel_unprepare(dpi->panel); | |
| 329 | − return; | |
| 330 | − } | |
| 331 | 225 | } |
| 332 | 226 | |
| 333 | 227 | static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder, |
| @@ -351,24 +245,34 @@ static const struct of_device_id vc4_dpi_dt_match[] = { · 17 unmodified lines | ||
| 351 | 245 | {} |
| 352 | 246 | }; |
| 353 | 247 | |
| 354 | −/* Walks the OF graph to find the panel node and then asks DRM to look | |
| 355 | − * up the panel. | |
| 248 | +/* Sets up the next link in the display chain, whether it's a panel or | |
| 249 | + * a bridge. | |
| 356 | 250 | */ |
| 357 | −static struct drm_panel *vc4_dpi_get_panel(struct device *dev) | |
| 251 | +static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) | |
| 358 | 252 | { |
| 359 | − struct device_node *panel_node; | |
| 360 | − struct device_node *np = dev->of_node; | |
| 253 | + struct device *dev = &dpi->pdev->dev; | |
| 361 | 254 | struct drm_panel *panel; |
| 255 | + int ret; | |
| 362 | 256 | |
| 363 | − /* don't proceed if we have an endpoint but no panel_node tied to it */ | |
| 364 | − panel_node = of_graph_get_remote_node(np, 0, 0); | |
| 365 | − if (!panel_node) | |
| 366 | − return NULL; | |
| 257 | + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, | |
| 258 | + &panel, &dpi->bridge); | |
| 259 | + if (ret) { | |
| 260 | + /* If nothing was connected in the DT, that's not an | |
| 261 | + * error. | |
| 262 | + */ | |
| 263 | + if (ret == -ENODEV) | |
| 264 | + return 0; | |
| 265 | + else | |
| 266 | + return ret; | |
| 267 | + } | |
| 367 | 268 | |
| 368 | − panel = of_drm_find_panel(panel_node); | |
| 369 | − of_node_put(panel_node); | |
| 269 | + if (panel) { | |
| 270 | + dpi->bridge = drm_panel_bridge_add(panel, | |
| 271 | + DRM_MODE_CONNECTOR_DPI); | |
| 272 | + dpi->is_panel_bridge = true; | |
| 273 | + } | |
| 370 | 274 | |
| 371 | − return panel; | |
| 275 | + return drm_bridge_attach(dpi->encoder, dpi->bridge, NULL); | |
| 372 | 276 | } |
| 373 | 277 | |
| 374 | 278 | static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) |
| @@ -422,20 +326,13 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) · 47 unmodified lines | ||
| 422 | 326 | if (ret) |
| 423 | 327 | DRM_ERROR("Failed to turn on core clock: %d\n", ret); |
| 424 | 328 | |
| 425 | − dpi->panel = vc4_dpi_get_panel(dev); | |
| 426 | − | |
| 427 | 329 | drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs, |
| 428 | 330 | DRM_MODE_ENCODER_DPI, NULL); |
| 429 | 331 | drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs); |
| 430 | 332 | |
| 431 | − dpi->connector = vc4_dpi_connector_init(drm, dpi); | |
| 432 | − if (IS_ERR(dpi->connector)) { | |
| 433 | − ret = PTR_ERR(dpi->connector); | |
| 333 | + ret = vc4_dpi_init_bridge(dpi); | |
| 334 | + if (ret) | |
| 434 | 335 | goto err_destroy_encoder; |
| 435 | − } | |
| 436 | − | |
| 437 | − if (dpi->panel) | |
| 438 | − drm_panel_attach(dpi->panel, dpi->connector); | |
| 439 | 336 | |
| 440 | 337 | dev_set_drvdata(dev, dpi); |
| 441 | 338 | |
| @@ -456,10 +353,9 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, · 14 unmodified lines | ||
| 456 | 353 | struct vc4_dev *vc4 = to_vc4_dev(drm); |
| 457 | 354 | struct vc4_dpi *dpi = dev_get_drvdata(dev); |
| 458 | 355 | |
| 459 | − if (dpi->panel) | |
| 460 | − drm_panel_detach(dpi->panel); | |
| 356 | + if (dpi->is_panel_bridge) | |
| 357 | + drm_panel_bridge_remove(dpi->bridge); | |
| 461 | 358 | |
| 462 | − vc4_dpi_connector_destroy(dpi->connector); | |
| 463 | 359 | drm_encoder_cleanup(dpi->encoder); |
| 464 | 360 | |
| 465 | 361 | clk_disable_unprepare(dpi->core_clock); |
| @@ -503,8 +503,8 @@ struct vc4_dsi { · 502 unmodified lines | ||
| 503 | 503 | |
| 504 | 504 | struct mipi_dsi_host dsi_host; |
| 505 | 505 | struct drm_encoder *encoder; |
| 506 | − struct drm_connector *connector; | |
| 507 | − struct drm_panel *panel; | |
| 506 | + struct drm_bridge *bridge; | |
| 507 | + bool is_panel_bridge; | |
| 508 | 508 | |
| 509 | 509 | void __iomem *regs; |
| 510 | 510 | |
| @@ -605,18 +605,6 @@ to_vc4_dsi_encoder(struct drm_encoder *encoder) · 94 unmodified lines | ||
| 605 | 605 | return container_of(encoder, struct vc4_dsi_encoder, base.base); |
| 606 | 606 | } |
| 607 | 607 | |
| 608 | −/* VC4 DSI connector KMS struct */ | |
| 609 | −struct vc4_dsi_connector { | |
| 610 | − struct drm_connector base; | |
| 611 | − struct vc4_dsi *dsi; | |
| 612 | −}; | |
| 613 | − | |
| 614 | −static inline struct vc4_dsi_connector * | |
| 615 | −to_vc4_dsi_connector(struct drm_connector *connector) | |
| 616 | −{ | |
| 617 | − return container_of(connector, struct vc4_dsi_connector, base); | |
| 618 | −} | |
| 619 | − | |
| 620 | 608 | #define DSI_REG(reg) { reg, #reg } |
| 621 | 609 | static const struct { |
| 622 | 610 | u32 reg; |
| @@ -724,79 +712,6 @@ int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) · 101 unmodified lines | ||
| 724 | 712 | } |
| 725 | 713 | #endif |
| 726 | 714 | |
| 727 | −static enum drm_connector_status | |
| 728 | −vc4_dsi_connector_detect(struct drm_connector *connector, bool force) | |
| 729 | −{ | |
| 730 | − struct vc4_dsi_connector *vc4_connector = | |
| 731 | − to_vc4_dsi_connector(connector); | |
| 732 | − struct vc4_dsi *dsi = vc4_connector->dsi; | |
| 733 | − | |
| 734 | − if (dsi->panel) | |
| 735 | − return connector_status_connected; | |
| 736 | − else | |
| 737 | − return connector_status_disconnected; | |
| 738 | −} | |
| 739 | − | |
| 740 | −static void vc4_dsi_connector_destroy(struct drm_connector *connector) | |
| 741 | −{ | |
| 742 | − drm_connector_unregister(connector); | |
| 743 | − drm_connector_cleanup(connector); | |
| 744 | −} | |
| 745 | − | |
| 746 | −static int vc4_dsi_connector_get_modes(struct drm_connector *connector) | |
| 747 | −{ | |
| 748 | − struct vc4_dsi_connector *vc4_connector = | |
| 749 | − to_vc4_dsi_connector(connector); | |
| 750 | − struct vc4_dsi *dsi = vc4_connector->dsi; | |
| 751 | − | |
| 752 | − if (dsi->panel) | |
| 753 | − return drm_panel_get_modes(dsi->panel); | |
| 754 | − | |
| 755 | − return 0; | |
| 756 | −} | |
| 757 | − | |
| 758 | −static const struct drm_connector_funcs vc4_dsi_connector_funcs = { | |
| 759 | − .dpms = drm_atomic_helper_connector_dpms, | |
| 760 | − .detect = vc4_dsi_connector_detect, | |
| 761 | − .fill_modes = drm_helper_probe_single_connector_modes, | |
| 762 | − .destroy = vc4_dsi_connector_destroy, | |
| 763 | − .reset = drm_atomic_helper_connector_reset, | |
| 764 | − .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 765 | − .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 766 | −}; | |
| 767 | − | |
| 768 | −static const struct drm_connector_helper_funcs vc4_dsi_connector_helper_funcs = { | |
| 769 | − .get_modes = vc4_dsi_connector_get_modes, | |
| 770 | −}; | |
| 771 | − | |
| 772 | −static struct drm_connector *vc4_dsi_connector_init(struct drm_device *dev, | |
| 773 | − struct vc4_dsi *dsi) | |
| 774 | −{ | |
| 775 | − struct drm_connector *connector; | |
| 776 | − struct vc4_dsi_connector *dsi_connector; | |
| 777 | − | |
| 778 | − dsi_connector = devm_kzalloc(dev->dev, sizeof(*dsi_connector), | |
| 779 | − GFP_KERNEL); | |
| 780 | − if (!dsi_connector) | |
| 781 | − return ERR_PTR(-ENOMEM); | |
| 782 | − | |
| 783 | − connector = &dsi_connector->base; | |
| 784 | − | |
| 785 | − dsi_connector->dsi = dsi; | |
| 786 | − | |
| 787 | − drm_connector_init(dev, connector, &vc4_dsi_connector_funcs, | |
| 788 | − DRM_MODE_CONNECTOR_DSI); | |
| 789 | − drm_connector_helper_add(connector, &vc4_dsi_connector_helper_funcs); | |
| 790 | − | |
| 791 | − connector->polled = 0; | |
| 792 | − connector->interlace_allowed = 0; | |
| 793 | − connector->doublescan_allowed = 0; | |
| 794 | − | |
| 795 | − drm_mode_connector_attach_encoder(connector, dsi->encoder); | |
| 796 | − | |
| 797 | − return connector; | |
| 798 | −} | |
| 799 | − | |
| 800 | 715 | static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) |
| 801 | 716 | { |
| 802 | 717 | drm_encoder_cleanup(encoder); |
| @@ -894,12 +809,8 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) · 91 unmodified lines | ||
| 894 | 809 | struct vc4_dsi *dsi = vc4_encoder->dsi; |
| 895 | 810 | struct device *dev = &dsi->pdev->dev; |
| 896 | 811 | |
| 897 | − drm_panel_disable(dsi->panel); | |
| 898 | − | |
| 899 | 812 | vc4_dsi_ulps(dsi, true); |
| 900 | 813 | |
| 901 | − drm_panel_unprepare(dsi->panel); | |
| 902 | − | |
| 903 | 814 | clk_disable_unprepare(dsi->pll_phy_clock); |
| 904 | 815 | clk_disable_unprepare(dsi->escape_clock); |
| 905 | 816 | clk_disable_unprepare(dsi->pixel_clock); |
| @@ -984,12 +895,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) · 78 unmodified lines | ||
| 984 | 895 | return; |
| 985 | 896 | } |
| 986 | 897 | |
| 987 | − ret = drm_panel_prepare(dsi->panel); | |
| 988 | − if (ret) { | |
| 989 | − DRM_ERROR("Panel failed to prepare\n"); | |
| 990 | − return; | |
| 991 | − } | |
| 992 | − | |
| 993 | 898 | if (debug_dump_regs) { |
| 994 | 899 | DRM_INFO("DSI regs before:\n"); |
| 995 | 900 | vc4_dsi_dump_regs(dsi); |
| @@ -1211,13 +1116,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) · 215 unmodified lines | ||
| 1211 | 1116 | DRM_INFO("DSI regs after:\n"); |
| 1212 | 1117 | vc4_dsi_dump_regs(dsi); |
| 1213 | 1118 | } |
| 1214 | − | |
| 1215 | − ret = drm_panel_enable(dsi->panel); | |
| 1216 | − if (ret) { | |
| 1217 | − DRM_ERROR("Panel failed to enable\n"); | |
| 1218 | − drm_panel_unprepare(dsi->panel); | |
| 1219 | − return; | |
| 1220 | − } | |
| 1221 | 1119 | } |
| 1222 | 1120 | |
| 1223 | 1121 | static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, |
| @@ -1415,17 +1313,22 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host, · 191 unmodified lines | ||
| 1415 | 1313 | return 0; |
| 1416 | 1314 | } |
| 1417 | 1315 | |
| 1418 | − dsi->panel = of_drm_find_panel(device->dev.of_node); | |
| 1419 | − if (!dsi->panel) | |
| 1420 | − return 0; | |
| 1421 | − | |
| 1422 | − ret = drm_panel_attach(dsi->panel, dsi->connector); | |
| 1423 | − if (ret != 0) | |
| 1424 | − return ret; | |
| 1316 | + dsi->bridge = of_drm_find_bridge(device->dev.of_node); | |
| 1317 | + if (!dsi->bridge) { | |
| 1318 | + struct drm_panel *panel = | |
| 1319 | + of_drm_find_panel(device->dev.of_node); | |
| 1425 | 1320 | |
| 1426 | − drm_helper_hpd_irq_event(dsi->connector->dev); | |
| 1321 | + dsi->bridge = drm_panel_bridge_add(panel, | |
| 1322 | + DRM_MODE_CONNECTOR_DSI); | |
| 1323 | + if (IS_ERR(dsi->bridge)) { | |
| 1324 | + ret = PTR_ERR(dsi->bridge); | |
| 1325 | + dsi->bridge = NULL; | |
| 1326 | + return ret; | |
| 1327 | + } | |
| 1328 | + dsi->is_panel_bridge = true; | |
| 1329 | + } | |
| 1427 | 1330 | |
| 1428 | − return 0; | |
| 1331 | + return drm_bridge_attach(dsi->encoder, dsi->bridge, NULL); | |
| 1429 | 1332 | } |
| 1430 | 1333 | |
| 1431 | 1334 | static int vc4_dsi_host_detach(struct mipi_dsi_host *host, |
| @@ -1433,15 +1336,9 @@ static int vc4_dsi_host_detach(struct mipi_dsi_host *host, · 1 unmodified line | ||
| 1433 | 1336 | { |
| 1434 | 1337 | struct vc4_dsi *dsi = host_to_dsi(host); |
| 1435 | 1338 | |
| 1436 | − if (dsi->panel) { | |
| 1437 | − int ret = drm_panel_detach(dsi->panel); | |
| 1438 | − | |
| 1439 | − if (ret) | |
| 1440 | − return ret; | |
| 1441 | − | |
| 1442 | − dsi->panel = NULL; | |
| 1443 | − | |
| 1444 | − drm_helper_hpd_irq_event(dsi->connector->dev); | |
| 1339 | + if (dsi->is_panel_bridge) { | |
| 1340 | + drm_panel_bridge_remove(dsi->bridge); | |
| 1341 | + dsi->bridge = NULL; | |
| 1445 | 1342 | } |
| 1446 | 1343 | |
| 1447 | 1344 | return 0; |
| @@ -1708,12 +1605,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) · 260 unmodified lines | ||
| 1708 | 1605 | DRM_MODE_ENCODER_DSI, NULL); |
| 1709 | 1606 | drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); |
| 1710 | 1607 | |
| 1711 | − dsi->connector = vc4_dsi_connector_init(drm, dsi); | |
| 1712 | − if (IS_ERR(dsi->connector)) { | |
| 1713 | − ret = PTR_ERR(dsi->connector); | |
| 1714 | − goto err_destroy_encoder; | |
| 1715 | − } | |
| 1716 | − | |
| 1717 | 1608 | dsi->dsi_host.ops = &vc4_dsi_host_ops; |
| 1718 | 1609 | dsi->dsi_host.dev = dev; |
| 1719 | 1610 | |
| @@ -1724,11 +1615,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) · 4 unmodified lines | ||
| 1724 | 1615 | pm_runtime_enable(dev); |
| 1725 | 1616 | |
| 1726 | 1617 | return 0; |
| 1727 | − | |
| 1728 | −err_destroy_encoder: | |
| 1729 | − vc4_dsi_encoder_destroy(dsi->encoder); | |
| 1730 | − | |
| 1731 | − return ret; | |
| 1732 | 1618 | } |
| 1733 | 1619 | |
| 1734 | 1620 | static void vc4_dsi_unbind(struct device *dev, struct device *master, |
| @@ -1740,7 +1626,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, · 5 unmodified lines | ||
| 1740 | 1626 | |
| 1741 | 1627 | pm_runtime_disable(dev); |
| 1742 | 1628 | |
| 1743 | − vc4_dsi_connector_destroy(dsi->connector); | |
| 1629 | + drm_bridge_remove(dsi->bridge); | |
| 1744 | 1630 | vc4_dsi_encoder_destroy(dsi->encoder); |
| 1745 | 1631 | |
| 1746 | 1632 | mipi_dsi_host_unregister(&dsi->dsi_host); |
| @@ -111,8 +111,8 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, · 110 unmodified lines | ||
| 111 | 111 | &handle); |
| 112 | 112 | |
| 113 | 113 | if (ret) { |
| 114 | − state->bo_count = i - 1; | |
| 115 | − goto err; | |
| 114 | + state->bo_count = i; | |
| 115 | + goto err_delete_handle; | |
| 116 | 116 | } |
| 117 | 117 | bo_state[i].handle = handle; |
| 118 | 118 | bo_state[i].paddr = vc4_bo->base.paddr; |
| @@ -124,13 +124,16 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, · 5 unmodified lines | ||
| 124 | 124 | state->bo_count * sizeof(*bo_state))) |
| 125 | 125 | ret = -EFAULT; |
| 126 | 126 | |
| 127 | − kfree(bo_state); | |
| 127 | +err_delete_handle: | |
| 128 | + if (ret) { | |
| 129 | + for (i = 0; i < state->bo_count; i++) | |
| 130 | + drm_gem_handle_delete(file_priv, bo_state[i].handle); | |
| 131 | + } | |
| 128 | 132 | |
| 129 | 133 | err_free: |
| 130 | − | |
| 131 | 134 | vc4_free_hang_state(dev, kernel_state); |
| 135 | + kfree(bo_state); | |
| 132 | 136 | |
| 133 | −err: | |
| 134 | 137 | return ret; |
| 135 | 138 | } |
| 136 | 139 | |
| @@ -196,7 +196,7 @@ static int zx_drm_probe(struct platform_device *pdev) · 195 unmodified lines | ||
| 196 | 196 | struct component_match *match = NULL; |
| 197 | 197 | int ret; |
| 198 | 198 | |
| 199 | − ret = of_platform_populate(parent, NULL, NULL, dev); | |
| 199 | + ret = devm_of_platform_populate(dev); | |
| 200 | 200 | if (ret) |
| 201 | 201 | return ret; |
| 202 | 202 | |
| @@ -125,7 +125,7 @@ struct dw_hdmi_phy_ops { · 124 unmodified lines | ||
| 125 | 125 | struct dw_hdmi_plat_data { |
| 126 | 126 | struct regmap *regm; |
| 127 | 127 | enum drm_mode_status (*mode_valid)(struct drm_connector *connector, |
| 128 | − struct drm_display_mode *mode); | |
| 128 | + const struct drm_display_mode *mode); | |
| 129 | 129 | unsigned long input_bus_format; |
| 130 | 130 | unsigned long input_bus_encoding; |
| 131 | 131 | |
| @@ -29,6 +29,7 @@ · 28 unmodified lines | ||
| 29 | 29 | #include <drm/drm_modes.h> |
| 30 | 30 | |
| 31 | 31 | struct drm_bridge; |
| 32 | +struct drm_panel; | |
| 32 | 33 | |
| 33 | 34 | /** |
| 34 | 35 | * struct drm_bridge_funcs - drm_bridge control functions |
| @@ -263,4 +264,10 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, · 228 unmodified lines | ||
| 263 | 264 | void drm_bridge_pre_enable(struct drm_bridge *bridge); |
| 264 | 265 | void drm_bridge_enable(struct drm_bridge *bridge); |
| 265 | 266 | |
| 267 | +#ifdef CONFIG_DRM_PANEL_BRIDGE | |
| 268 | +struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, | |
| 269 | + u32 connector_type); | |
| 270 | +void drm_panel_bridge_remove(struct drm_bridge *bridge); | |
| 271 | +#endif | |
| 272 | + | |
| 266 | 273 | #endif |
| @@ -30,8 +30,6 @@ · 29 unmodified lines | ||
| 30 | 30 | |
| 31 | 31 | #include <uapi/drm/drm_mode.h> |
| 32 | 32 | |
| 33 | −struct drm_device; | |
| 34 | − | |
| 35 | 33 | struct drm_connector_helper_funcs; |
| 36 | 34 | struct drm_modeset_acquire_ctx; |
| 37 | 35 | struct drm_device; |
| @@ -214,7 +214,7 @@ struct drm_property_blob { · 213 unmodified lines | ||
| 214 | 214 | |
| 215 | 215 | struct drm_prop_enum_list { |
| 216 | 216 | int type; |
| 217 | − char *name; | |
| 217 | + const char *name; | |
| 218 | 218 | }; |
| 219 | 219 | |
| 220 | 220 | #define obj_to_property(x) container_of(x, struct drm_property, base) |