blob: 060a35c0a8ef99050b09bc46e8bbd7143e60e5ab [file] [log] [blame]
Mario Scheithauer956a9f62017-03-29 17:09:37 +02001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2014-2017 Siemens AG
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <console/console.h>
Mario Scheithauer956a9f62017-03-29 17:09:37 +020017#include <hwilib.h>
Nico Huber0f2dd1e2017-08-01 14:02:40 +020018#include <device/i2c_simple.h>
Mario Scheithauer61413532018-04-25 14:05:09 +020019#include <variant/ptn3460.h>
Mario Scheithauer956a9f62017-03-29 17:09:37 +020020
21/**
22 * This function sets up the DP2LVDS-converter to be used with the appropriate
23 * lcd panel.
24 *
25 * @param *hwi_block Filename in CBFS of the block to use as HW-Info.
26 * @return 0 on success or HWI-Data/PTN error code.
27 */
28int ptn3460_init(const char *hwi_block)
29{
30 struct ptn_3460_config cfg;
31 int status;
32 uint8_t disp_con = 0, color_depth = 0;
33 uint8_t edid_data[PTN_EDID_LEN];
34 int i;
35
36 if (!hwi_block || hwilib_find_blocks(hwi_block) != CB_SUCCESS) {
37 printk(BIOS_ERR, "LCD: Info block \"%s\" not found!\n",
38 hwi_block);
39 return 1;
40 }
41 /* Get all needed information from hwinfo block. */
42 if (hwilib_get_field(Edid, edid_data, sizeof(edid_data)) !=
43 sizeof(edid_data)) {
44 printk(BIOS_ERR, "LCD: No EDID data available in %s\n",
45 hwi_block);
46 return 1;
47 }
48 if ((hwilib_get_field(PF_DisplCon, &disp_con, sizeof(disp_con)) !=
49 sizeof(disp_con))) {
50 printk(BIOS_ERR, "LCD: Missing panel features from %s\n",
51 hwi_block);
52 return 1;
53 }
54 if (hwilib_get_field(PF_Color_Depth, &color_depth, sizeof(color_depth))
55 != sizeof(color_depth)) {
56 printk(BIOS_ERR, "LCD: Missing panel features from %s\n",
57 hwi_block);
58 return 1;
59 }
60 /*
61 * Here, all the desired information for setting up DP2LVDS converter
62 * is present. Inside the converter, table 6 will be used for the
63 * timings.
64 */
65 status = ptn3460_write_edid(6, edid_data);
66 if (status)
67 return status;
68 /* Select this table to be emulated. */
69 ptn_select_edid(6);
70 /* Read PTN configuration data. */
71 status = i2c_read_bytes(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR,
72 PTN_CONFIG_OFF, (uint8_t *)&cfg,
73 sizeof(cfg));
74 if (status)
75 return (PTN_BUS_ERROR | status);
76 /* Set up configuration data according to the hwinfo block we get. */
77 cfg.dp_interface_ctrl = 0;
78 cfg.lvds_interface_ctrl1 = 0x00;
79 if (disp_con == PF_DISPLCON_LVDS_DUAL)
80 /* Turn on dual LVDS lane and clock. */
81 cfg.lvds_interface_ctrl1 |= 0x0b;
82 if (color_depth == PF_COLOR_DEPTH_6BIT)
83 /* Use 18 bits per pixel. */
84 cfg.lvds_interface_ctrl1 |= 0x20;
85
Mario Scheithauer1e67f072018-09-27 11:51:23 +020086 /* 1 % clock spreading, 300 mV LVDS swing. */
87 cfg.lvds_interface_ctrl2 = 0x13;
Mario Scheithauer956a9f62017-03-29 17:09:37 +020088 /* No LVDS signal swap. */
89 cfg.lvds_interface_ctrl3 = 0x00;
90 /* Delay T2 (VDD to LVDS active) by 16 ms. */
91 cfg.t2_delay = 1;
92 /* 250 ms from LVDS to backlight active. */
93 cfg.t3_timing = 10;
94 /* 1 second re-power delay. */
95 cfg.t12_timing = 20;
96 /* 150 ms backlight off to LVDS inactive. */
97 cfg.t4_timing = 3;
98 /* Delay T5 (LVDS to VDD inactive) by 16 ms. */
99 cfg.t5_delay = 1;
100 /* Enable backlight control. */
101 cfg.backlight_ctrl = 0;
102
103 /* Write back configuration data to PTN3460. */
104 for (i = 0; i < sizeof(struct ptn_3460_config); i++) {
105 status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR,
106 PTN_CONFIG_OFF+i,
107 *(((uint8_t *)&cfg)+i));
108 if (status)
109 return (PTN_BUS_ERROR | status);
110 }
111
Mario Scheithauer956a9f62017-03-29 17:09:37 +0200112 return PTN_NO_ERROR;
113}
114
115/**
116 * This function writes one Extended Display Identification Data (EDID)
117 * structure to PTN3460.
118 *
119 * @param edid_num Number of EDID that must be written (0..6).
120 * @param *data Pointer to a buffer where data to write is stored in.
121 * @return 0 on success or error code.
122 */
123int ptn3460_write_edid(uint8_t edid_num, const uint8_t data[PTN_EDID_LEN])
124{
125 int status;
126 int i;
127
128 if (edid_num > PTN_MAX_EDID_NUM)
129 return PTN_INVALID_EDID;
130
131 /* First enable access to the desired EDID table. */
132 status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR,
133 PTN_CONFIG_OFF + 5, edid_num);
134 if (status)
135 return (PTN_BUS_ERROR | status);
136
137 /* Now we can simply write EDID-data to ptn3460. */
138 for (i = 0; i < PTN_EDID_LEN; i++) {
139 status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR,
140 PTN_EDID_OFF + i, data[i]);
141 if (status)
142 return (PTN_BUS_ERROR | status);
143 }
144
145 return PTN_NO_ERROR;
146}
147
148/**
149 * This function selects one of 7 EDID-tables inside PTN3460 which should be
150 * emulated on DisplayPort and turn emulation ON.
151 *
152 * @param edid_num Number of EDID to emulate (0..6).
153 * @return 0 on success or error code.
154 */
155int ptn_select_edid(uint8_t edid_num)
156{
157 int status;
158 uint8_t val;
159
160 if (edid_num > PTN_MAX_EDID_NUM)
161 return PTN_INVALID_EDID;
162 /* Enable emulation of the desired EDID table. */
163 val = (edid_num << 1) | 1;
164 status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR,
165 PTN_CONFIG_OFF + 4, val);
166 if (status)
167 return (PTN_BUS_ERROR | status);
168 else
169 return PTN_NO_ERROR;
170}