summaryrefslogtreecommitdiff
path: root/src/mainboard/siemens/mc_tcu3/ptn3460.c
blob: 56f51f964744b9962ccbd89802c15f4d56e6bb09 (plain)
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
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2014-2019 Siemens AG
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <console/console.h>
#include <hwilib.h>
#include <string.h>
#include <delay.h>
#include <types.h>
#include <device/i2c_simple.h>
#include "soc/i2c.h"
#include "ptn3460.h"

/** \brief This functions sets up the DP2LVDS-converter to be used with the
 *         appropriate lcd panel
 * @param  *hwi_block	Filename in CBFS of the block to use as HW-Info
 * @return		0 on success or error code
 */
int ptn3460_init(char *hwi_block)
{
	struct ptn_3460_config cfg;
	int status;
	uint8_t disp_con = 0, color_depth = 0;
	uint8_t edid_data[0x80];
	uint8_t hwid[4], tcu31_hwid[4] = {7, 9, 2, 0};
	uint8_t i;

	if (!hwi_block || hwilib_find_blocks(hwi_block) != CB_SUCCESS) {
		printk(BIOS_ERR, "LCD: Info block \"%s\" not found!\n",
			hwi_block);
		return 1;
	}

	/* Get all needed information from hwinfo block */
	if (hwilib_get_field(Edid, edid_data, 0x80) != sizeof(edid_data)) {
		printk(BIOS_ERR, "LCD: No EDID data available in %s\n",
			hwi_block);
		return 1;
	}
	if ((hwilib_get_field(PF_DisplCon, &disp_con, 1) != 1)) {
		printk(BIOS_ERR, "LCD: Missing panel features from %s\n",
			hwi_block);
		return 1;
	}
	if (hwilib_get_field(PF_Color_Depth, &color_depth, 1) != 1) {
		printk(BIOS_ERR, "LCD: Missing panel features from %s\n",
			hwi_block);
		return 1;
	}
	/* Here, all the desired information for setting up DP2LVDS converter*/
	/* are present. Inside the converter, table 6 will be used for */
	/* the timings. */
	if ((status = ptn3460_write_edid(6, edid_data)) != 0)
		return status;
	/* Select this table to be emulated */
	ptn_select_edid(6);
	/* Read PTN configuration data */
	status = i2c_read_bytes(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF, (u8 *) &cfg,
				sizeof(struct ptn_3460_config));
	if (status)
		return (PTN_BUS_ERROR | status);

	/* Set up configuration data according to the hwinfo blocks we get */
	cfg.dp_interface_ctrl = 0;
	cfg.lvds_interface_ctrl1 = 0x00;
	if (disp_con == PF_DISPLCON_LVDS_DUAL)
		cfg.lvds_interface_ctrl1 |= 0x0b; /* Turn on dual LVDS lane and clock */
	if (color_depth == PF_COLOR_DEPTH_6BIT)
		cfg.lvds_interface_ctrl1 |= 0x20; /* Use 18 bits per pixel */
	cfg.lvds_interface_ctrl2 = 0x03;  /* no clock spreading, 300 mV LVDS swing */
	/* Swap LVDS even and odd lanes for HW-ID 7.9.2.0 only. */
	if (hwilib_get_field(HWID, hwid, sizeof(hwid)) == sizeof(hwid) &&
	    !(memcmp(hwid, tcu31_hwid, sizeof(hwid)))) {
		cfg.lvds_interface_ctrl3 = 0x01;  /* swap LVDS even and odd */
	} else
		cfg.lvds_interface_ctrl3 = 0x00;  /* no LVDS signal swap */
	cfg.t2_delay = 1;		/* Delay T2 (VDD to LVDS active) by 16 ms */
	cfg.t3_timing = 10;		/* 500 ms from LVDS to backlight active */
	cfg.t12_timing = 20;		/* 1 second re-power delay */
	cfg.t4_timing = 3;		/* 150 ms backlight off to LVDS inactive */
	cfg.t5_delay = 1;		/* Delay T5 (LVDS to VDD inactive) by 16 ms */
	cfg.backlight_ctrl = 0;		/* Enable backlight control */

	/* Write back configuration data to PTN3460 */
	for (i = 0; i < sizeof(struct ptn_3460_config); i++) {
		status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + i,
				    *(((uint8_t *) &cfg) + i));
		if (status)
			return (PTN_BUS_ERROR | status);
	}

	return PTN_NO_ERROR;
}

