| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Generic LVDS panel driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * Copyright (C) 2016 Renesas Electronics Corporation |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 11 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 12 | | - * (at your option) any later version. |
|---|
| 13 | 9 | */ |
|---|
| 14 | 10 | |
|---|
| 15 | | -#include <linux/backlight.h> |
|---|
| 16 | 11 | #include <linux/gpio/consumer.h> |
|---|
| 17 | 12 | #include <linux/module.h> |
|---|
| 18 | 13 | #include <linux/of_platform.h> |
|---|
| .. | .. |
|---|
| 20 | 15 | #include <linux/regulator/consumer.h> |
|---|
| 21 | 16 | #include <linux/slab.h> |
|---|
| 22 | 17 | |
|---|
| 23 | | -#include <drm/drmP.h> |
|---|
| 24 | | -#include <drm/drm_crtc.h> |
|---|
| 25 | | -#include <drm/drm_panel.h> |
|---|
| 26 | | - |
|---|
| 27 | 18 | #include <video/display_timing.h> |
|---|
| 28 | 19 | #include <video/of_display_timing.h> |
|---|
| 29 | 20 | #include <video/videomode.h> |
|---|
| 21 | + |
|---|
| 22 | +#include <drm/drm_crtc.h> |
|---|
| 23 | +#include <drm/drm_panel.h> |
|---|
| 30 | 24 | |
|---|
| 31 | 25 | struct panel_lvds { |
|---|
| 32 | 26 | struct drm_panel panel; |
|---|
| .. | .. |
|---|
| 39 | 33 | unsigned int bus_format; |
|---|
| 40 | 34 | bool data_mirror; |
|---|
| 41 | 35 | |
|---|
| 42 | | - struct backlight_device *backlight; |
|---|
| 43 | 36 | struct regulator *supply; |
|---|
| 44 | 37 | |
|---|
| 45 | 38 | struct gpio_desc *enable_gpio; |
|---|
| 46 | 39 | struct gpio_desc *reset_gpio; |
|---|
| 40 | + |
|---|
| 41 | + enum drm_panel_orientation orientation; |
|---|
| 47 | 42 | }; |
|---|
| 48 | 43 | |
|---|
| 49 | 44 | static inline struct panel_lvds *to_panel_lvds(struct drm_panel *panel) |
|---|
| 50 | 45 | { |
|---|
| 51 | 46 | return container_of(panel, struct panel_lvds, panel); |
|---|
| 52 | | -} |
|---|
| 53 | | - |
|---|
| 54 | | -static int panel_lvds_disable(struct drm_panel *panel) |
|---|
| 55 | | -{ |
|---|
| 56 | | - struct panel_lvds *lvds = to_panel_lvds(panel); |
|---|
| 57 | | - |
|---|
| 58 | | - if (lvds->backlight) { |
|---|
| 59 | | - lvds->backlight->props.power = FB_BLANK_POWERDOWN; |
|---|
| 60 | | - lvds->backlight->props.state |= BL_CORE_FBBLANK; |
|---|
| 61 | | - backlight_update_status(lvds->backlight); |
|---|
| 62 | | - } |
|---|
| 63 | | - |
|---|
| 64 | | - return 0; |
|---|
| 65 | 47 | } |
|---|
| 66 | 48 | |
|---|
| 67 | 49 | static int panel_lvds_unprepare(struct drm_panel *panel) |
|---|
| .. | .. |
|---|
| 98 | 80 | return 0; |
|---|
| 99 | 81 | } |
|---|
| 100 | 82 | |
|---|
| 101 | | -static int panel_lvds_enable(struct drm_panel *panel) |
|---|
| 83 | +static int panel_lvds_get_modes(struct drm_panel *panel, |
|---|
| 84 | + struct drm_connector *connector) |
|---|
| 102 | 85 | { |
|---|
| 103 | 86 | struct panel_lvds *lvds = to_panel_lvds(panel); |
|---|
| 104 | | - |
|---|
| 105 | | - if (lvds->backlight) { |
|---|
| 106 | | - lvds->backlight->props.state &= ~BL_CORE_FBBLANK; |
|---|
| 107 | | - lvds->backlight->props.power = FB_BLANK_UNBLANK; |
|---|
| 108 | | - backlight_update_status(lvds->backlight); |
|---|
| 109 | | - } |
|---|
| 110 | | - |
|---|
| 111 | | - return 0; |
|---|
| 112 | | -} |
|---|
| 113 | | - |
|---|
| 114 | | -static int panel_lvds_get_modes(struct drm_panel *panel) |
|---|
| 115 | | -{ |
|---|
| 116 | | - struct panel_lvds *lvds = to_panel_lvds(panel); |
|---|
| 117 | | - struct drm_connector *connector = lvds->panel.connector; |
|---|
| 118 | 87 | struct drm_display_mode *mode; |
|---|
| 119 | 88 | |
|---|
| 120 | | - mode = drm_mode_create(lvds->panel.drm); |
|---|
| 89 | + mode = drm_mode_create(connector->dev); |
|---|
| 121 | 90 | if (!mode) |
|---|
| 122 | 91 | return 0; |
|---|
| 123 | 92 | |
|---|
| .. | .. |
|---|
| 132 | 101 | connector->display_info.bus_flags = lvds->data_mirror |
|---|
| 133 | 102 | ? DRM_BUS_FLAG_DATA_LSB_TO_MSB |
|---|
| 134 | 103 | : DRM_BUS_FLAG_DATA_MSB_TO_LSB; |
|---|
| 104 | + drm_connector_set_panel_orientation(connector, lvds->orientation); |
|---|
| 135 | 105 | |
|---|
| 136 | 106 | return 1; |
|---|
| 137 | 107 | } |
|---|
| 138 | 108 | |
|---|
| 139 | 109 | static const struct drm_panel_funcs panel_lvds_funcs = { |
|---|
| 140 | | - .disable = panel_lvds_disable, |
|---|
| 141 | 110 | .unprepare = panel_lvds_unprepare, |
|---|
| 142 | 111 | .prepare = panel_lvds_prepare, |
|---|
| 143 | | - .enable = panel_lvds_enable, |
|---|
| 144 | 112 | .get_modes = panel_lvds_get_modes, |
|---|
| 145 | 113 | }; |
|---|
| 146 | 114 | |
|---|
| .. | .. |
|---|
| 151 | 119 | const char *mapping; |
|---|
| 152 | 120 | int ret; |
|---|
| 153 | 121 | |
|---|
| 154 | | - ret = of_get_display_timing(np, "panel-timing", &timing); |
|---|
| 155 | | - if (ret < 0) |
|---|
| 122 | + ret = of_drm_get_panel_orientation(np, &lvds->orientation); |
|---|
| 123 | + if (ret < 0) { |
|---|
| 124 | + dev_err(lvds->dev, "%pOF: failed to get orientation %d\n", np, ret); |
|---|
| 156 | 125 | return ret; |
|---|
| 126 | + } |
|---|
| 127 | + |
|---|
| 128 | + ret = of_get_display_timing(np, "panel-timing", &timing); |
|---|
| 129 | + if (ret < 0) { |
|---|
| 130 | + dev_err(lvds->dev, "%pOF: problems parsing panel-timing (%d)\n", |
|---|
| 131 | + np, ret); |
|---|
| 132 | + return ret; |
|---|
| 133 | + } |
|---|
| 157 | 134 | |
|---|
| 158 | 135 | videomode_from_timing(&timing, &lvds->video_mode); |
|---|
| 159 | 136 | |
|---|
| .. | .. |
|---|
| 244 | 221 | return ret; |
|---|
| 245 | 222 | } |
|---|
| 246 | 223 | |
|---|
| 247 | | - lvds->backlight = devm_of_find_backlight(lvds->dev); |
|---|
| 248 | | - if (IS_ERR(lvds->backlight)) |
|---|
| 249 | | - return PTR_ERR(lvds->backlight); |
|---|
| 250 | | - |
|---|
| 251 | 224 | /* |
|---|
| 252 | 225 | * TODO: Handle all power supplies specified in the DT node in a generic |
|---|
| 253 | 226 | * way for panels that don't care about power supply ordering. LVDS |
|---|
| .. | .. |
|---|
| 256 | 229 | */ |
|---|
| 257 | 230 | |
|---|
| 258 | 231 | /* Register the panel. */ |
|---|
| 259 | | - drm_panel_init(&lvds->panel); |
|---|
| 260 | | - lvds->panel.dev = lvds->dev; |
|---|
| 261 | | - lvds->panel.funcs = &panel_lvds_funcs; |
|---|
| 232 | + drm_panel_init(&lvds->panel, lvds->dev, &panel_lvds_funcs, |
|---|
| 233 | + DRM_MODE_CONNECTOR_LVDS); |
|---|
| 262 | 234 | |
|---|
| 263 | | - ret = drm_panel_add(&lvds->panel); |
|---|
| 264 | | - if (ret < 0) |
|---|
| 235 | + ret = drm_panel_of_backlight(&lvds->panel); |
|---|
| 236 | + if (ret) |
|---|
| 265 | 237 | return ret; |
|---|
| 238 | + |
|---|
| 239 | + drm_panel_add(&lvds->panel); |
|---|
| 266 | 240 | |
|---|
| 267 | 241 | dev_set_drvdata(lvds->dev, lvds); |
|---|
| 268 | 242 | return 0; |
|---|
| .. | .. |
|---|
| 274 | 248 | |
|---|
| 275 | 249 | drm_panel_remove(&lvds->panel); |
|---|
| 276 | 250 | |
|---|
| 277 | | - panel_lvds_disable(&lvds->panel); |
|---|
| 251 | + drm_panel_disable(&lvds->panel); |
|---|
| 278 | 252 | |
|---|
| 279 | 253 | return 0; |
|---|
| 280 | 254 | } |
|---|