adv7533.c 5.4 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2 3 4 5
/*
 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
 */

6 7
#include <linux/of_graph.h>

8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#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 },
24
	{ 0x05, 0xc8 },
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
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;
	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);
}

66 67
void adv7533_dsi_power_on(struct adv7511 *adv)
{
68 69
	struct mipi_dsi_device *dsi = adv->dsi;

70 71 72
	if (adv->use_timing_gen)
		adv7511_dsi_config_timing_gen(adv);

73 74
	/* set number of dsi lanes */
	regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4);
75 76 77 78 79 80 81 82 83 84 85

	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);
	}

86 87 88 89 90 91 92 93 94 95 96 97 98
	/* 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);
99 100
	/* disable internal timing generator */
	regmap_write(adv->regmap_cec, 0x27, 0x0b);
101 102
}

103
void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode)
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
{
	struct mipi_dsi_device *dsi = adv->dsi;
	int lanes, ret;

	if (adv->num_dsi_lanes != 4)
		return;

	if (mode->clock > 80000)
		lanes = 4;
	else
		lanes = 3;

	if (lanes != dsi->lanes) {
		mipi_dsi_detach(dsi);
		dsi->lanes = lanes;
		ret = mipi_dsi_attach(dsi);
		if (ret)
			dev_err(&dsi->dev, "failed to change host lanes\n");
	}
}

125 126 127 128 129 130 131
int adv7533_patch_registers(struct adv7511 *adv)
{
	return regmap_register_patch(adv->regmap,
				     adv7533_fixed_registers,
				     ARRAY_SIZE(adv7533_fixed_registers));
}

132
int adv7533_patch_cec_registers(struct adv7511 *adv)
133
{
134
	return regmap_register_patch(adv->regmap_cec,
135 136 137
				    adv7533_cec_fixed_registers,
				    ARRAY_SIZE(adv7533_cec_fixed_registers));
}
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 198 199 200

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) {
		dev_err(dev, "failed to find dsi host\n");
		return -EPROBE_DEFER;
	}

	dsi = mipi_dsi_device_register_full(host, &info);
	if (IS_ERR(dsi)) {
		dev_err(dev, "failed to create dsi device\n");
		ret = PTR_ERR(dsi);
		goto err_dsi_device;
	}

	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_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE;

	ret = mipi_dsi_attach(dsi);
	if (ret < 0) {
		dev_err(dev, "failed to attach dsi to host\n");
		goto err_dsi_attach;
	}

	return 0;

err_dsi_attach:
	mipi_dsi_device_unregister(dsi);
err_dsi_device:
	return ret;
}

void adv7533_detach_dsi(struct adv7511 *adv)
{
	mipi_dsi_detach(adv->dsi);
	mipi_dsi_device_unregister(adv->dsi);
}

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;

201 202
	adv->host_node = of_graph_get_remote_node(np, 0, 0);
	if (!adv->host_node)
203 204 205 206
		return -ENODEV;

	of_node_put(adv->host_node);

207 208 209
	adv->use_timing_gen = !of_property_read_bool(np,
						"adi,disable-timing-generator");

210 211 212 213 214 215
	/* TODO: Check if these need to be parsed by DT or not */
	adv->rgb = true;
	adv->embedded_sync = false;

	return 0;
}