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 | Samsung S6E3HA2 5.7" 1440x2560 AMOLED panel | 1 | Samsung S6E3HA2 5.7" 1440x2560 AMOLED panel |
| 2 | Samsung S6E3HF2 5.65" 1600x2560 AMOLED panel | ||
| 2 | 3 | ||
| 3 | Required properties: | 4 | Required properties: |
| 4 | - compatible: "samsung,s6e3ha2" | 5 | - compatible: should be one of: |
| 6 | "samsung,s6e3ha2", | ||
| 7 | "samsung,s6e3hf2". | ||
| 5 | - reg: the virtual channel number of a DSI peripheral | 8 | - reg: the virtual channel number of a DSI peripheral |
| 6 | - vdd3-supply: I/O voltage supply | 9 | - vdd3-supply: I/O voltage supply |
| 7 | - vci-supply: voltage supply for analog circuits | 10 | - vci-supply: voltage supply for analog circuits |
| @@ -219,6 +219,7 @@ nexbox Nexbox · 218 unmodified lines | |||
| 219 | newhaven Newhaven Display International | 219 | newhaven Newhaven Display International |
| 220 | ni National Instruments | 220 | ni National Instruments |
| 221 | nintendo Nintendo | 221 | nintendo Nintendo |
| 222 | nlt NLT Technologies, Ltd. | ||
| 222 | nokia Nokia | 223 | nokia Nokia |
| 223 | nordic Nordic Semiconductor | 224 | nordic Nordic Semiconductor |
| 224 | nuvoton Nuvoton Technology Corporation | 225 | nuvoton Nuvoton Technology Corporation |
| @@ -143,6 +143,12 @@ Bridge Helper Reference · 142 unmodified lines | |||
| 143 | .. kernel-doc:: drivers/gpu/drm/drm_bridge.c | 143 | .. kernel-doc:: drivers/gpu/drm/drm_bridge.c |
| 144 | :export: | 144 | :export: |
| 145 | 145 | ||
| 146 | Panel-Bridge Helper Reference | ||
| 147 | ----------------------------- | ||
| 148 | |||
| 149 | .. kernel-doc:: drivers/gpu/drm/bridge/panel.c | ||
| 150 | :export: | ||
| 151 | |||
| 146 | .. _drm_panel_helper: | 152 | .. _drm_panel_helper: |
| 147 | 153 | ||
| 148 | Panel Helper Reference | 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 | drm_simple_kms_helper.o drm_modeset_helper.o \ | 35 | drm_simple_kms_helper.o drm_modeset_helper.o \ |
| 36 | drm_scdc_helper.o | 36 | drm_scdc_helper.o |
| 37 | 37 | ||
| 38 | drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o | ||
| 38 | drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o | 39 | drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o |
| 39 | drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o | 40 | drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o |
| 40 | drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o | 41 | drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o |
| @@ -23,139 +23,17 @@ · 22 unmodified lines | |||
| 23 | 23 | ||
| 24 | #include <drm/drmP.h> | 24 | #include <drm/drmP.h> |
| 25 | #include <drm/drm_of.h> | 25 | #include <drm/drm_of.h> |
| 26 | #include <drm/drm_bridge.h> | ||
| 26 | 27 | ||
| 27 | #include "atmel_hlcdc_dc.h" | 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 | static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { | 30 | static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { |
| 93 | .destroy = atmel_hlcdc_rgb_encoder_destroy, | 31 | .destroy = drm_encoder_cleanup, |
| 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, | ||
| 153 | }; | 32 | }; |
| 154 | 33 | ||
| 155 | static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) | 34 | static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) |
| 156 | { | 35 | { |
| 157 | struct atmel_hlcdc_dc *dc = dev->dev_private; | 36 | struct drm_encoder *encoder; |
| 158 | struct atmel_hlcdc_rgb_output *output; | ||
| 159 | struct drm_panel *panel; | 37 | struct drm_panel *panel; |
| 160 | struct drm_bridge *bridge; | 38 | struct drm_bridge *bridge; |
| 161 | int ret; | 39 | int ret; |
| @@ -165,55 +43,34 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) · 3 unmodified lines | |||
| 165 | if (ret) | 43 | if (ret) |
| 166 | return ret; | 44 | return ret; |
| 167 | 45 | ||
| 168 | output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); | 46 | encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL); |
| 169 | if (!output) | 47 | if (!encoder) |
| 170 | return -EINVAL; | 48 | return -EINVAL; |
| 171 | 49 | ||
| 172 | output->dc = dc; | 50 | ret = drm_encoder_init(dev, encoder, |
| 173 | |||
| 174 | drm_encoder_helper_add(&output->encoder, | ||
| 175 | &atmel_hlcdc_panel_encoder_helper_funcs); | ||
| 176 | ret = drm_encoder_init(dev, &output->encoder, | ||
| 177 | &atmel_hlcdc_panel_encoder_funcs, | 51 | &atmel_hlcdc_panel_encoder_funcs, |
| 178 | DRM_MODE_ENCODER_NONE, NULL); | 52 | DRM_MODE_ENCODER_NONE, NULL); |
| 179 | if (ret) | 53 | if (ret) |
| 180 | return ret; | 54 | return ret; |
| 181 | 55 | ||
| 182 | output->encoder.possible_crtcs = 0x1; | 56 | encoder->possible_crtcs = 0x1; |
| 183 | 57 | ||
| 184 | if (panel) { | 58 | if (panel) { |
| 185 | output->connector.dpms = DRM_MODE_DPMS_OFF; | 59 | bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); |
| 186 | output->connector.polled = DRM_CONNECTOR_POLL_CONNECT; | 60 | if (IS_ERR(bridge)) |
| 187 | drm_connector_helper_add(&output->connector, | 61 | return PTR_ERR(bridge); |
| 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; | ||
| 207 | } | 62 | } |
| 208 | 63 | ||
| 209 | if (bridge) { | 64 | if (bridge) { |
| 210 | ret = drm_bridge_attach(&output->encoder, bridge, NULL); | 65 | ret = drm_bridge_attach(encoder, bridge, NULL); |
| 211 | if (!ret) | 66 | if (!ret) |
| 212 | return 0; | 67 | return 0; |
| 68 | |||
| 69 | if (panel) | ||
| 70 | drm_panel_bridge_remove(bridge); | ||
| 213 | } | 71 | } |
| 214 | 72 | ||
| 215 | err_encoder_cleanup: | 73 | drm_encoder_cleanup(encoder); |
| 216 | drm_encoder_cleanup(&output->encoder); | ||
| 217 | 74 | ||
| 218 | return ret; | 75 | return ret; |
| 219 | } | 76 | } |
| @@ -4,6 +4,14 @@ config DRM_BRIDGE · 3 unmodified lines | |||
| 4 | help | 4 | help |
| 5 | Bridge registration and lookup framework. | 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 | menu "Display Interface Bridges" | 15 | menu "Display Interface Bridges" |
| 8 | depends on DRM && DRM_BRIDGE | 16 | depends on DRM && DRM_BRIDGE |
| 9 | 17 | ||
| @@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC · 17 unmodified lines | |||
| 27 | config DRM_LVDS_ENCODER | 35 | config DRM_LVDS_ENCODER |
| 28 | tristate "Transparent parallel to LVDS encoder support" | 36 | tristate "Transparent parallel to LVDS encoder support" |
| 29 | depends on OF | 37 | depends on OF |
| 30 | select DRM_KMS_HELPER | 38 | select DRM_PANEL_BRIDGE |
| 31 | select DRM_PANEL | ||
| 32 | help | 39 | help |
| 33 | Support for transparent parallel to LVDS encoders that don't require | 40 | Support for transparent parallel to LVDS encoders that don't require |
| 34 | any configuration. | 41 | any configuration. |
| @@ -8,144 +8,18 @@ · 7 unmodified lines | |||
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <drm/drmP.h> | 10 | #include <drm/drmP.h> |
| 11 | #include <drm/drm_atomic_helper.h> | 11 | #include <drm/drm_bridge.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> | ||
| 16 | #include <drm/drm_panel.h> | 12 | #include <drm/drm_panel.h> |
| 17 | 13 | ||
| 18 | #include <linux/of_graph.h> | 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 | static int lvds_encoder_probe(struct platform_device *pdev) | 16 | static int lvds_encoder_probe(struct platform_device *pdev) |
| 134 | { | 17 | { |
| 135 | struct lvds_encoder *lvds; | ||
| 136 | struct device_node *port; | 18 | struct device_node *port; |
| 137 | struct device_node *endpoint; | 19 | struct device_node *endpoint; |
| 138 | struct device_node *panel; | 20 | struct device_node *panel_node; |
| 139 | 21 | struct drm_panel *panel; | |
| 140 | lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); | 22 | struct drm_bridge *bridge; |
| 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; | ||
| 149 | 23 | ||
| 150 | /* Locate the panel DT node. */ | 24 | /* Locate the panel DT node. */ |
| 151 | port = of_graph_get_port_by_id(pdev->dev.of_node, 1); | 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 | return -ENXIO; | 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 | of_node_put(endpoint); | 39 | of_node_put(endpoint); |
| 166 | if (!panel) { | 40 | if (!panel_node) { |
| 167 | dev_dbg(&pdev->dev, "no remote endpoint for port 1\n"); | 41 | dev_dbg(&pdev->dev, "no remote endpoint for port 1\n"); |
| 168 | return -ENXIO; | 42 | return -ENXIO; |
| 169 | } | 43 | } |
| 170 | 44 | ||
| 171 | lvds->panel = of_drm_find_panel(panel); | 45 | panel = of_drm_find_panel(panel_node); |
| 172 | of_node_put(panel); | 46 | of_node_put(panel_node); |
| 173 | if (!lvds->panel) { | 47 | if (!panel) { |
| 174 | dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); | 48 | dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); |
| 175 | return -EPROBE_DEFER; | 49 | return -EPROBE_DEFER; |
| 176 | } | 50 | } |
| 177 | 51 | ||
| 178 | /* Register the bridge. */ | 52 | bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS); |
| 179 | return drm_bridge_add(&lvds->bridge); | 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 | static int lvds_encoder_remove(struct platform_device *pdev) | 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 | return 0; | 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 | return ret; | 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 | static void dw_hdmi_connector_force(struct drm_connector *connector) | 1910 | static void dw_hdmi_connector_force(struct drm_connector *connector) |
| 1929 | { | 1911 | { |
| 1930 | struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, | 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 | static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { | 1933 | static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { |
| 1952 | .get_modes = dw_hdmi_connector_get_modes, | 1934 | .get_modes = dw_hdmi_connector_get_modes, |
| 1953 | .mode_valid = dw_hdmi_connector_mode_valid, | ||
| 1954 | .best_encoder = drm_atomic_helper_best_encoder, | 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 | return 0; | 1954 | return 0; |
| 1974 | } | 1955 | } |
| 1975 | 1956 | ||
| 1976 | static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, | 1957 | static enum drm_mode_status |
| 1977 | const struct drm_display_mode *orig_mode, | 1958 | dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge, |
| 1978 | struct drm_display_mode *mode) | 1959 | const struct drm_display_mode *mode) |
| 1979 | { | 1960 | { |
| 1980 | struct dw_hdmi *hdmi = bridge->driver_private; | 1961 | struct dw_hdmi *hdmi = bridge->driver_private; |
| 1981 | struct drm_connector *connector = &hdmi->connector; | 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); | 1965 | /* We don't support double-clocked modes */ |
| 1985 | if (status != MODE_OK) | 1966 | if (mode->flags & DRM_MODE_FLAG_DBLCLK) |
| 1986 | return false; | 1967 | return MODE_BAD; |
| 1987 | return true; | 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 | static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, | 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 | .enable = dw_hdmi_bridge_enable, | 2013 | .enable = dw_hdmi_bridge_enable, |
| 2029 | .disable = dw_hdmi_bridge_disable, | 2014 | .disable = dw_hdmi_bridge_disable, |
| 2030 | .mode_set = dw_hdmi_bridge_mode_set, | 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 | static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi) | 2019 | static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi) |
| @@ -109,9 +109,10 @@ struct drm_atomic_state * · 108 unmodified lines | |||
| 109 | drm_atomic_state_alloc(struct drm_device *dev) | 109 | drm_atomic_state_alloc(struct drm_device *dev) |
| 110 | { | 110 | { |
| 111 | struct drm_mode_config *config = &dev->mode_config; | 111 | struct drm_mode_config *config = &dev->mode_config; |
| 112 | struct drm_atomic_state *state; | ||
| 113 | 112 | ||
| 114 | if (!config->funcs->atomic_state_alloc) { | 113 | if (!config->funcs->atomic_state_alloc) { |
| 114 | struct drm_atomic_state *state; | ||
| 115 | |||
| 115 | state = kzalloc(sizeof(*state), GFP_KERNEL); | 116 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
| 116 | if (!state) | 117 | if (!state) |
| 117 | return NULL; | 118 | return NULL; |
| @@ -147,8 +147,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { · 146 unmodified lines | |||
| 147 | .destroy = drm_encoder_cleanup, | 147 | .destroy = drm_encoder_cleanup, |
| 148 | }; | 148 | }; |
| 149 | 149 | ||
| 150 | static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con, | 150 | static enum drm_mode_status |
| 151 | struct drm_display_mode *mode) | 151 | imx6q_hdmi_mode_valid(struct drm_connector *con, |
| 152 | const struct drm_display_mode *mode) | ||
| 152 | { | 153 | { |
| 153 | if (mode->clock < 13500) | 154 | if (mode->clock < 13500) |
| 154 | return MODE_CLOCK_LOW; | 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 | return MODE_OK; | 160 | return MODE_OK; |
| 160 | } | 161 | } |
| 161 | 162 | ||
| 162 | static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con, | 163 | static enum drm_mode_status |
| 163 | struct drm_display_mode *mode) | 164 | imx6dl_hdmi_mode_valid(struct drm_connector *con, |
| 165 | const struct drm_display_mode *mode) | ||
| 164 | { | 166 | { |
| 165 | if (mode->clock < 13500) | 167 | if (mode->clock < 13500) |
| 166 | return MODE_CLOCK_LOW; | 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 | /* TOFIX Enable support for non-vic modes */ | 538 | /* TOFIX Enable support for non-vic modes */ |
| 539 | static enum drm_mode_status dw_hdmi_mode_valid(struct drm_connector *connector, | 539 | static enum drm_mode_status |
| 540 | struct drm_display_mode *mode) | 540 | dw_hdmi_mode_valid(struct drm_connector *connector, |
| 541 | const struct drm_display_mode *mode) | ||
| 541 | { | 542 | { |
| 542 | unsigned int vclk_freq; | 543 | unsigned int vclk_freq; |
| 543 | unsigned int venc_freq; | 544 | unsigned int venc_freq; |
| @@ -35,6 +35,13 @@ · 34 unmodified lines | |||
| 35 | #include "mxsfb_drv.h" | 35 | #include "mxsfb_drv.h" |
| 36 | #include "mxsfb_regs.h" | 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 | static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) | 45 | static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) |
| 39 | { | 46 | { |
| 40 | return (val & mxsfb->devdata->hs_wdth_mask) << | 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 | clk_disable_unprepare(mxsfb->clk_disp_axi); | 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 | static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) | 199 | static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) |
| 163 | { | 200 | { |
| 164 | struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode; | 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 | mxsfb_enable_axi_clk(mxsfb); | 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 | /* Clear the FIFOs */ | 218 | /* Clear the FIFOs */ |
| 177 | writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET); | 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 | that it can be automatically turned off when the panel goes into a | 28 | that it can be automatically turned off when the panel goes into a |
| 29 | low power state. | 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 | config DRM_PANEL_JDI_LT070ME05000 | 42 | config DRM_PANEL_JDI_LT070ME05000 |
| 32 | tristate "JDI LT070ME05000 WUXGA DSI panel" | 43 | tristate "JDI LT070ME05000 WUXGA DSI panel" |
| 33 | depends on OF | 44 | depends on OF |
| @@ -66,6 +77,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA2 · 32 unmodified lines | |||
| 66 | tristate "Samsung S6E3HA2 DSI video mode panel" | 77 | tristate "Samsung S6E3HA2 DSI video mode panel" |
| 67 | depends on OF | 78 | depends on OF |
| 68 | depends on DRM_MIPI_DSI | 79 | depends on DRM_MIPI_DSI |
| 80 | depends on BACKLIGHT_CLASS_DEVICE | ||
| 69 | select VIDEOMODE_HELPERS | 81 | select VIDEOMODE_HELPERS |
| 70 | 82 | ||
| 71 | config DRM_PANEL_SAMSUNG_S6E8AA0 | 83 | config DRM_PANEL_SAMSUNG_S6E8AA0 |
| @@ -100,6 +112,7 @@ config DRM_PANEL_SHARP_LS043T1LE01 · 28 unmodified lines | |||
| 100 | config DRM_PANEL_SITRONIX_ST7789V | 112 | config DRM_PANEL_SITRONIX_ST7789V |
| 101 | tristate "Sitronix ST7789V panel" | 113 | tristate "Sitronix ST7789V panel" |
| 102 | depends on OF && SPI | 114 | depends on OF && SPI |
| 115 | depends on BACKLIGHT_CLASS_DEVICE | ||
| 103 | help | 116 | help |
| 104 | Say Y here if you want to enable support for the Sitronix | 117 | Say Y here if you want to enable support for the Sitronix |
| 105 | ST7789V controller for 240x320 LCD panels | 118 | ST7789V controller for 240x320 LCD panels |
| @@ -1,5 +1,6 @@ | |||
| 1 | obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o | 1 | obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o |
| 2 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o | 2 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o |
| 3 | obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o | ||
| 3 | obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o | 4 | obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o |
| 4 | obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o | 5 | obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o |
| 5 | obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o | 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 | #include <drm/drm_panel.h> | 16 | #include <drm/drm_panel.h> |
| 17 | #include <linux/backlight.h> | 17 | #include <linux/backlight.h> |
| 18 | #include <linux/gpio/consumer.h> | 18 | #include <linux/gpio/consumer.h> |
| 19 | #include <linux/of_device.h> | ||
| 19 | #include <linux/regulator/consumer.h> | 20 | #include <linux/regulator/consumer.h> |
| 20 | 21 | ||
| 21 | #define S6E3HA2_MIN_BRIGHTNESS 0 | 22 | #define S6E3HA2_MIN_BRIGHTNESS 0 |
| @@ -218,6 +219,16 @@ unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = { · 196 unmodified lines | |||
| 218 | 0x1d, 0x1e, 0x1f, 0x20, 0x21 | 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 | struct s6e3ha2 { | 232 | struct s6e3ha2 { |
| 222 | struct device *dev; | 233 | struct device *dev; |
| 223 | struct drm_panel panel; | 234 | struct drm_panel panel; |
| @@ -226,6 +237,8 @@ struct s6e3ha2 { · 2 unmodified lines | |||
| 226 | struct regulator_bulk_data supplies[2]; | 237 | struct regulator_bulk_data supplies[2]; |
| 227 | struct gpio_desc *reset_gpio; | 238 | struct gpio_desc *reset_gpio; |
| 228 | struct gpio_desc *enable_gpio; | 239 | struct gpio_desc *enable_gpio; |
| 240 | |||
| 241 | const struct s6e3ha2_panel_desc *desc; | ||
| 229 | }; | 242 | }; |
| 230 | 243 | ||
| 231 | static int s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len) | 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 | static int s6e3ha2_freq_calibration(struct s6e3ha2 *ctx) | 296 | static int s6e3ha2_freq_calibration(struct s6e3ha2 *ctx) |
| 284 | { | 297 | { |
| 285 | s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c); | 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 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39); | 301 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39); |
| 287 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0); | 302 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0); |
| 288 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20); | 303 | s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20); |
| 289 | s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62, 0x40, | 304 | |
| 290 | 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5); | 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 | return 0; | 314 | return 0; |
| 292 | } | 315 | } |
| 293 | 316 | ||
| @@ -583,7 +606,7 @@ static int s6e3ha2_enable(struct drm_panel *panel) · 289 unmodified lines | |||
| 583 | return 0; | 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 | .clock = 222372, | 610 | .clock = 222372, |
| 588 | .hdisplay = 1440, | 611 | .hdisplay = 1440, |
| 589 | .hsync_start = 1440 + 1, | 612 | .hsync_start = 1440 + 1, |
| @@ -597,16 +620,41 @@ static const struct drm_display_mode default_mode = { · 7 unmodified lines | |||
| 597 | .flags = 0, | 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 | static int s6e3ha2_get_modes(struct drm_panel *panel) | 647 | static int s6e3ha2_get_modes(struct drm_panel *panel) |
| 601 | { | 648 | { |
| 602 | struct drm_connector *connector = panel->connector; | 649 | struct drm_connector *connector = panel->connector; |
| 650 | struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel); | ||
| 603 | struct drm_display_mode *mode; | 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 | if (!mode) { | 654 | if (!mode) { |
| 607 | DRM_ERROR("failed to add mode %ux%ux@%u\n", | 655 | DRM_ERROR("failed to add mode %ux%ux@%u\n", |
| 608 | default_mode.hdisplay, default_mode.vdisplay, | 656 | ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay, |
| 609 | default_mode.vrefresh); | 657 | ctx->desc->mode->vrefresh); |
| 610 | return -ENOMEM; | 658 | return -ENOMEM; |
| 611 | } | 659 | } |
| 612 | 660 | ||
| @@ -642,6 +690,7 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) · 29 unmodified lines | |||
| 642 | mipi_dsi_set_drvdata(dsi, ctx); | 690 | mipi_dsi_set_drvdata(dsi, ctx); |
| 643 | 691 | ||
| 644 | ctx->dev = dev; | 692 | ctx->dev = dev; |
| 693 | ctx->desc = of_device_get_match_data(dev); | ||
| 645 | 694 | ||
| 646 | dsi->lanes = 4; | 695 | dsi->lanes = 4; |
| 647 | dsi->format = MIPI_DSI_FMT_RGB888; | 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 | static const struct of_device_id s6e3ha2_of_match[] = { | 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 | MODULE_DEVICE_TABLE(of, s6e3ha2_of_match); | 773 | MODULE_DEVICE_TABLE(of, s6e3ha2_of_match); |
| @@ -638,6 +638,34 @@ static const struct panel_desc auo_g185han01 = { · 637 unmodified lines | |||
| 638 | .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, | 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 | static const struct drm_display_mode auo_t215hvn01_mode = { | 669 | static const struct drm_display_mode auo_t215hvn01_mode = { |
| 642 | .clock = 148800, | 670 | .clock = 148800, |
| 643 | .hdisplay = 1920, | 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 | static const struct drm_display_mode nec_nl4827hc19_05b_mode = { | 1380 | static const struct drm_display_mode nec_nl4827hc19_05b_mode = { |
| 1326 | .clock = 10870, | 1381 | .clock = 10870, |
| 1327 | .hdisplay = 480, | 1382 | .hdisplay = 480, |
| @@ -1371,6 +1426,32 @@ static const struct panel_desc netron_dy_e231732 = { · 43 unmodified lines | |||
| 1371 | .bus_format = MEDIA_BUS_FMT_RGB666_1X18, | 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 | static const struct drm_display_mode nvd_9128_mode = { | 1455 | static const struct drm_display_mode nvd_9128_mode = { |
| 1375 | .clock = 29500, | 1456 | .clock = 29500, |
| 1376 | .hdisplay = 800, | 1457 | .hdisplay = 800, |
| @@ -1887,6 +1968,9 @@ static const struct of_device_id platform_of_match[] = { · 510 unmodified lines | |||
| 1887 | }, { | 1968 | }, { |
| 1888 | .compatible = "auo,g185han01", | 1969 | .compatible = "auo,g185han01", |
| 1889 | .data = &auo_g185han01, | 1970 | .data = &auo_g185han01, |
| 1971 | }, { | ||
| 1972 | .compatible = "auo,p320hvn03", | ||
| 1973 | .data = &auo_p320hvn03, | ||
| 1890 | }, { | 1974 | }, { |
| 1891 | .compatible = "auo,t215hvn01", | 1975 | .compatible = "auo,t215hvn01", |
| 1892 | .data = &auo_t215hvn01, | 1976 | .data = &auo_t215hvn01, |
| @@ -1971,12 +2055,18 @@ static const struct of_device_id platform_of_match[] = { · 78 unmodified lines | |||
| 1971 | }, { | 2055 | }, { |
| 1972 | .compatible = "lg,lp129qe", | 2056 | .compatible = "lg,lp129qe", |
| 1973 | .data = &lg_lp129qe, | 2057 | .data = &lg_lp129qe, |
| 2058 | }, { | ||
| 2059 | .compatible = "nec,nl12880bc20-05", | ||
| 2060 | .data = &nec_nl12880bc20_05, | ||
| 1974 | }, { | 2061 | }, { |
| 1975 | .compatible = "nec,nl4827hc19-05b", | 2062 | .compatible = "nec,nl4827hc19-05b", |
| 1976 | .data = &nec_nl4827hc19_05b, | 2063 | .data = &nec_nl4827hc19_05b, |
| 1977 | }, { | 2064 | }, { |
| 1978 | .compatible = "netron-dy,e231732", | 2065 | .compatible = "netron-dy,e231732", |
| 1979 | .data = &netron_dy_e231732, | 2066 | .data = &netron_dy_e231732, |
| 2067 | }, { | ||
| 2068 | .compatible = "nlt,nl192108ac18-02d", | ||
| 2069 | .data = &nlt_nl192108ac18_02d, | ||
| 1980 | }, { | 2070 | }, { |
| 1981 | .compatible = "nvd,9128", | 2071 | .compatible = "nvd,9128", |
| 1982 | .data = &nvd_9128, | 2072 | .data = &nvd_9128, |
| @@ -50,17 +50,6 @@ irqreturn_t pl111_irq(int irq, void *data) · 49 unmodified lines | |||
| 50 | return status; | 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 | static int pl111_display_check(struct drm_simple_display_pipe *pipe, | 53 | static int pl111_display_check(struct drm_simple_display_pipe *pipe, |
| 65 | struct drm_plane_state *pstate, | 54 | struct drm_plane_state *pstate, |
| 66 | struct drm_crtc_state *cstate) | 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 | return -EINVAL; | 62 | return -EINVAL; |
| 74 | 63 | ||
| 75 | if (fb) { | 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 | /* FB base address must be dword aligned. */ | 67 | /* FB base address must be dword aligned. */ |
| 79 | if (offset & 3) | 68 | if (offset & 3) |
| @@ -249,7 +238,7 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe, · 169 unmodified lines | |||
| 249 | struct drm_framebuffer *fb = pstate->fb; | 238 | struct drm_framebuffer *fb = pstate->fb; |
| 250 | 239 | ||
| 251 | if (fb) { | 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 | writel(addr, priv->regs + CLCD_UBAS); | 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 | static enum drm_mode_status | 156 | static enum drm_mode_status |
| 157 | dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, | 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 | const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; | 160 | const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; |
| 161 | int pclk = mode->clock * 1000; | 161 | int pclk = mode->clock * 1000; |
| @@ -7,7 +7,7 @@ config DRM_VC4 · 6 unmodified lines | |||
| 7 | select DRM_KMS_HELPER | 7 | select DRM_KMS_HELPER |
| 8 | select DRM_KMS_CMA_HELPER | 8 | select DRM_KMS_CMA_HELPER |
| 9 | select DRM_GEM_CMA_HELPER | 9 | select DRM_GEM_CMA_HELPER |
| 10 | select DRM_PANEL | 10 | select DRM_PANEL_BRIDGE |
| 11 | select SND_PCM | 11 | select SND_PCM |
| 12 | select SND_PCM_ELD | 12 | select SND_PCM_ELD |
| 13 | select SND_SOC_GENERIC_DMAENGINE_PCM | 13 | select SND_SOC_GENERIC_DMAENGINE_PCM |
| @@ -91,8 +91,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo) · 90 unmodified lines | |||
| 91 | vc4->bo_stats.num_allocated--; | 91 | vc4->bo_stats.num_allocated--; |
| 92 | vc4->bo_stats.size_allocated -= obj->size; | 92 | vc4->bo_stats.size_allocated -= obj->size; |
| 93 | 93 | ||
| 94 | if (bo->resv == &bo->_resv) | 94 | reservation_object_fini(&bo->_resv); |
| 95 | reservation_object_fini(bo->resv); | ||
| 96 | 95 | ||
| 97 | drm_gem_cma_free_object(obj); | 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 | vc4->bo_stats.num_allocated++; | 211 | vc4->bo_stats.num_allocated++; |
| 213 | vc4->bo_stats.size_allocated += size; | 212 | vc4->bo_stats.size_allocated += size; |
| 214 | mutex_unlock(&vc4->bo_lock); | 213 | mutex_unlock(&vc4->bo_lock); |
| 214 | bo->resv = &bo->_resv; | ||
| 215 | reservation_object_init(bo->resv); | ||
| 215 | 216 | ||
| 216 | return &bo->base.base; | 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 | return ERR_PTR(-ENOMEM); | 251 | return ERR_PTR(-ENOMEM); |
| 251 | } | 252 | } |
| 252 | } | 253 | } |
| 253 | bo = to_vc4_bo(&cma_obj->base); | 254 | return to_vc4_bo(&cma_obj->base); |
| 254 | |||
| 255 | bo->resv = &bo->_resv; | ||
| 256 | reservation_object_init(bo->resv); | ||
| 257 | |||
| 258 | return bo; | ||
| 259 | } | 255 | } |
| 260 | 256 | ||
| 261 | int vc4_dumb_create(struct drm_file *file_priv, | 257 | int vc4_dumb_create(struct drm_file *file_priv, |
| @@ -23,8 +23,10 @@ · 22 unmodified lines | |||
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | #include <drm/drm_atomic_helper.h> | 25 | #include <drm/drm_atomic_helper.h> |
| 26 | #include <drm/drm_bridge.h> | ||
| 26 | #include <drm/drm_crtc_helper.h> | 27 | #include <drm/drm_crtc_helper.h> |
| 27 | #include <drm/drm_edid.h> | 28 | #include <drm/drm_edid.h> |
| 29 | #include <drm/drm_of.h> | ||
| 28 | #include <drm/drm_panel.h> | 30 | #include <drm/drm_panel.h> |
| 29 | #include <linux/clk.h> | 31 | #include <linux/clk.h> |
| 30 | #include <linux/component.h> | 32 | #include <linux/component.h> |
| @@ -95,7 +97,8 @@ struct vc4_dpi { · 64 unmodified lines | |||
| 95 | 97 | ||
| 96 | struct drm_encoder *encoder; | 98 | struct drm_encoder *encoder; |
| 97 | struct drm_connector *connector; | 99 | struct drm_connector *connector; |
| 98 | struct drm_panel *panel; | 100 | struct drm_bridge *bridge; |
| 101 | bool is_panel_bridge; | ||
| 99 | 102 | ||
| 100 | void __iomem *regs; | 103 | void __iomem *regs; |
| 101 | 104 | ||
| @@ -118,24 +121,6 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder) · 16 unmodified lines | |||
| 118 | return container_of(encoder, struct vc4_dpi_encoder, base.base); | 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 | #define DPI_REG(reg) { reg, #reg } | 124 | #define DPI_REG(reg) { reg, #reg } |
| 140 | static const struct { | 125 | static const struct { |
| 141 | u32 reg; | 126 | u32 reg; |
| @@ -167,80 +152,6 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused) · 25 unmodified lines | |||
| 167 | } | 152 | } |
| 168 | #endif | 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 | static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { | 155 | static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { |
| 245 | .destroy = drm_encoder_cleanup, | 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 | struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder); | 161 | struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder); |
| 251 | struct vc4_dpi *dpi = vc4_encoder->dpi; | 162 | struct vc4_dpi *dpi = vc4_encoder->dpi; |
| 252 | 163 | ||
| 253 | drm_panel_disable(dpi->panel); | ||
| 254 | |||
| 255 | clk_disable_unprepare(dpi->pixel_clock); | 164 | clk_disable_unprepare(dpi->pixel_clock); |
| 256 | |||
| 257 | drm_panel_unprepare(dpi->panel); | ||
| 258 | } | 165 | } |
| 259 | 166 | ||
| 260 | static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) | 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 | u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE; | 172 | u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE; |
| 266 | int ret; | 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 | if (dpi->connector->display_info.num_bus_formats) { | 175 | if (dpi->connector->display_info.num_bus_formats) { |
| 275 | u32 bus_format = dpi->connector->display_info.bus_formats[0]; | 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 | ret = clk_prepare_enable(dpi->pixel_clock); | 222 | ret = clk_prepare_enable(dpi->pixel_clock); |
| 322 | if (ret) | 223 | if (ret) |
| 323 | DRM_ERROR("Failed to set clock rate: %d\n", ret); | 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 | static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder, | 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 | 248 | /* Sets up the next link in the display chain, whether it's a panel or |
| 355 | * up the panel. | 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; | 253 | struct device *dev = &dpi->pdev->dev; |
| 360 | struct device_node *np = dev->of_node; | ||
| 361 | struct drm_panel *panel; | 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 */ | 257 | ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, |
| 364 | panel_node = of_graph_get_remote_node(np, 0, 0); | 258 | &panel, &dpi->bridge); |
| 365 | if (!panel_node) | 259 | if (ret) { |
| 366 | return NULL; | 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); | 269 | if (panel) { |
| 369 | of_node_put(panel_node); | 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 | static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) | 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 | if (ret) | 326 | if (ret) |
| 423 | DRM_ERROR("Failed to turn on core clock: %d\n", ret); | 327 | DRM_ERROR("Failed to turn on core clock: %d\n", ret); |
| 424 | 328 | ||
| 425 | dpi->panel = vc4_dpi_get_panel(dev); | ||
| 426 | |||
| 427 | drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs, | 329 | drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs, |
| 428 | DRM_MODE_ENCODER_DPI, NULL); | 330 | DRM_MODE_ENCODER_DPI, NULL); |
| 429 | drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs); | 331 | drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs); |
| 430 | 332 | ||
| 431 | dpi->connector = vc4_dpi_connector_init(drm, dpi); | 333 | ret = vc4_dpi_init_bridge(dpi); |
| 432 | if (IS_ERR(dpi->connector)) { | 334 | if (ret) |
| 433 | ret = PTR_ERR(dpi->connector); | ||
| 434 | goto err_destroy_encoder; | 335 | goto err_destroy_encoder; |
| 435 | } | ||
| 436 | |||
| 437 | if (dpi->panel) | ||
| 438 | drm_panel_attach(dpi->panel, dpi->connector); | ||
| 439 | 336 | ||
| 440 | dev_set_drvdata(dev, dpi); | 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 | struct vc4_dev *vc4 = to_vc4_dev(drm); | 353 | struct vc4_dev *vc4 = to_vc4_dev(drm); |
| 457 | struct vc4_dpi *dpi = dev_get_drvdata(dev); | 354 | struct vc4_dpi *dpi = dev_get_drvdata(dev); |
| 458 | 355 | ||
| 459 | if (dpi->panel) | 356 | if (dpi->is_panel_bridge) |
| 460 | drm_panel_detach(dpi->panel); | 357 | drm_panel_bridge_remove(dpi->bridge); |
| 461 | 358 | ||
| 462 | vc4_dpi_connector_destroy(dpi->connector); | ||
| 463 | drm_encoder_cleanup(dpi->encoder); | 359 | drm_encoder_cleanup(dpi->encoder); |
| 464 | 360 | ||
| 465 | clk_disable_unprepare(dpi->core_clock); | 361 | clk_disable_unprepare(dpi->core_clock); |
| @@ -503,8 +503,8 @@ struct vc4_dsi { · 502 unmodified lines | |||
| 503 | 503 | ||
| 504 | struct mipi_dsi_host dsi_host; | 504 | struct mipi_dsi_host dsi_host; |
| 505 | struct drm_encoder *encoder; | 505 | struct drm_encoder *encoder; |
| 506 | struct drm_connector *connector; | 506 | struct drm_bridge *bridge; |
| 507 | struct drm_panel *panel; | 507 | bool is_panel_bridge; |
| 508 | 508 | ||
| 509 | void __iomem *regs; | 509 | void __iomem *regs; |
| 510 | 510 | ||
| @@ -605,18 +605,6 @@ to_vc4_dsi_encoder(struct drm_encoder *encoder) · 94 unmodified lines | |||
| 605 | return container_of(encoder, struct vc4_dsi_encoder, base.base); | 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 | #define DSI_REG(reg) { reg, #reg } | 608 | #define DSI_REG(reg) { reg, #reg } |
| 621 | static const struct { | 609 | static const struct { |
| 622 | u32 reg; | 610 | u32 reg; |
| @@ -724,79 +712,6 @@ int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) · 101 unmodified lines | |||
| 724 | } | 712 | } |
| 725 | #endif | 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 | static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) | 715 | static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) |
| 801 | { | 716 | { |
| 802 | drm_encoder_cleanup(encoder); | 717 | drm_encoder_cleanup(encoder); |
| @@ -894,12 +809,8 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) · 91 unmodified lines | |||
| 894 | struct vc4_dsi *dsi = vc4_encoder->dsi; | 809 | struct vc4_dsi *dsi = vc4_encoder->dsi; |
| 895 | struct device *dev = &dsi->pdev->dev; | 810 | struct device *dev = &dsi->pdev->dev; |
| 896 | 811 | ||
| 897 | drm_panel_disable(dsi->panel); | ||
| 898 | |||
| 899 | vc4_dsi_ulps(dsi, true); | 812 | vc4_dsi_ulps(dsi, true); |
| 900 | 813 | ||
| 901 | drm_panel_unprepare(dsi->panel); | ||
| 902 | |||
| 903 | clk_disable_unprepare(dsi->pll_phy_clock); | 814 | clk_disable_unprepare(dsi->pll_phy_clock); |
| 904 | clk_disable_unprepare(dsi->escape_clock); | 815 | clk_disable_unprepare(dsi->escape_clock); |
| 905 | clk_disable_unprepare(dsi->pixel_clock); | 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 | return; | 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 | if (debug_dump_regs) { | 898 | if (debug_dump_regs) { |
| 994 | DRM_INFO("DSI regs before:\n"); | 899 | DRM_INFO("DSI regs before:\n"); |
| 995 | vc4_dsi_dump_regs(dsi); | 900 | vc4_dsi_dump_regs(dsi); |
| @@ -1211,13 +1116,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) · 215 unmodified lines | |||
| 1211 | DRM_INFO("DSI regs after:\n"); | 1116 | DRM_INFO("DSI regs after:\n"); |
| 1212 | vc4_dsi_dump_regs(dsi); | 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 | static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, | 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 | return 0; | 1313 | return 0; |
| 1416 | } | 1314 | } |
| 1417 | 1315 | ||
| 1418 | dsi->panel = of_drm_find_panel(device->dev.of_node); | 1316 | dsi->bridge = of_drm_find_bridge(device->dev.of_node); |
| 1419 | if (!dsi->panel) | 1317 | if (!dsi->bridge) { |
| 1420 | return 0; | 1318 | struct drm_panel *panel = |
| 1421 | 1319 | of_drm_find_panel(device->dev.of_node); | |
| 1422 | ret = drm_panel_attach(dsi->panel, dsi->connector); | ||
| 1423 | if (ret != 0) | ||
| 1424 | return ret; | ||
| 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 | static int vc4_dsi_host_detach(struct mipi_dsi_host *host, | 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 | struct vc4_dsi *dsi = host_to_dsi(host); | 1337 | struct vc4_dsi *dsi = host_to_dsi(host); |
| 1435 | 1338 | ||
| 1436 | if (dsi->panel) { | 1339 | if (dsi->is_panel_bridge) { |
| 1437 | int ret = drm_panel_detach(dsi->panel); | 1340 | drm_panel_bridge_remove(dsi->bridge); |
| 1438 | 1341 | dsi->bridge = NULL; | |
| 1439 | if (ret) | ||
| 1440 | return ret; | ||
| 1441 | |||
| 1442 | dsi->panel = NULL; | ||
| 1443 | |||
| 1444 | drm_helper_hpd_irq_event(dsi->connector->dev); | ||
| 1445 | } | 1342 | } |
| 1446 | 1343 | ||
| 1447 | return 0; | 1344 | return 0; |
| @@ -1708,12 +1605,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) · 260 unmodified lines | |||
| 1708 | DRM_MODE_ENCODER_DSI, NULL); | 1605 | DRM_MODE_ENCODER_DSI, NULL); |
| 1709 | drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); | 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 | dsi->dsi_host.ops = &vc4_dsi_host_ops; | 1608 | dsi->dsi_host.ops = &vc4_dsi_host_ops; |
| 1718 | dsi->dsi_host.dev = dev; | 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 | pm_runtime_enable(dev); | 1615 | pm_runtime_enable(dev); |
| 1725 | 1616 | ||
| 1726 | return 0; | 1617 | return 0; |
| 1727 | |||
| 1728 | err_destroy_encoder: | ||
| 1729 | vc4_dsi_encoder_destroy(dsi->encoder); | ||
| 1730 | |||
| 1731 | return ret; | ||
| 1732 | } | 1618 | } |
| 1733 | 1619 | ||
| 1734 | static void vc4_dsi_unbind(struct device *dev, struct device *master, | 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 | pm_runtime_disable(dev); | 1627 | pm_runtime_disable(dev); |
| 1742 | 1628 | ||
| 1743 | vc4_dsi_connector_destroy(dsi->connector); | 1629 | drm_bridge_remove(dsi->bridge); |
| 1744 | vc4_dsi_encoder_destroy(dsi->encoder); | 1630 | vc4_dsi_encoder_destroy(dsi->encoder); |
| 1745 | 1631 | ||
| 1746 | mipi_dsi_host_unregister(&dsi->dsi_host); | 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 | &handle); | 111 | &handle); |
| 112 | 112 | ||
| 113 | if (ret) { | 113 | if (ret) { |
| 114 | state->bo_count = i - 1; | 114 | state->bo_count = i; |
| 115 | goto err; | 115 | goto err_delete_handle; |
| 116 | } | 116 | } |
| 117 | bo_state[i].handle = handle; | 117 | bo_state[i].handle = handle; |
| 118 | bo_state[i].paddr = vc4_bo->base.paddr; | 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 | state->bo_count * sizeof(*bo_state))) | 124 | state->bo_count * sizeof(*bo_state))) |
| 125 | ret = -EFAULT; | 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 | err_free: | 133 | err_free: |
| 130 | |||
| 131 | vc4_free_hang_state(dev, kernel_state); | 134 | vc4_free_hang_state(dev, kernel_state); |
| 135 | kfree(bo_state); | ||
| 132 | 136 | ||
| 133 | err: | ||
| 134 | return ret; | 137 | return ret; |
| 135 | } | 138 | } |
| 136 | 139 | ||
| @@ -196,7 +196,7 @@ static int zx_drm_probe(struct platform_device *pdev) · 195 unmodified lines | |||
| 196 | struct component_match *match = NULL; | 196 | struct component_match *match = NULL; |
| 197 | int ret; | 197 | int ret; |
| 198 | 198 | ||
| 199 | ret = of_platform_populate(parent, NULL, NULL, dev); | 199 | ret = devm_of_platform_populate(dev); |
| 200 | if (ret) | 200 | if (ret) |
| 201 | return ret; | 201 | return ret; |
| 202 | 202 | ||
| @@ -125,7 +125,7 @@ struct dw_hdmi_phy_ops { · 124 unmodified lines | |||
| 125 | struct dw_hdmi_plat_data { | 125 | struct dw_hdmi_plat_data { |
| 126 | struct regmap *regm; | 126 | struct regmap *regm; |
| 127 | enum drm_mode_status (*mode_valid)(struct drm_connector *connector, | 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 | unsigned long input_bus_format; | 129 | unsigned long input_bus_format; |
| 130 | unsigned long input_bus_encoding; | 130 | unsigned long input_bus_encoding; |
| 131 | 131 | ||
| @@ -29,6 +29,7 @@ · 28 unmodified lines | |||
| 29 | #include <drm/drm_modes.h> | 29 | #include <drm/drm_modes.h> |
| 30 | 30 | ||
| 31 | struct drm_bridge; | 31 | struct drm_bridge; |
| 32 | struct drm_panel; | ||
| 32 | 33 | ||
| 33 | /** | 34 | /** |
| 34 | * struct drm_bridge_funcs - drm_bridge control functions | 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 | void drm_bridge_pre_enable(struct drm_bridge *bridge); | 264 | void drm_bridge_pre_enable(struct drm_bridge *bridge); |
| 264 | void drm_bridge_enable(struct drm_bridge *bridge); | 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 | #endif | 273 | #endif |
| @@ -30,8 +30,6 @@ · 29 unmodified lines | |||
| 30 | 30 | ||
| 31 | #include <uapi/drm/drm_mode.h> | 31 | #include <uapi/drm/drm_mode.h> |
| 32 | 32 | ||
| 33 | struct drm_device; | ||
| 34 | |||
| 35 | struct drm_connector_helper_funcs; | 33 | struct drm_connector_helper_funcs; |
| 36 | struct drm_modeset_acquire_ctx; | 34 | struct drm_modeset_acquire_ctx; |
| 37 | struct drm_device; | 35 | struct drm_device; |
| @@ -214,7 +214,7 @@ struct drm_property_blob { · 213 unmodified lines | |||
| 214 | 214 | ||
| 215 | struct drm_prop_enum_list { | 215 | struct drm_prop_enum_list { |
| 216 | int type; | 216 | int type; |
| 217 | char *name; | 217 | const char *name; |
| 218 | }; | 218 | }; |
| 219 | 219 | ||
| 220 | #define obj_to_property(x) container_of(x, struct drm_property, base) | 220 | #define obj_to_property(x) container_of(x, struct drm_property, base) |