/** \brief This functions reads one desired EDID data structure from PTN3460
 * @param  edid_num Number of EDID that must be read (0..6)
 * @param  *data    Pointer to a buffer where to store read data
 * @return          0 on success or error code
 */
int ptn3460_read_edid(u8 edid_num, u8 *data)
{
	int status;

	if (edid_num > PTN_MAX_EDID_NUM)
		return PTN_INVALID_EDID;
	/* First enable access to the desired EDID table */
	status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5, edid_num);
	if (status)
		return (PTN_BUS_ERROR | status);

	/* Now we can simply read back EDID-data */
	status = i2c_read_bytes(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF, data,
				PTN_EDID_LEN);
	if (status)
		return (PTN_BUS_ERROR | status);
	else
		return PTN_NO_ERROR;
}

/** \brief This functions writes one EDID data structure to PTN3460
 * @param  edid_num  Number of EDID that must be written (0..6)
 * @param  *data     Pointer to a buffer where data to write is stored in
 * @return           0 on success or error code
 */
int ptn3460_write_edid(u8 edid_num, u8 *data)
{
	int status;
	int i;

	if (edid_num > PTN_MAX_EDID_NUM)
		return PTN_INVALID_EDID;

	/* First enable access to the desired EDID table */
	status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5, edid_num);
	if (status)
		return (PTN_BUS_ERROR | status);

	/* Now we can simply write EDID-data to ptn3460 */
	for (i = 0; i < PTN_EDID_LEN; i++) {
		status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF + i,
				    data[i]);
		if (status)
			return (PTN_BUS_ERROR | status);
	}
	return PTN_NO_ERROR;
}

/** \brief  This functions selects one of 7 EDID-tables inside PTN3460
 *          which should be emulated on display port and turn emulation ON
 * @param   edid_num  Number of EDID to emulate (0..6)
 * @return            0 on success or error code
 */
int ptn_select_edid (u8 edid_num)
{
	int status;
	u8 val;

	if (edid_num > PTN_MAX_EDID_NUM)
		return PTN_INVALID_EDID;
	/* Enable emulation of the desired EDID table */
	val = (edid_num << 1) | 1;
	status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 4, val);
	if (status)
		return (PTN_BUS_ERROR | status);
	else
		return PTN_NO_ERROR;
}

/** \brief This functions performs a flash operation which will write
 *         current configuration table (all the EDID-tables and the
 *         configuration space with a total amount of 1 KByte)
 *         to the internal flash of PTN3460
 * @param  none
 * @return 0 on success or error code
 */
int ptn3460_flash_config(void)
{
	int status, i;
	struct ptn_3460_flash flash;

	flash.cmd = 0x01;	/* perform erase and flash cycle */
	flash.magic = 0x7845;	/* Magic number to protect flash operation */
	flash.trigger = 0x56;	/* This value starts flash operation */

	for (i = 0; i < sizeof(struct ptn_3460_flash); i++) {
		status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_FLASH_CFG_OFF+i,
				*(((uint8_t *) &flash) + i));
			if (status)
				return (PTN_BUS_ERROR | status);
	}
	if (status) {
		return (PTN_BUS_ERROR | status);
	} else {
		/* To ensure the flash operation is finished, we have to wait 300 ms */
		mdelay(300);
		return PTN_NO_ERROR;
	}
}