linux/drivers/gpu/drm/bridge/adv7511/adv7533.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/of_graph.h>
   7
   8#include "adv7511.h"
   9
  10static const struct reg_sequence adv7533_fixed_registers[] = {
  11        { 0x16, 0x20 },
  12        { 0x9a, 0xe0 },
  13        { 0xba, 0x70 },
  14        { 0xde, 0x82 },
  15        { 0xe4, 0x40 },
  16        { 0xe5, 0x80 },
  17};
  18
  19static const struct reg_sequence adv7533_cec_fixed_registers[] = {
  20        { 0x15, 0xd0 },
  21        { 0x17, 0xd0 },
  22        { 0x24, 0x20 },
  23        { 0x57, 0x11 },
  24        { 0x05, 0xc8 },
  25};
  26
  27static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
  28{
  29        struct mipi_dsi_device *dsi = adv->dsi;
  30        struct drm_display_mode *mode = &adv->curr_mode;
  31        unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
  32        u8 clock_div_by_lanes[] = { 6, 4, 3 };  /* 2, 3, 4 lanes */
  33
  34        hsw = mode->hsync_end - mode->hsync_start;
  35        hfp = mode->hsync_start - mode->hdisplay;
  36        hbp = mode->htotal - mode->hsync_end;
  37        vsw = mode->vsync_end - mode->vsync_start;
  38        vfp = mode->vsync_start - mode->vdisplay;
  39        vbp = mode->vtotal - mode->vsync_end;
  40
  41        /* set pixel clock divider mode */
  42        regmap_write(adv->regmap_cec, 0x16,
  43                     clock_div_by_lanes[dsi->lanes - 2] << 3);
  44
  45        /* horizontal porch params */
  46        regmap_write(adv->regmap_cec, 0x28, mode->htotal >> 4);
  47        regmap_write(adv->regmap_cec, 0x29, (mode->htotal << 4) & 0xff);
  48        regmap_write(adv->regmap_cec, 0x2a, hsw >> 4);
  49        regmap_write(adv->regmap_cec, 0x2b, (hsw << 4) & 0xff);
  50        regmap_write(adv->regmap_cec, 0x2c, hfp >> 4);
  51        regmap_write(adv->regmap_cec, 0x2d, (hfp << 4) & 0xff);
  52        regmap_write(adv->regmap_cec, 0x2e, hbp >> 4);
  53        regmap_write(adv->regmap_cec, 0x2f, (hbp << 4) & 0xff);
  54
  55        /* vertical porch params */
  56        regmap_write(adv->regmap_cec, 0x30, mode->vtotal >> 4);
  57        regmap_write(adv->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff);
  58        regmap_write(adv->regmap_cec, 0x32, vsw >> 4);
  59        regmap_write(adv->regmap_cec, 0x33, (vsw << 4) & 0xff);
  60        regmap_write(adv->regmap_cec, 0x34, vfp >> 4);
  61        regmap_write(adv->regmap_cec, 0x35, (vfp << 4) & 0xff);
  62        regmap_write(adv->regmap_cec, 0x36, vbp >> 4);
  63        regmap_write(adv->regmap_cec, 0x37, (vbp << 4) & 0xff);
  64}
  65
  66void adv7533_dsi_power_on(struct adv7511 *adv)
  67{
  68        struct mipi_dsi_device *dsi = adv->dsi;
  69
  70        if (adv->use_timing_gen)
  71                adv7511_dsi_config_timing_gen(adv);
  72
  73        /* set number of dsi lanes */
  74        regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4);
  75
  76        if (adv->use_timing_gen) {
  77                /* reset internal timing generator */
  78                regmap_write(adv->regmap_cec, 0x27, 0xcb);
  79                regmap_write(adv->regmap_cec, 0x27, 0x8b);
  80                regmap_write(adv->regmap_cec, 0x27, 0xcb);
  81        } else {
  82                /* disable internal timing generator */
  83                regmap_write(adv->regmap_cec, 0x27, 0x0b);
  84        }
  85
  86        /* enable hdmi */
  87        regmap_write(adv->regmap_cec, 0x03, 0x89);
  88        /* disable test mode */
  89        regmap_write(adv->regmap_cec, 0x55, 0x00);
  90
  91        regmap_register_patch(adv->regmap_cec, adv7533_cec_fixed_registers,
  92                              ARRAY_SIZE(adv7533_cec_fixed_registers));
  93}
  94
  95void adv7533_dsi_power_off(struct adv7511 *adv)
  96{
  97        /* disable hdmi */
  98        regmap_write(adv->regmap_cec, 0x03, 0x0b);
  99        /* disable internal timing generator */
 100        regmap_write(adv->regmap_cec, 0x27, 0x0b);
 101}
 102
 103void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode)
 104{
 105        struct mipi_dsi_device *dsi = adv->dsi;
 106        int lanes, ret;
 107
 108        if (adv->num_dsi_lanes != 4)
 109                return;
 110
 111        if (mode->clock > 80000)
 112                lanes = 4;
 113        else
 114                lanes = 3;
 115
 116        if (lanes != dsi->lanes) {
 117                mipi_dsi_detach(dsi);
 118                dsi->lanes = lanes;
 119                ret = mipi_dsi_attach(dsi);
 120                if (ret)
 121                        dev_err(&dsi->dev, "failed to change host lanes\n");
 122        }
 123}
 124
 125int adv7533_patch_registers(struct adv7511 *adv)
 126{
 127        return regmap_register_patch(adv->regmap,
 128                                     adv7533_fixed_registers,
 129                                     ARRAY_SIZE(adv7533_fixed_registers));
 130}
 131
 132int adv7533_patch_cec_registers(struct adv7511 *adv)
 133{
 134        return regmap_register_patch(adv->regmap_cec,
 135                                    adv7533_cec_fixed_registers,
 136                                    ARRAY_SIZE(adv7533_cec_fixed_registers));
 137}
 138
 139int adv7533_attach_dsi(struct adv7511 *adv)
 140{
 141        struct device *dev = &adv->i2c_main->dev;
 142        struct mipi_dsi_host *host;
 143        struct mipi_dsi_device *dsi;
 144        int ret = 0;
 145        const struct mipi_dsi_device_info info = { .type = "adv7533",
 146                                                   .channel = 0,
 147                                                   .node = NULL,
 148                                                 };
 149
 150        host = of_find_mipi_dsi_host_by_node(adv->host_node);
 151        if (!host) {
 152                dev_err(dev, "failed to find dsi host\n");
 153                return -EPROBE_DEFER;
 154        }
 155
 156        dsi = mipi_dsi_device_register_full(host, &info);
 157        if (IS_ERR(dsi)) {
 158                dev_err(dev, "failed to create dsi device\n");
 159                ret = PTR_ERR(dsi);
 160                goto err_dsi_device;
 161        }
 162
 163        adv->dsi = dsi;
 164
 165        dsi->lanes = adv->num_dsi_lanes;
 166        dsi->format = MIPI_DSI_FMT_RGB888;
 167        dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
 168                          MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE;
 169
 170        ret = mipi_dsi_attach(dsi);
 171        if (ret < 0) {
 172                dev_err(dev, "failed to attach dsi to host\n");
 173                goto err_dsi_attach;
 174        }
 175
 176        return 0;
 177
 178err_dsi_attach:
 179        mipi_dsi_device_unregister(dsi);
 180err_dsi_device:
 181        return ret;
 182}
 183
 184void adv7533_detach_dsi(struct adv7511 *adv)
 185{
 186        mipi_dsi_detach(adv->dsi);
 187        mipi_dsi_device_unregister(adv->dsi);
 188}
 189
 190int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
 191{
 192        u32 num_lanes;
 193
 194        of_property_read_u32(np, "adi,dsi-lanes", &num_lanes);
 195
 196        if (num_lanes < 1 || num_lanes > 4)
 197                return -EINVAL;
 198
 199        adv->num_dsi_lanes = num_lanes;
 200
 201        adv->host_node = of_graph_get_remote_node(np, 0, 0);
 202        if (!adv->host_node)
 203                return -ENODEV;
 204
 205        of_node_put(adv->host_node);
 206
 207        adv->use_timing_gen = !of_property_read_bool(np,
 208                                                "adi,disable-timing-generator");
 209
 210        /* TODO: Check if these need to be parsed by DT or not */
 211        adv->rgb = true;
 212        adv->embedded_sync = false;
 213
 214        return 0;
 215}
 216