linux/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2018-2019, Bridge Systems BV
   4 * Copyright (C) 2018-2019, Bootlin
   5 * Copyright (C) 2017, Free Electrons
   6 *
   7 * This file based on panel-ilitek-ili9881c.c
   8 */
   9
  10#include <linux/delay.h>
  11#include <linux/device.h>
  12#include <linux/err.h>
  13#include <linux/errno.h>
  14#include <linux/fb.h>
  15#include <linux/kernel.h>
  16#include <linux/media-bus-format.h>
  17#include <linux/module.h>
  18
  19#include <linux/gpio/consumer.h>
  20#include <linux/regulator/consumer.h>
  21
  22#include <drm/drm_connector.h>
  23#include <drm/drm_mipi_dsi.h>
  24#include <drm/drm_modes.h>
  25#include <drm/drm_panel.h>
  26
  27struct rb070d30_panel {
  28        struct drm_panel panel;
  29        struct mipi_dsi_device *dsi;
  30        struct regulator *supply;
  31
  32        struct {
  33                struct gpio_desc *power;
  34                struct gpio_desc *reset;
  35                struct gpio_desc *updn;
  36                struct gpio_desc *shlr;
  37        } gpios;
  38};
  39
  40static inline struct rb070d30_panel *panel_to_rb070d30_panel(struct drm_panel *panel)
  41{
  42        return container_of(panel, struct rb070d30_panel, panel);
  43}
  44
  45static int rb070d30_panel_prepare(struct drm_panel *panel)
  46{
  47        struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
  48        int ret;
  49
  50        ret = regulator_enable(ctx->supply);
  51        if (ret < 0) {
  52                dev_err(&ctx->dsi->dev, "Failed to enable supply: %d\n", ret);
  53                return ret;
  54        }
  55
  56        msleep(20);
  57        gpiod_set_value(ctx->gpios.power, 1);
  58        msleep(20);
  59        gpiod_set_value(ctx->gpios.reset, 1);
  60        msleep(20);
  61        return 0;
  62}
  63
  64static int rb070d30_panel_unprepare(struct drm_panel *panel)
  65{
  66        struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
  67
  68        gpiod_set_value(ctx->gpios.reset, 0);
  69        gpiod_set_value(ctx->gpios.power, 0);
  70        regulator_disable(ctx->supply);
  71
  72        return 0;
  73}
  74
  75static int rb070d30_panel_enable(struct drm_panel *panel)
  76{
  77        struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
  78
  79        return mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
  80}
  81
  82static int rb070d30_panel_disable(struct drm_panel *panel)
  83{
  84        struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
  85
  86        return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
  87}
  88
  89/* Default timings */
  90static const struct drm_display_mode default_mode = {
  91        .clock          = 51206,
  92        .hdisplay       = 1024,
  93        .hsync_start    = 1024 + 160,
  94        .hsync_end      = 1024 + 160 + 80,
  95        .htotal         = 1024 + 160 + 80 + 80,
  96        .vdisplay       = 600,
  97        .vsync_start    = 600 + 12,
  98        .vsync_end      = 600 + 12 + 10,
  99        .vtotal         = 600 + 12 + 10 + 13,
 100
 101        .width_mm       = 154,
 102        .height_mm      = 85,
 103};
 104
 105static int rb070d30_panel_get_modes(struct drm_panel *panel,
 106                                    struct drm_connector *connector)
 107{
 108        struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
 109        struct drm_display_mode *mode;
 110        static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 111
 112        mode = drm_mode_duplicate(connector->dev, &default_mode);
 113        if (!mode) {
 114                dev_err(&ctx->dsi->dev, "Failed to add mode " DRM_MODE_FMT "\n",
 115                        DRM_MODE_ARG(&default_mode));
 116                return -EINVAL;
 117        }
 118
 119        drm_mode_set_name(mode);
 120
 121        mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 122        drm_mode_probed_add(connector, mode);
 123
 124        connector->display_info.bpc = 8;
 125        connector->display_info.width_mm = mode->width_mm;
 126        connector->display_info.height_mm = mode->height_mm;
 127        drm_display_info_set_bus_formats(&connector->display_info,
 128                                         &bus_format, 1);
 129
 130        return 1;
 131}
 132
 133static const struct drm_panel_funcs rb070d30_panel_funcs = {
 134        .get_modes      = rb070d30_panel_get_modes,
 135        .prepare        = rb070d30_panel_prepare,
 136        .enable         = rb070d30_panel_enable,
 137        .disable        = rb070d30_panel_disable,
 138        .unprepare      = rb070d30_panel_unprepare,
 139};
 140
 141static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi)
 142{
 143        struct rb070d30_panel *ctx;
 144        int ret;
 145
 146        ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
 147        if (!ctx)
 148                return -ENOMEM;
 149
 150        ctx->supply = devm_regulator_get(&dsi->dev, "vcc-lcd");
 151        if (IS_ERR(ctx->supply))
 152                return PTR_ERR(ctx->supply);
 153
 154        mipi_dsi_set_drvdata(dsi, ctx);
 155        ctx->dsi = dsi;
 156
 157        drm_panel_init(&ctx->panel, &dsi->dev, &rb070d30_panel_funcs,
 158                       DRM_MODE_CONNECTOR_DSI);
 159
 160        ctx->gpios.reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
 161        if (IS_ERR(ctx->gpios.reset)) {
 162                dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
 163                return PTR_ERR(ctx->gpios.reset);
 164        }
 165
 166        ctx->gpios.power = devm_gpiod_get(&dsi->dev, "power", GPIOD_OUT_LOW);
 167        if (IS_ERR(ctx->gpios.power)) {
 168                dev_err(&dsi->dev, "Couldn't get our power GPIO\n");
 169                return PTR_ERR(ctx->gpios.power);
 170        }
 171
 172        /*
 173         * We don't change the state of that GPIO later on but we need
 174         * to force it into a low state.
 175         */
 176        ctx->gpios.updn = devm_gpiod_get(&dsi->dev, "updn", GPIOD_OUT_LOW);
 177        if (IS_ERR(ctx->gpios.updn)) {
 178                dev_err(&dsi->dev, "Couldn't get our updn GPIO\n");
 179                return PTR_ERR(ctx->gpios.updn);
 180        }
 181
 182        /*
 183         * We don't change the state of that GPIO later on but we need
 184         * to force it into a low state.
 185         */
 186        ctx->gpios.shlr = devm_gpiod_get(&dsi->dev, "shlr", GPIOD_OUT_LOW);
 187        if (IS_ERR(ctx->gpios.shlr)) {
 188                dev_err(&dsi->dev, "Couldn't get our shlr GPIO\n");
 189                return PTR_ERR(ctx->gpios.shlr);
 190        }
 191
 192        ret = drm_panel_of_backlight(&ctx->panel);
 193        if (ret)
 194                return ret;
 195
 196        drm_panel_add(&ctx->panel);
 197
 198        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
 199        dsi->format = MIPI_DSI_FMT_RGB888;
 200        dsi->lanes = 4;
 201
 202        return mipi_dsi_attach(dsi);
 203}
 204
 205static int rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi)
 206{
 207        struct rb070d30_panel *ctx = mipi_dsi_get_drvdata(dsi);
 208
 209        mipi_dsi_detach(dsi);
 210        drm_panel_remove(&ctx->panel);
 211
 212        return 0;
 213}
 214
 215static const struct of_device_id rb070d30_panel_of_match[] = {
 216        { .compatible = "ronbo,rb070d30" },
 217        { /* sentinel */ },
 218};
 219MODULE_DEVICE_TABLE(of, rb070d30_panel_of_match);
 220
 221static struct mipi_dsi_driver rb070d30_panel_driver = {
 222        .probe = rb070d30_panel_dsi_probe,
 223        .remove = rb070d30_panel_dsi_remove,
 224        .driver = {
 225                .name = "panel-ronbo-rb070d30",
 226                .of_match_table = rb070d30_panel_of_match,
 227        },
 228};
 229module_mipi_dsi_driver(rb070d30_panel_driver);
 230
 231MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>");
 232MODULE_AUTHOR("Konstantin Sudakov <k.sudakov@integrasources.com>");
 233MODULE_DESCRIPTION("Ronbo RB070D30 Panel Driver");
 234MODULE_LICENSE("GPL");
 235