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