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