soc/mediatek/mt8196: Add DSI dual channel
Add support for DSI dual channel and dual Display Stream Compression (DSC) features. - Add DSI dual channel and dual DSC feature. - Add dsi1, mipi1, dsc0, dsc1 engine drivers. - Add configuration for dual channel feature selection. - Add 'dsi.c' for mt8196 mipi data rate calculation. - Add 'mtk_mipi_dphy.c' for mt8196 timing calculation. BUG=b:424782827 TEST=Build pass, boot ok. Verify display output on the following platforms: - 8196 Navi: eDP path. - 8189 Skywalker: eDP path. - 8189 Padme: single MIPI path (without DSC). - 8196 Sapphire: dual MIPI path (with DSC). Change-Id: I2bea829da72d23165a74b399eabfcdd55a7f28a1 Signed-off-by: Payne Lin <payne.lin@mediatek.corp-partner.google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/90504 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Yu-Ping Wu <yupingso@google.com> Reviewed-by: Chen-Tsung Hsieh <chentsung@google.com> Reviewed-by: Yidi Lin <yidilin@google.com>
This commit is contained in:
parent
def7aa7094
commit
060d18f070
9 changed files with 656 additions and 137 deletions
|
|
@ -69,6 +69,12 @@ config MEDIATEK_WDT_RESET_BY_SW
|
|||
If the kernel disables WDT external reset (mediatek,disable-extrst),
|
||||
then this option must be disabled to allow WDT hardware external reset.
|
||||
|
||||
config MEDIATEK_DSI_DUAL_CHANNEL
|
||||
bool
|
||||
default n
|
||||
help
|
||||
The configuration to support DSI dual channel.
|
||||
|
||||
config MEMORY_TEST
|
||||
bool
|
||||
default y
|
||||
|
|
|
|||
|
|
@ -16,6 +16,25 @@
|
|||
#define CPHY_SYMBOL_RATE 7
|
||||
#define CPHY_SYMBOL_RATE_DIVISOR 16
|
||||
|
||||
#define COMPRESSION_RATIO 3
|
||||
#define UNCOMPRESSED_RATIO 1
|
||||
|
||||
static const struct {
|
||||
struct dsi_regs *const dsi_reg;
|
||||
struct mipi_tx_regs *const mipi_reg;
|
||||
} dsi_mipi_regs[] = {
|
||||
{
|
||||
.dsi_reg = dsi0,
|
||||
.mipi_reg = mipi_tx0,
|
||||
},
|
||||
#if CONFIG(MEDIATEK_DSI_DUAL_CHANNEL)
|
||||
{
|
||||
.dsi_reg = dsi1,
|
||||
.mipi_reg = mipi_tx1,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static unsigned int mtk_dsi_get_bits_per_pixel(u32 format)
|
||||
{
|
||||
switch (format) {
|
||||
|
|
@ -33,7 +52,7 @@ static unsigned int mtk_dsi_get_bits_per_pixel(u32 format)
|
|||
}
|
||||
|
||||
static u32 mtk_dsi_get_data_rate(u32 bits_per_pixel, u32 lanes,
|
||||
const struct edid *edid, bool is_cphy)
|
||||
const struct edid *edid, u32 mode_flags)
|
||||
{
|
||||
/* data_rate = pixel_clock * bits_per_pixel * mipi_ratio / lanes
|
||||
* Note pixel_clock comes in kHz and returned data_rate is in bps.
|
||||
|
|
@ -41,9 +60,18 @@ static u32 mtk_dsi_get_data_rate(u32 bits_per_pixel, u32 lanes,
|
|||
* for older platforms which do not have complete implementation in HFP.
|
||||
* Newer platforms should just set that to 1.0 (100 / 100).
|
||||
*/
|
||||
u64 pixel_clock = edid->mode.pixel_clock;
|
||||
u32 data_rate;
|
||||
u64 dividend = (u64)edid->mode.pixel_clock * bits_per_pixel * 1000 *
|
||||
MTK_DSI_MIPI_RATIO_NUMERATOR;
|
||||
bool is_cphy = !!(mode_flags & MIPI_DSI_MODE_CPHY);
|
||||
bool is_dsi_dual_channel = !!(mode_flags & MIPI_DSI_DUAL_CHANNEL);
|
||||
bool is_dsc_enabled = !!(mode_flags & MIPI_DSI_DSC_MODE);
|
||||
|
||||
if (is_dsc_enabled)
|
||||
pixel_clock = pixel_clock *
|
||||
(DIV_ROUND_UP(edid->mode.ha, 3) + edid->mode.hbl) /
|
||||
(edid->mode.ha + edid->mode.hbl);
|
||||
|
||||
u64 dividend = pixel_clock * bits_per_pixel * 1000 * MTK_DSI_MIPI_RATIO_NUMERATOR;
|
||||
u64 divisor = (u64)lanes * MTK_DSI_MIPI_RATIO_DENOMINATOR;
|
||||
|
||||
if (is_cphy) {
|
||||
|
|
@ -51,15 +79,20 @@ static u32 mtk_dsi_get_data_rate(u32 bits_per_pixel, u32 lanes,
|
|||
divisor *= CPHY_SYMBOL_RATE_DIVISOR;
|
||||
}
|
||||
data_rate = DIV_ROUND_UP(dividend, divisor);
|
||||
printk(BIOS_INFO, "DSI data_rate: %u bps\n", data_rate);
|
||||
printk(BIOS_INFO, "pixel_clock: %lld\n", pixel_clock);
|
||||
printk(BIOS_INFO, "bits_per_pixel: %d\n", bits_per_pixel);
|
||||
if (is_dsi_dual_channel)
|
||||
data_rate = data_rate / 2;
|
||||
|
||||
printk(BIOS_INFO, "DSI final data_rate: %u bps\n", data_rate);
|
||||
|
||||
if (data_rate < MTK_DSI_DATA_RATE_MIN_MHZ * MHz) {
|
||||
printk(BIOS_ERR, "data rate (%ubps) must be >= %ubps. "
|
||||
"Please check the pixel clock (%u), "
|
||||
"Please check the pixel clock (%llu), "
|
||||
"bits per pixel (%u), "
|
||||
"mipi_ratio (%d%%) and number of lanes (%d)\n",
|
||||
data_rate, MTK_DSI_DATA_RATE_MIN_MHZ * MHz,
|
||||
edid->mode.pixel_clock, bits_per_pixel,
|
||||
pixel_clock, bits_per_pixel,
|
||||
(100 * MTK_DSI_MIPI_RATIO_NUMERATOR /
|
||||
MTK_DSI_MIPI_RATIO_DENOMINATOR), lanes);
|
||||
return 0;
|
||||
|
|
@ -72,7 +105,8 @@ __weak void mtk_dsi_override_phy_timing(struct mtk_phy_timing *timing)
|
|||
/* Do nothing. */
|
||||
}
|
||||
|
||||
static void mtk_dsi_dphy_timing(u32 data_rate, struct mtk_phy_timing *timing)
|
||||
static void mtk_dsi_dphy_timing(struct dsi_regs *dsi_reg, u32 data_rate,
|
||||
struct mtk_phy_timing *timing)
|
||||
{
|
||||
u32 timcon0, timcon1, timcon2, timcon3;
|
||||
u32 data_rate_mhz = DIV_ROUND_UP(data_rate, MHz);
|
||||
|
|
@ -91,23 +125,23 @@ static void mtk_dsi_dphy_timing(u32 data_rate, struct mtk_phy_timing *timing)
|
|||
timcon3 = timing->clk_hs_prepare | timing->clk_hs_post << 8 |
|
||||
timing->clk_hs_exit << 16;
|
||||
|
||||
write32(&dsi0->dsi_phy_timecon0, timcon0);
|
||||
write32(&dsi0->dsi_phy_timecon1, timcon1);
|
||||
write32(&dsi0->dsi_phy_timecon2, timcon2);
|
||||
write32(&dsi0->dsi_phy_timecon3, timcon3);
|
||||
write32(&dsi_reg->dsi_phy_timecon0, timcon0);
|
||||
write32(&dsi_reg->dsi_phy_timecon1, timcon1);
|
||||
write32(&dsi_reg->dsi_phy_timecon2, timcon2);
|
||||
write32(&dsi_reg->dsi_phy_timecon3, timcon3);
|
||||
}
|
||||
|
||||
static void mtk_dsi_clk_hs_mode_enable(void)
|
||||
static void mtk_dsi_clk_hs_mode_enable(struct dsi_regs *dsi_reg)
|
||||
{
|
||||
setbits32(&dsi0->dsi_phy_lccon, LC_HS_TX_EN);
|
||||
setbits32(&dsi_reg->dsi_phy_lccon, LC_HS_TX_EN);
|
||||
}
|
||||
|
||||
static void mtk_dsi_clk_hs_mode_disable(void)
|
||||
static void mtk_dsi_clk_hs_mode_disable(struct dsi_regs *dsi_reg)
|
||||
{
|
||||
clrbits32(&dsi0->dsi_phy_lccon, LC_HS_TX_EN);
|
||||
clrbits32(&dsi_reg->dsi_phy_lccon, LC_HS_TX_EN);
|
||||
}
|
||||
|
||||
static void mtk_dsi_set_mode(u32 mode_flags)
|
||||
static void mtk_dsi_set_mode(struct dsi_regs *dsi_reg, u32 mode_flags)
|
||||
{
|
||||
u32 tmp_reg1 = 0;
|
||||
|
||||
|
|
@ -121,10 +155,10 @@ static void mtk_dsi_set_mode(u32 mode_flags)
|
|||
tmp_reg1 = SYNC_PULSE_MODE;
|
||||
}
|
||||
|
||||
write32(&dsi0->dsi_mode_ctrl, tmp_reg1);
|
||||
write32(&dsi_reg->dsi_mode_ctrl, tmp_reg1);
|
||||
}
|
||||
|
||||
static void mtk_dsi_rxtx_control(u32 mode_flags, u32 lanes)
|
||||
static void mtk_dsi_rxtx_control(struct dsi_regs *dsi_reg, u32 mode_flags, u32 lanes)
|
||||
{
|
||||
u32 tmp_reg = 0;
|
||||
|
||||
|
|
@ -150,7 +184,7 @@ static void mtk_dsi_rxtx_control(u32 mode_flags, u32 lanes)
|
|||
if (!(mode_flags & MIPI_DSI_MODE_EOT_PACKET))
|
||||
tmp_reg |= EOTP_DISABLE;
|
||||
|
||||
write32(&dsi0->dsi_txrx_ctrl, tmp_reg);
|
||||
write32(&dsi_reg->dsi_txrx_ctrl, tmp_reg);
|
||||
}
|
||||
|
||||
static void mtk_dsi_dphy_vdo_timing(const u32 mode_flags,
|
||||
|
|
@ -168,6 +202,9 @@ static void mtk_dsi_dphy_vdo_timing(const u32 mode_flags,
|
|||
phy_timing->da_hs_zero + phy_timing->da_hs_exit + 3;
|
||||
|
||||
u32 delta = 10;
|
||||
int channels = (mode_flags & MIPI_DSI_DUAL_CHANNEL) ? 2 : 1;
|
||||
int dsc_ratio = (mode_flags & MIPI_DSI_DSC_MODE) ? COMPRESSION_RATIO :
|
||||
UNCOMPRESSED_RATIO;
|
||||
|
||||
if (mode_flags & MIPI_DSI_MODE_EOT_PACKET)
|
||||
delta += 2;
|
||||
|
|
@ -188,17 +225,44 @@ static void mtk_dsi_dphy_vdo_timing(const u32 mode_flags,
|
|||
hbp * bytes_per_pixel, d_phy);
|
||||
}
|
||||
|
||||
*hsync_active_byte = edid->mode.hspw * bytes_per_pixel - 10;
|
||||
*hsync_active_byte = edid->mode.hspw / channels * bytes_per_pixel - 10;
|
||||
|
||||
if (mode_flags & MIPI_DSI_MODE_LINE_END) {
|
||||
*hsync_active_byte = DIV_ROUND_UP(*hsync_active_byte, lanes) * lanes - 2;
|
||||
*hbp_byte = DIV_ROUND_UP(*hbp_byte, lanes) * lanes - 2;
|
||||
*hfp_byte = DIV_ROUND_UP(*hfp_byte, lanes) * lanes - 2;
|
||||
*hbp_byte -= (edid->mode.ha * bytes_per_pixel + 2) % lanes;
|
||||
*hbp_byte -= (edid->mode.ha / dsc_ratio / channels * bytes_per_pixel + 2) %
|
||||
lanes;
|
||||
}
|
||||
}
|
||||
|
||||
static void mtk_dsi_config_vdo_timing(u32 mode_flags, u32 format, u32 lanes,
|
||||
const struct edid *edid,
|
||||
static u32 mtk_dsi_get_packet_fmt(u32 format)
|
||||
{
|
||||
u32 packet_fmt;
|
||||
|
||||
switch (format) {
|
||||
case MIPI_DSI_FMT_RGB888:
|
||||
packet_fmt = PACKED_PS_24BIT_RGB888;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666:
|
||||
packet_fmt = LOOSELY_PS_18BIT_RGB666;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666_PACKED:
|
||||
packet_fmt = PACKED_PS_18BIT_RGB666;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB565:
|
||||
packet_fmt = PACKED_PS_16BIT_RGB565;
|
||||
break;
|
||||
default:
|
||||
packet_fmt = PACKED_PS_24BIT_RGB888;
|
||||
break;
|
||||
}
|
||||
|
||||
return packet_fmt;
|
||||
}
|
||||
|
||||
static void mtk_dsi_config_vdo_timing(struct dsi_regs *const dsi_reg, u32 mode_flags,
|
||||
u32 format, u32 lanes, const struct edid *edid,
|
||||
const struct mtk_phy_timing *phy_timing)
|
||||
{
|
||||
u32 hsync_active_byte;
|
||||
|
|
@ -211,26 +275,30 @@ static void mtk_dsi_config_vdo_timing(u32 mode_flags, u32 format, u32 lanes,
|
|||
u32 bytes_per_pixel;
|
||||
u32 packet_fmt;
|
||||
u32 hactive;
|
||||
u32 hbp_offset;
|
||||
bool is_cphy = !!(mode_flags & MIPI_DSI_MODE_CPHY);
|
||||
bool is_dsc_enabled = !!(mode_flags & MIPI_DSI_DSC_MODE);
|
||||
int channels = !!(mode_flags & MIPI_DSI_DUAL_CHANNEL) ? 2 : 1;
|
||||
int dsc_ratio = is_dsc_enabled ? COMPRESSION_RATIO : UNCOMPRESSED_RATIO;
|
||||
|
||||
bytes_per_pixel = DIV_ROUND_UP(mtk_dsi_get_bits_per_pixel(format), 8);
|
||||
vbp_byte = edid->mode.vbl - edid->mode.vso - edid->mode.vspw -
|
||||
edid->mode.vborder;
|
||||
vfp_byte = edid->mode.vso - edid->mode.vborder;
|
||||
|
||||
write32(&dsi0->dsi_vsa_nl, edid->mode.vspw);
|
||||
write32(&dsi0->dsi_vbp_nl, vbp_byte);
|
||||
write32(&dsi0->dsi_vfp_nl, vfp_byte);
|
||||
write32(&dsi0->dsi_vact_nl, edid->mode.va);
|
||||
write32(&dsi_reg->dsi_vsa_nl, edid->mode.vspw);
|
||||
write32(&dsi_reg->dsi_vbp_nl, vbp_byte);
|
||||
write32(&dsi_reg->dsi_vfp_nl, vfp_byte);
|
||||
write32(&dsi_reg->dsi_vact_nl, edid->mode.va);
|
||||
|
||||
hbp = edid->mode.hbl - edid->mode.hso - edid->mode.hspw -
|
||||
edid->mode.hborder;
|
||||
hfp = edid->mode.hso - edid->mode.hborder;
|
||||
hbp = (edid->mode.hbl - edid->mode.hso - edid->mode.hspw -
|
||||
edid->mode.hborder) / channels;
|
||||
hfp = (edid->mode.hso - edid->mode.hborder) / channels;
|
||||
|
||||
hbp_offset = (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ?
|
||||
0 : (edid->mode.hspw / channels);
|
||||
hbp_byte = (hbp + hbp_offset) * bytes_per_pixel - 10;
|
||||
|
||||
if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
|
||||
hbp_byte = hbp * bytes_per_pixel - 10;
|
||||
else
|
||||
hbp_byte = (hbp + edid->mode.hspw) * bytes_per_pixel - 10;
|
||||
hfp_byte = hfp * bytes_per_pixel;
|
||||
|
||||
if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy)
|
||||
|
|
@ -255,49 +323,40 @@ static void mtk_dsi_config_vdo_timing(u32 mode_flags, u32 format, u32 lanes,
|
|||
hbp_byte = MIN_HBP_BYTE;
|
||||
}
|
||||
|
||||
write32(&dsi0->dsi_hsa_wc, hsync_active_byte);
|
||||
write32(&dsi0->dsi_hbp_wc, hbp_byte);
|
||||
write32(&dsi0->dsi_hfp_wc, hfp_byte);
|
||||
write32(&dsi_reg->dsi_hsa_wc, hsync_active_byte);
|
||||
write32(&dsi_reg->dsi_hbp_wc, hbp_byte);
|
||||
write32(&dsi_reg->dsi_hfp_wc, hfp_byte);
|
||||
|
||||
switch (format) {
|
||||
case MIPI_DSI_FMT_RGB888:
|
||||
packet_fmt = PACKED_PS_24BIT_RGB888;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666:
|
||||
packet_fmt = LOOSELY_PS_18BIT_RGB666;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666_PACKED:
|
||||
packet_fmt = PACKED_PS_18BIT_RGB666;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB565:
|
||||
packet_fmt = PACKED_PS_16BIT_RGB565;
|
||||
break;
|
||||
default:
|
||||
packet_fmt = PACKED_PS_24BIT_RGB888;
|
||||
break;
|
||||
}
|
||||
if (is_dsc_enabled)
|
||||
packet_fmt = COMPRESSED_PIXEL_STREAM_V2;
|
||||
else
|
||||
packet_fmt = mtk_dsi_get_packet_fmt(format);
|
||||
|
||||
hactive = edid->mode.ha;
|
||||
hactive = edid->mode.ha / dsc_ratio / channels;
|
||||
packet_fmt |= (hactive * bytes_per_pixel) & DSI_PS_WC;
|
||||
|
||||
write32(&dsi0->dsi_psctrl,
|
||||
write32(&dsi_reg->dsi_psctrl,
|
||||
PIXEL_STREAM_CUSTOM_HEADER << DSI_PSCON_CUSTOM_HEADER_SHIFT |
|
||||
packet_fmt);
|
||||
|
||||
/* Older systems like MT8173 do not support size_con. */
|
||||
if (MTK_DSI_HAVE_SIZE_CON)
|
||||
write32(&dsi0->dsi_size_con,
|
||||
write32(&dsi_reg->dsi_size_con,
|
||||
edid->mode.va << DSI_SIZE_CON_HEIGHT_SHIFT |
|
||||
hactive << DSI_SIZE_CON_WIDTH_SHIFT);
|
||||
if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy)
|
||||
mtk_dsi_cphy_enable_cmdq_6byte(dsi0);
|
||||
mtk_dsi_cphy_enable_cmdq_6byte(dsi_reg);
|
||||
}
|
||||
|
||||
static void mtk_dsi_start(void)
|
||||
static void mtk_dsi_dual_enable(struct dsi_regs *dsi_reg, bool enable)
|
||||
{
|
||||
write32(&dsi0->dsi_start, 0);
|
||||
/* Only start master DSI */
|
||||
write32(&dsi0->dsi_start, 1);
|
||||
clrsetbits32(&dsi_reg->dsi_con_ctrl, DSI_DUAL, (enable ? DSI_DUAL : 0));
|
||||
}
|
||||
|
||||
static void mtk_dsi_start(struct dsi_regs *dsi_reg)
|
||||
{
|
||||
write32(&dsi_reg->dsi_start, 0);
|
||||
write32(&dsi_reg->dsi_start, 1);
|
||||
}
|
||||
|
||||
static bool mtk_dsi_is_read_command(enum mipi_dsi_transaction type)
|
||||
|
|
@ -313,60 +372,77 @@ static bool mtk_dsi_is_read_command(enum mipi_dsi_transaction type)
|
|||
}
|
||||
}
|
||||
|
||||
static void mtk_dsi_enable_and_start(bool is_dsi_dual_channel)
|
||||
{
|
||||
/*
|
||||
* For dual channel synchronization,
|
||||
* the secondary dsi1 must be dual-enabled before starting the primary dsi0.
|
||||
*/
|
||||
if (is_dsi_dual_channel)
|
||||
mtk_dsi_dual_enable(dsi_mipi_regs[ARRAY_SIZE(dsi_mipi_regs) - 1].dsi_reg, true);
|
||||
/*
|
||||
* Only start primary dsi0 for single or dual channel mode.
|
||||
* The secondary dsi1 is dual-enabled in mtk_dsi_dual_enable()
|
||||
* and does not require a separate start.
|
||||
* Starting only the primary dsi0 ensures correct synchronization
|
||||
* with secondary dsi1 if there is.
|
||||
*/
|
||||
mtk_dsi_start(dsi_mipi_regs[0].dsi_reg);
|
||||
}
|
||||
|
||||
static enum cb_err mtk_dsi_cmdq(enum mipi_dsi_transaction type, const u8 *data, u8 len,
|
||||
void *user_data)
|
||||
{
|
||||
const u8 *tx_buf = data;
|
||||
u32 config;
|
||||
int i, j;
|
||||
uint32_t *mode_flags = (uint32_t *)user_data;
|
||||
bool is_dsi_dual_channel = (*mode_flags & MIPI_DSI_DUAL_CHANNEL);
|
||||
|
||||
if (!wait_ms(20, !(read32(&dsi0->dsi_intsta) & DSI_BUSY))) {
|
||||
printk(BIOS_ERR, "%s: cannot get DSI ready for sending commands"
|
||||
" after 20ms and the panel may not work properly.\n",
|
||||
__func__);
|
||||
return CB_ERR;
|
||||
}
|
||||
write32(&dsi0->dsi_intsta, 0);
|
||||
|
||||
if (mtk_dsi_is_read_command(type))
|
||||
config = BTA;
|
||||
else
|
||||
config = (len > 2) ? LONG_PACKET : SHORT_PACKET;
|
||||
|
||||
if (len <= 2) {
|
||||
uint32_t val = (type << 8) | config;
|
||||
for (i = 0; i < len; i++)
|
||||
val |= tx_buf[i] << (i + 2) * 8;
|
||||
write32(&dsi0->dsi_cmdq[0], val);
|
||||
write32(&dsi0->dsi_cmdq_size, 1);
|
||||
} else {
|
||||
/* TODO(hungte) Replace by buffer_to_fifo32_prefix */
|
||||
write32(&dsi0->dsi_cmdq[0], (len << 16) | (type << 8) | config);
|
||||
for (i = 0; i < len; i += 4) {
|
||||
uint32_t val = 0;
|
||||
for (j = 0; j < MIN(len - i, 4); j++)
|
||||
val |= tx_buf[i + j] << j * 8;
|
||||
write32(&dsi0->dsi_cmdq[i / 4 + 1], val);
|
||||
for (unsigned int k = 0; k < ARRAY_SIZE(dsi_mipi_regs); k++) {
|
||||
struct dsi_regs *dsi = dsi_mipi_regs[k].dsi_reg;
|
||||
if (!wait_ms(20, !(read32(&dsi->dsi_intsta) & DSI_BUSY))) {
|
||||
printk(BIOS_ERR, "%s: cannot get DSI-%d ready for sending commands"
|
||||
" after 20ms and the panel may not work properly.\n",
|
||||
__func__, k);
|
||||
return CB_ERR;
|
||||
}
|
||||
write32(&dsi0->dsi_cmdq_size, 1 + DIV_ROUND_UP(len, 4));
|
||||
write32(&dsi->dsi_intsta, 0);
|
||||
|
||||
if (mtk_dsi_is_read_command(type))
|
||||
config = BTA;
|
||||
else
|
||||
config = (len > 2) ? LONG_PACKET : SHORT_PACKET;
|
||||
|
||||
u32 prefix = config | type << 8;
|
||||
int prefsz = 2;
|
||||
if (len > 2) {
|
||||
prefix |= len << 16;
|
||||
prefsz += 2;
|
||||
}
|
||||
buffer_to_fifo32_prefix(tx_buf, prefix, prefsz, prefsz + len, &dsi->dsi_cmdq[0],
|
||||
4, 4);
|
||||
write32(&dsi->dsi_cmdq_size, DIV_ROUND_UP(prefsz + len, 4));
|
||||
setbits32(&dsi->dsi_cmdq_size, CMDQ_SIZE_SEL);
|
||||
}
|
||||
setbits32(&dsi0->dsi_cmdq_size, CMDQ_SIZE_SEL);
|
||||
|
||||
mtk_dsi_start();
|
||||
mtk_dsi_enable_and_start(is_dsi_dual_channel);
|
||||
|
||||
if (!wait_us(400, read32(&dsi0->dsi_intsta) & CMD_DONE_INT_FLAG)) {
|
||||
printk(BIOS_ERR, "%s: failed sending DSI command, "
|
||||
"panel may not work.\n", __func__);
|
||||
return CB_ERR;
|
||||
for (unsigned int k = 0; k < ARRAY_SIZE(dsi_mipi_regs); k++) {
|
||||
struct dsi_regs *dsi = dsi_mipi_regs[k].dsi_reg;
|
||||
if (!wait_us(400, read32(&dsi->dsi_intsta) & CMD_DONE_INT_FLAG)) {
|
||||
printk(BIOS_ERR, "%s: failed sending DSI-%d command, "
|
||||
"panel may not work.\n", __func__, k);
|
||||
return CB_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
static void mtk_dsi_reset_phy(void)
|
||||
static void mtk_dsi_reset_phy(struct dsi_regs *const dsi_reg)
|
||||
{
|
||||
setbits32(&dsi0->dsi_con_ctrl, DPHY_RESET);
|
||||
clrbits32(&dsi0->dsi_con_ctrl, DPHY_RESET);
|
||||
setbits32(&dsi_reg->dsi_con_ctrl, DPHY_RESET);
|
||||
clrbits32(&dsi_reg->dsi_con_ctrl, DPHY_RESET);
|
||||
}
|
||||
|
||||
int mtk_dsi_init(u32 mode_flags, u32 format, u32 lanes, const struct edid *edid,
|
||||
|
|
@ -375,6 +451,8 @@ int mtk_dsi_init(u32 mode_flags, u32 format, u32 lanes, const struct edid *edid,
|
|||
u32 data_rate;
|
||||
u32 bits_per_pixel = mtk_dsi_get_bits_per_pixel(format);
|
||||
bool is_cphy = !!(mode_flags & MIPI_DSI_MODE_CPHY);
|
||||
bool is_dsi_dual_channel = !!(mode_flags & MIPI_DSI_DUAL_CHANNEL);
|
||||
unsigned int num_dsi = is_dsi_dual_channel ? 2 : 1;
|
||||
|
||||
if (!CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) {
|
||||
printk(BIOS_ERR, "%s: Board is built without C-PHY interface support. "
|
||||
|
|
@ -382,27 +460,47 @@ int mtk_dsi_init(u32 mode_flags, u32 format, u32 lanes, const struct edid *edid,
|
|||
return -1;
|
||||
}
|
||||
|
||||
data_rate = mtk_dsi_get_data_rate(bits_per_pixel, lanes, edid, is_cphy);
|
||||
if (num_dsi > ARRAY_SIZE(dsi_mipi_regs)) {
|
||||
printk(BIOS_ERR, "%s: num_dsi %d > %lu\n", __func__,
|
||||
num_dsi, ARRAY_SIZE(dsi_mipi_regs));
|
||||
return -1;
|
||||
}
|
||||
|
||||
data_rate = mtk_dsi_get_data_rate(bits_per_pixel, lanes, edid, mode_flags);
|
||||
if (!data_rate)
|
||||
return -1;
|
||||
|
||||
mtk_dsi_configure_mipi_tx(mipi_tx0, data_rate, lanes, is_cphy);
|
||||
mtk_dsi_reset(dsi0);
|
||||
struct mtk_phy_timing phy_timing = {};
|
||||
if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy)
|
||||
mtk_dsi_cphy_timing(data_rate, &phy_timing);
|
||||
else
|
||||
mtk_dsi_dphy_timing(data_rate, &phy_timing);
|
||||
mtk_dsi_rxtx_control(mode_flags, lanes);
|
||||
mdelay(1);
|
||||
mtk_dsi_reset_phy();
|
||||
mtk_dsi_clk_hs_mode_disable();
|
||||
mtk_dsi_config_vdo_timing(mode_flags, format, lanes, edid, &phy_timing);
|
||||
mtk_dsi_clk_hs_mode_enable();
|
||||
for (unsigned int i = 0; i < num_dsi; i++) {
|
||||
struct dsi_regs *dsi = dsi_mipi_regs[i].dsi_reg;
|
||||
struct mipi_tx_regs *mipi = dsi_mipi_regs[i].mipi_reg;
|
||||
if (!dsi || !mipi) {
|
||||
printk(BIOS_ERR, "%s: Null dsi/mipi reg for DSI-%d\n", __func__, i);
|
||||
return -1;
|
||||
}
|
||||
mtk_dsi_configure_mipi_tx(mipi, data_rate, lanes, is_cphy);
|
||||
mtk_dsi_reset(dsi);
|
||||
struct mtk_phy_timing phy_timing = {};
|
||||
if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy)
|
||||
/* Dual channel is not implemented for CPHY. */
|
||||
mtk_dsi_cphy_timing(data_rate, &phy_timing);
|
||||
else
|
||||
mtk_dsi_dphy_timing(dsi, data_rate, &phy_timing);
|
||||
|
||||
mtk_dsi_rxtx_control(dsi, mode_flags, lanes);
|
||||
mdelay(1);
|
||||
mtk_dsi_reset_phy(dsi);
|
||||
mtk_dsi_clk_hs_mode_disable(dsi);
|
||||
mtk_dsi_config_vdo_timing(dsi, mode_flags, format, lanes, edid, &phy_timing);
|
||||
mtk_dsi_clk_hs_mode_enable(dsi);
|
||||
}
|
||||
|
||||
if (init_commands)
|
||||
mipi_panel_parse_init_commands(init_commands, mtk_dsi_cmdq, NULL);
|
||||
mtk_dsi_set_mode(mode_flags);
|
||||
mtk_dsi_start();
|
||||
mipi_panel_parse_init_commands(init_commands, mtk_dsi_cmdq, &mode_flags);
|
||||
|
||||
for (unsigned int i = 0; i < num_dsi; i++)
|
||||
mtk_dsi_set_mode(dsi_mipi_regs[i].dsi_reg, mode_flags);
|
||||
|
||||
mtk_dsi_enable_and_start(is_dsi_dual_channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ enum {
|
|||
LOOSELY_PS_18BIT_RGB666 = (1 << 16),
|
||||
PACKED_PS_18BIT_RGB666 = (2 << 16),
|
||||
PACKED_PS_24BIT_RGB888 = (3 << 16),
|
||||
COMPRESSED_PIXEL_STREAM_V2 = (5 << 16),
|
||||
|
||||
DSI_PSCON_CUSTOM_HEADER_SHIFT = 26,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ config SOC_MEDIATEK_MT8196
|
|||
select ARM64_USE_ARCH_TIMER
|
||||
select PCI
|
||||
select EARLY_MMU_INIT
|
||||
select MEDIATEK_DSI_DUAL_CHANNEL
|
||||
|
||||
if SOC_MEDIATEK_MT8196
|
||||
|
||||
|
|
|
|||
|
|
@ -64,12 +64,14 @@ ramstage-y += ../common/dp/dp_intf_v2.c
|
|||
ramstage-y += ../common/dp/dptx_common.c ../common/dp/dptx_v2.c dptx.c
|
||||
ramstage-y += ../common/dp/dptx_hal_common.c ../common/dp/dptx_hal_v2.c dptx_hal.c
|
||||
ramstage-y += ../common/dramc_info.c
|
||||
ramstage-y += ../common/dsi_common.c dsi.c
|
||||
ramstage-y += ../common/early_init.c
|
||||
ramstage-y += ../common/emi.c
|
||||
ramstage-y += gpueb.c
|
||||
ramstage-y += l2c_ops.c
|
||||
ramstage-y += ../common/mcu.c mcupm.c
|
||||
ramstage-y += ../common/mmu_operations.c
|
||||
ramstage-y += ../common/mtk_mipi_dphy.c mtk_mipi_dphy.c
|
||||
ramstage-$(CONFIG_PCI) += ../common/pcie.c pcie.c
|
||||
ramstage-$(CONFIG_COMMONLIB_STORAGE_MMC) += msdc.c
|
||||
ramstage-y += ../common/mt6363.c mt6363.c
|
||||
|
|
|
|||
|
|
@ -11,6 +11,30 @@
|
|||
#define SIZE(w, h) ((u32)(h) << 16 | (w))
|
||||
#define DUAL_PIPE(path) ((path) == DISP_PATH_DUAL_MIPI)
|
||||
|
||||
#define DSC_EN BIT(0)
|
||||
#define DSC_DUAL_INOUT BIT(2)
|
||||
#define DSC_IN_SRC_SEL BIT(3)
|
||||
#define DSC_BYPASS BIT(4)
|
||||
#define DSC_RELAY BIT(5)
|
||||
#define DSC_PT_MEM_EN BIT(7)
|
||||
#define DSC_EMPTY_FLAG_SEL GENMASK(15, 14)
|
||||
#define DSC_EMPTY_FLAG_ALWAYS_LOW BIT(15)
|
||||
#define DSC_UFOE_SEL BIT(16)
|
||||
#define DSC_ZERO_FIFO_STALL_DISABLE BIT(20)
|
||||
|
||||
#define DSC_INTEN_SEL GENMASK(6, 0)
|
||||
#define DSC_ZERO_FIFO BIT(2)
|
||||
|
||||
#define DSC_INTACK_SEL GENMASK(6, 0)
|
||||
#define DSC_INTACK_BUF_UNDERFLOW BIT(6)
|
||||
|
||||
#define DSC_PIC_PREPAD_HEIGHT_SEL GENMASK(15, 0)
|
||||
#define DSC_PIC_PREPAD_WIDTH_SEL GENMASK(31, 16)
|
||||
|
||||
#define ALIGN_PADDING(V, N) (((N) - ((V) % (N))) % (N))
|
||||
/* Provide default value for x == 0. */
|
||||
#define DEF(x, default) ((x) == 0 ? (default) : (x))
|
||||
|
||||
struct disp_pipe_regs {
|
||||
struct disp_mdp_rsz_regs *const mdp_rsz;
|
||||
struct disp_tdshp_regs *const tdshp;
|
||||
|
|
@ -44,6 +68,245 @@ static const struct disp_pipe_regs disp_pipe1_regs = {
|
|||
.dsc = disp_dsc3_reg,
|
||||
};
|
||||
|
||||
static void dsc_configure_registers(struct disp_dsc_regs *reg, u16 w, u16 h,
|
||||
const struct dsc_config *dsc_cfg)
|
||||
{
|
||||
u32 init_delay_limit, init_delay_height;
|
||||
u32 pic_group_width, pic_height_ext_num;
|
||||
u32 slice_group_width;
|
||||
u32 pad_num;
|
||||
u32 slice_mode;
|
||||
u32 dsc_cfg_mode = 0x22;
|
||||
u32 mask;
|
||||
u32 val;
|
||||
u32 rgb_swap = 0;
|
||||
|
||||
if (dsc_cfg->bits_per_component == 0xA)
|
||||
dsc_cfg_mode = 0x828;
|
||||
|
||||
assert(dsc_cfg->pic_width > 0);
|
||||
assert(dsc_cfg->pic_width >= dsc_cfg->slice_width);
|
||||
assert(dsc_cfg->slice_width > 0);
|
||||
slice_mode = dsc_cfg->pic_width / dsc_cfg->slice_width - 1;
|
||||
pic_group_width = DIV_ROUND_UP(dsc_cfg->pic_width, 3);
|
||||
pic_height_ext_num = DIV_ROUND_UP(h, dsc_cfg->slice_height);
|
||||
slice_group_width = DIV_ROUND_UP(dsc_cfg->slice_width, 3);
|
||||
pad_num = ALIGN_PADDING(dsc_cfg->slice_chunk_size * (slice_mode + 1), 3);
|
||||
init_delay_limit = DIV_ROUND_UP(dsc_cfg->initial_xmit_delay, 3);
|
||||
init_delay_limit = DIV_ROUND_UP((128 + init_delay_limit) * 3, dsc_cfg->slice_width);
|
||||
init_delay_height = MIN(15, init_delay_limit);
|
||||
|
||||
mask = DSC_EN | DSC_DUAL_INOUT | DSC_IN_SRC_SEL | DSC_BYPASS | DSC_RELAY |
|
||||
DSC_PT_MEM_EN | DSC_EMPTY_FLAG_SEL | DSC_UFOE_SEL |
|
||||
DSC_ZERO_FIFO_STALL_DISABLE;
|
||||
val = DSC_PT_MEM_EN | DSC_EMPTY_FLAG_ALWAYS_LOW | DSC_UFOE_SEL |
|
||||
DSC_ZERO_FIFO_STALL_DISABLE;
|
||||
|
||||
clrsetbits32(®->dsc_con, mask, val);
|
||||
clrsetbits32(®->dsc_inten, DSC_INTEN_SEL, 0x7F);
|
||||
clrsetbits32(®->dsc_intack, DSC_INTACK_SEL, DSC_INTACK_BUF_UNDERFLOW);
|
||||
|
||||
write32(®->dsc_spr, 0x0);
|
||||
|
||||
val = w | (pic_group_width - 1) << 16;
|
||||
write32(®->pic_w, val);
|
||||
|
||||
val = (h - 1) | (pic_height_ext_num * dsc_cfg->slice_height - 1) << 16;
|
||||
write32(®->pic_h, val);
|
||||
|
||||
val = dsc_cfg->slice_width | (slice_group_width - 1) << 16;
|
||||
write32(®->dsc_slice_w, val);
|
||||
|
||||
val = (dsc_cfg->slice_height - 1) | (pic_height_ext_num - 1) << 16 |
|
||||
(dsc_cfg->slice_width % 3) << 30;
|
||||
write32(®->dsc_slice_h, val);
|
||||
|
||||
val = dsc_cfg->slice_chunk_size |
|
||||
(((dsc_cfg->slice_chunk_size << slice_mode) + 2) / 3) << 16;
|
||||
write32(®->chunk_size, val);
|
||||
|
||||
mask = GENMASK(23, 0);
|
||||
val = dsc_cfg->slice_chunk_size * dsc_cfg->slice_height;
|
||||
clrsetbits32(®->dsc_buf_size, mask, val);
|
||||
|
||||
mask = BIT(0) | BIT(2) | GENMASK(11, 8);
|
||||
val = slice_mode | rgb_swap << 2 | init_delay_height << 8;
|
||||
clrsetbits32(®->dsc_mode, mask, val);
|
||||
|
||||
write32(®->dsc_cfg, dsc_cfg_mode);
|
||||
|
||||
mask = GENMASK(2, 0);
|
||||
val = pad_num;
|
||||
clrsetbits32(®->dsc_pad, mask, val);
|
||||
|
||||
val = dsc_cfg->slice_width | dsc_cfg->pic_width << 16;
|
||||
write32(®->dsc_enc_width, val);
|
||||
|
||||
mask = DSC_PIC_PREPAD_HEIGHT_SEL | DSC_PIC_PREPAD_WIDTH_SEL;
|
||||
val = h | w << 16;
|
||||
clrsetbits32(®->dsc_pic_pre_pad_size, mask, val);
|
||||
|
||||
setbits32(®->dsc_dbg_con, BIT(9));
|
||||
|
||||
write32(®->dsc_obuf, 0x410);
|
||||
|
||||
DEFINE_BITFIELD(LINE_BUF_DEPTH, 3, 0)
|
||||
DEFINE_BITFIELD(BITS_PER_COMPONENT, 7, 4)
|
||||
DEFINE_BITFIELD(BITS_PER_PIXEL, 17, 8)
|
||||
DEFINE_BIT(CONVERT_RGB, 18)
|
||||
DEFINE_BIT(BLOCK_PRED_ENABLE, 19)
|
||||
SET32_BITFIELDS(®->dsc_pps[0],
|
||||
LINE_BUF_DEPTH, DEF(dsc_cfg->line_buf_depth, 0x9),
|
||||
BITS_PER_COMPONENT, DEF(dsc_cfg->bits_per_component, 0x8),
|
||||
BITS_PER_PIXEL, DEF(dsc_cfg->bits_per_pixel, 0x80),
|
||||
CONVERT_RGB, DEF((u8)dsc_cfg->convert_rgb, 1),
|
||||
BLOCK_PRED_ENABLE, DEF((u8)dsc_cfg->block_pred_enable, 0));
|
||||
|
||||
DEFINE_BITFIELD(INITIAL_DEC_DELAY, 31, 16)
|
||||
DEFINE_BITFIELD(INITIAL_XMIT_DELAY, 15, 0)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[1],
|
||||
INITIAL_DEC_DELAY, DEF(dsc_cfg->initial_dec_delay, 0x268),
|
||||
INITIAL_XMIT_DELAY, DEF(dsc_cfg->initial_xmit_delay, 0x200));
|
||||
|
||||
DEFINE_BITFIELD(INITIAL_SCALE_VALUE, 15, 0)
|
||||
DEFINE_BITFIELD(SCALE_INCREMENT_INTERVAL, 31, 16)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[2],
|
||||
INITIAL_SCALE_VALUE, DEF(dsc_cfg->initial_scale_value, 0x20),
|
||||
SCALE_INCREMENT_INTERVAL,
|
||||
DEF(dsc_cfg->scale_increment_interval, 0x387));
|
||||
|
||||
DEFINE_BITFIELD(FIRST_LINE_BPG_OFFSET, 31, 16)
|
||||
DEFINE_BITFIELD(SCALE_DECREMENT_INTERVAL, 15, 0)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[3],
|
||||
FIRST_LINE_BPG_OFFSET, DEF(dsc_cfg->first_line_bpg_offset, 0xc),
|
||||
SCALE_DECREMENT_INTERVAL,
|
||||
DEF(dsc_cfg->scale_decrement_interval, 0xa));
|
||||
|
||||
DEFINE_BITFIELD(NFL_BPG_OFFSET, 15, 0)
|
||||
DEFINE_BITFIELD(SLICE_BPG_OFFSET, 31, 16)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[4],
|
||||
NFL_BPG_OFFSET, DEF(dsc_cfg->nfl_bpg_offset, 0x319),
|
||||
SLICE_BPG_OFFSET, DEF(dsc_cfg->slice_bpg_offset, 0x263));
|
||||
|
||||
DEFINE_BITFIELD(INITIAL_OFFSET, 15, 0)
|
||||
DEFINE_BITFIELD(FINAL_OFFSET, 31, 16)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[5],
|
||||
INITIAL_OFFSET, DEF(dsc_cfg->initial_offset, 0x1800),
|
||||
FINAL_OFFSET, DEF(dsc_cfg->final_offset, 0x10f0));
|
||||
|
||||
DEFINE_BITFIELD(FLATNESS_MIN_QP, 4, 0)
|
||||
DEFINE_BITFIELD(FLATNESS_MAX_QP, 12, 8)
|
||||
DEFINE_BITFIELD(RC_MODEL_SIZE, 31, 16)
|
||||
SET32_BITFIELDS(®->dsc_pps[6],
|
||||
FLATNESS_MIN_QP, DEF(dsc_cfg->flatness_min_qp, 0x3),
|
||||
FLATNESS_MAX_QP, DEF(dsc_cfg->flatness_max_qp, 0xc),
|
||||
RC_MODEL_SIZE, DEF(dsc_cfg->rc_model_size, 0x2000));
|
||||
|
||||
DEFINE_BITFIELD(RC_TGT_OFFSET_LOW, 31, 28)
|
||||
DEFINE_BITFIELD(RC_TGT_OFFSET_HIGH, 27, 24)
|
||||
DEFINE_BITFIELD(RC_QUANT_INCR_LIMIT1, 20, 16)
|
||||
DEFINE_BITFIELD(RC_QUANT_INCR_LIMIT0, 12, 8)
|
||||
DEFINE_BITFIELD(RC_EDGE_FACTOR, 7, 0)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[7],
|
||||
RC_TGT_OFFSET_LOW, dsc_cfg->rc_tgt_offset_low,
|
||||
RC_TGT_OFFSET_HIGH, dsc_cfg->rc_tgt_offset_high,
|
||||
RC_QUANT_INCR_LIMIT1, dsc_cfg->rc_quant_incr_limit1,
|
||||
RC_QUANT_INCR_LIMIT0, dsc_cfg->rc_quant_incr_limit0,
|
||||
RC_EDGE_FACTOR, dsc_cfg->rc_edge_factor);
|
||||
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_3, 31, 24)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_2, 23, 16)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_1, 15, 8)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_0, 7, 0)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[8],
|
||||
RC_BUF_THRESH_3, dsc_cfg->rc_buf_thresh[3],
|
||||
RC_BUF_THRESH_2, dsc_cfg->rc_buf_thresh[2],
|
||||
RC_BUF_THRESH_1, dsc_cfg->rc_buf_thresh[1],
|
||||
RC_BUF_THRESH_0, dsc_cfg->rc_buf_thresh[0]);
|
||||
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_7, 31, 24)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_6, 23, 16)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_5, 15, 8)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_4, 7, 0)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[9],
|
||||
RC_BUF_THRESH_7, dsc_cfg->rc_buf_thresh[7],
|
||||
RC_BUF_THRESH_6, dsc_cfg->rc_buf_thresh[6],
|
||||
RC_BUF_THRESH_5, dsc_cfg->rc_buf_thresh[5],
|
||||
RC_BUF_THRESH_4, dsc_cfg->rc_buf_thresh[4]);
|
||||
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_11, 31, 24)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_10, 23, 16)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_9, 15, 8)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_8, 7, 0)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[10],
|
||||
RC_BUF_THRESH_11, dsc_cfg->rc_buf_thresh[11],
|
||||
RC_BUF_THRESH_10, dsc_cfg->rc_buf_thresh[10],
|
||||
RC_BUF_THRESH_9, dsc_cfg->rc_buf_thresh[9],
|
||||
RC_BUF_THRESH_8, dsc_cfg->rc_buf_thresh[8]);
|
||||
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_13, 15, 8)
|
||||
DEFINE_BITFIELD(RC_BUF_THRESH_12, 7, 0)
|
||||
WRITE32_BITFIELDS(®->dsc_pps[11],
|
||||
RC_BUF_THRESH_13, dsc_cfg->rc_buf_thresh[13],
|
||||
RC_BUF_THRESH_12, dsc_cfg->rc_buf_thresh[12]);
|
||||
|
||||
DEFINE_BITFIELD(RC_RANGE_BPG_OFFSET_ODD, 31, 26)
|
||||
DEFINE_BITFIELD(RC_RANGE_MAX_QP_ODD, 25, 21)
|
||||
DEFINE_BITFIELD(RC_RANGE_MIN_QP_ODD, 20, 16)
|
||||
DEFINE_BITFIELD(RC_RANGE_BPG_OFFSET_EVEN, 15, 10)
|
||||
DEFINE_BITFIELD(RC_RANGE_MAX_QP_EVEN, 9, 5)
|
||||
DEFINE_BITFIELD(RC_RANGE_MIN_QP_EVEN, 4, 0)
|
||||
for (int i = 0; i < 7; i++) {
|
||||
WRITE32_BITFIELDS(®->dsc_pps_rc_range_params[i],
|
||||
RC_RANGE_BPG_OFFSET_ODD,
|
||||
dsc_cfg->rc_range_params[2 * i + 1].range_bpg_offset,
|
||||
RC_RANGE_MAX_QP_ODD,
|
||||
dsc_cfg->rc_range_params[2 * i + 1].range_max_qp,
|
||||
RC_RANGE_MIN_QP_ODD,
|
||||
dsc_cfg->rc_range_params[2 * i + 1].range_min_qp,
|
||||
RC_RANGE_BPG_OFFSET_EVEN,
|
||||
dsc_cfg->rc_range_params[2 * i].range_bpg_offset,
|
||||
RC_RANGE_MAX_QP_EVEN,
|
||||
dsc_cfg->rc_range_params[2 * i].range_max_qp,
|
||||
RC_RANGE_MIN_QP_EVEN,
|
||||
dsc_cfg->rc_range_params[2 * i].range_min_qp);
|
||||
}
|
||||
/* Special case for the last register */
|
||||
WRITE32_BITFIELDS(®->dsc_pps_rc_range_params[7],
|
||||
RC_RANGE_BPG_OFFSET_EVEN,
|
||||
dsc_cfg->rc_range_params[14].range_bpg_offset,
|
||||
RC_RANGE_MAX_QP_EVEN, dsc_cfg->rc_range_params[14].range_max_qp,
|
||||
RC_RANGE_MIN_QP_EVEN, dsc_cfg->rc_range_params[14].range_min_qp);
|
||||
|
||||
if (dsc_cfg->dsc_version_minor == 1)
|
||||
write32(®->dsc_shadow, 0x20);
|
||||
else if (dsc_cfg->dsc_version_minor == 2)
|
||||
write32(®->dsc_shadow, 0x40);
|
||||
else
|
||||
printk(BIOS_WARNING, "%s : wrong version minor:%d\n", __func__,
|
||||
dsc_cfg->dsc_version_minor);
|
||||
}
|
||||
|
||||
static void dsc_config(struct disp_dsc_regs *reg, u16 w, u16 h,
|
||||
const struct dsc_config *dsc_cfg)
|
||||
{
|
||||
bool dsc_enable;
|
||||
|
||||
dsc_enable = (dsc_cfg && dsc_cfg->dsc_version_major);
|
||||
printk(BIOS_INFO, "%s: w:%d, h:%d, dsc enable:%d\n", __func__, w, h, dsc_enable);
|
||||
|
||||
if (!dsc_enable) {
|
||||
setbits32(®->dsc_con, DSC_RELAY);
|
||||
clrsetbits32(®->chunk_size, GENMASK(31, 16), w << 16);
|
||||
clrsetbits32(®->pic_w, GENMASK(15, 0), w);
|
||||
clrsetbits32(®->pic_h, GENMASK(15, 0), h);
|
||||
printk(BIOS_INFO, "%s: DSC_relay mode\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
dsc_configure_registers(reg, w, h, dsc_cfg);
|
||||
}
|
||||
|
||||
static void blender_config(struct blender *reg, u16 width, u16 height,
|
||||
enum mtk_disp_blender_layer type)
|
||||
{
|
||||
|
|
@ -157,17 +420,9 @@ static void dither_start(struct disp_dither_regs *reg)
|
|||
setbits32(®->en, BIT(0));
|
||||
}
|
||||
|
||||
static void dsc_config(struct disp_dsc_regs *reg, u16 width, u16 height)
|
||||
{
|
||||
write32(®->dsc_con, BIT(5));
|
||||
write32(®->pic_w, width);
|
||||
write32(®->pic_h, height);
|
||||
write32(®->chunk_size, width << 16);
|
||||
}
|
||||
|
||||
static void dsc_start(struct disp_dsc_regs *reg)
|
||||
{
|
||||
setbits32(®->dsc_con, BIT(0));
|
||||
setbits32(®->dsc_con, DSC_EN);
|
||||
}
|
||||
|
||||
static void ovlsys_path_connect(struct ovlsys_cfg *reg)
|
||||
|
|
@ -325,7 +580,8 @@ static void disp_config_blender(struct blender *const blenders[], size_t size, u
|
|||
}
|
||||
}
|
||||
|
||||
static void main_disp_path_setup(u16 width, u16 height, u32 vrefresh, enum disp_path_sel path)
|
||||
static void main_disp_path_setup(u16 width, u16 height, u32 vrefresh, enum disp_path_sel path,
|
||||
const struct dsc_config *dsc_cfg)
|
||||
{
|
||||
u16 w = width;
|
||||
size_t num_pipe = DUAL_PIPE(path) ? 2 : 1;
|
||||
|
|
@ -361,7 +617,7 @@ static void main_disp_path_setup(u16 width, u16 height, u32 vrefresh, enum disp_
|
|||
postmask_start(pipes[i].postmask);
|
||||
dither_config(pipes[i].dither, w, height);
|
||||
dither_start(pipes[i].dither);
|
||||
dsc_config(pipes[i].dsc, w, height);
|
||||
dsc_config(pipes[i].dsc, w, height, dsc_cfg);
|
||||
dsc_start(pipes[i].dsc);
|
||||
}
|
||||
|
||||
|
|
@ -435,7 +691,7 @@ void mtk_ddp_soc_mode_set(u32 fmt, u32 bpp, u32 width, u32 height, u32 vrefresh,
|
|||
if (width > 0x1FFF || height > 0x1FFF)
|
||||
printk(BIOS_WARNING, "%s: w/h: %d/%d exceed hw limit %u\n", __func__,
|
||||
width, height, 0x1FFF);
|
||||
main_disp_path_setup(width, height, vrefresh, path);
|
||||
main_disp_path_setup(width, height, vrefresh, path, dsc_config);
|
||||
ovlsys_layer_config(fmt, bpp, width, height, path);
|
||||
}
|
||||
|
||||
|
|
|
|||
88
src/soc/mediatek/mt8196/dsi.c
Normal file
88
src/soc/mediatek/mt8196/dsi.c
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
|
||||
#include <assert.h>
|
||||
#include <delay.h>
|
||||
#include <device/mmio.h>
|
||||
#include <soc/dsi.h>
|
||||
#include <soc/pll.h>
|
||||
|
||||
#define RG_DSI_PRD_REF_SEL GENMASK(5, 0)
|
||||
#define RG_DSI_PRD_REF_MINI 0
|
||||
#define RG_DSI_PRD_REF_DEF 4
|
||||
#define RG_DSI_PRD_REF_MAX 7
|
||||
|
||||
void mtk_dsi_configure_mipi_tx(struct mipi_tx_regs *mipi_tx_reg, u32 data_rate,
|
||||
u32 lanes, bool is_cphy)
|
||||
{
|
||||
unsigned int txdiv0;
|
||||
u64 pcw;
|
||||
|
||||
/* Select different voltage when different data rate */
|
||||
if (data_rate < (u32)2500 * MHz) {
|
||||
clrsetbits32(&mipi_tx_reg->pll_con1, RG_DSI_PRD_REF_SEL, RG_DSI_PRD_REF_MINI);
|
||||
write32(&mipi_tx_reg->cdphy_preserved, 0xFFFF00F0);
|
||||
} else {
|
||||
clrsetbits32(&mipi_tx_reg->pll_con1, RG_DSI_PRD_REF_SEL, RG_DSI_PRD_REF_DEF);
|
||||
write32(&mipi_tx_reg->cdphy_preserved, 0xFFFF0030);
|
||||
}
|
||||
|
||||
if (data_rate >= 2000 * MHz) {
|
||||
txdiv0 = 0;
|
||||
} else if (data_rate >= 1000 * MHz) {
|
||||
txdiv0 = 1;
|
||||
} else if (data_rate >= 500 * MHz) {
|
||||
txdiv0 = 2;
|
||||
} else if (data_rate > 250 * MHz) {
|
||||
/* (data_rate == 250MHz) is a special case that should go to the
|
||||
else-block below (txdiv0 = 4) */
|
||||
txdiv0 = 3;
|
||||
} else {
|
||||
/* MIN = 125 */
|
||||
assert(data_rate >= MTK_DSI_DATA_RATE_MIN_MHZ * MHz);
|
||||
txdiv0 = 4;
|
||||
}
|
||||
|
||||
if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy)
|
||||
mtk_dsi_cphy_lane_sel_setting(mipi_tx_reg);
|
||||
|
||||
clrbits32(&mipi_tx_reg->pll_con4, BIT(11) | BIT(10));
|
||||
setbits32(&mipi_tx_reg->pll_pwr, AD_DSI_PLL_SDM_PWR_ON);
|
||||
udelay(30);
|
||||
clrbits32(&mipi_tx_reg->pll_pwr, AD_DSI_PLL_SDM_ISO_EN);
|
||||
|
||||
pcw = (u64)(data_rate >> 1) * (1 << txdiv0);
|
||||
pcw <<= 24;
|
||||
pcw /= CLK26M_HZ;
|
||||
|
||||
write32(&mipi_tx_reg->pll_con0, pcw);
|
||||
clrsetbits32(&mipi_tx_reg->pll_con1, RG_DSI_PLL_POSDIV, txdiv0 << 8);
|
||||
udelay(30);
|
||||
setbits32(&mipi_tx_reg->pll_con1, RG_DSI_PLL_EN);
|
||||
|
||||
/* BG_LPF_EN / BG_CORE_EN */
|
||||
write32(&mipi_tx_reg->lane_con, 0x3FFF0180);
|
||||
udelay(40);
|
||||
write32(&mipi_tx_reg->lane_con, 0x3FFF00C0);
|
||||
|
||||
if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy)
|
||||
mtk_dsi_cphy_enable(mipi_tx_reg);
|
||||
|
||||
/* Switch OFF each Lane */
|
||||
clrbits32(&mipi_tx_reg->d0_sw_ctl_en, DSI_SW_CTL_EN);
|
||||
clrbits32(&mipi_tx_reg->d1_sw_ctl_en, DSI_SW_CTL_EN);
|
||||
clrbits32(&mipi_tx_reg->d2_sw_ctl_en, DSI_SW_CTL_EN);
|
||||
clrbits32(&mipi_tx_reg->d3_sw_ctl_en, DSI_SW_CTL_EN);
|
||||
clrbits32(&mipi_tx_reg->ck_sw_ctl_en, DSI_SW_CTL_EN);
|
||||
|
||||
if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy)
|
||||
mtk_dsi_cphy_disable_ck_mode(mipi_tx_reg);
|
||||
else
|
||||
mtk_dsi_dphy_disable_ck_mode(mipi_tx_reg);
|
||||
}
|
||||
|
||||
void mtk_dsi_reset(struct dsi_regs *dsi_reg)
|
||||
{
|
||||
write32(&dsi_reg->dsi_shadow_ctrl, DSI_FORCE_COMMIT_ALWAYS);
|
||||
write32(&dsi_reg->dsi_con_ctrl, 1);
|
||||
write32(&dsi_reg->dsi_con_ctrl, 0);
|
||||
}
|
||||
|
|
@ -539,17 +539,55 @@ check_member(disp_postmask_regs, size, 0x30);
|
|||
|
||||
struct disp_dsc_regs {
|
||||
u32 dsc_con;
|
||||
u32 reserved0x4[5];
|
||||
u32 dsc_inten;
|
||||
u32 dsc_intsta;
|
||||
u32 dsc_intack;
|
||||
u32 dsc_sta;
|
||||
u32 dsc_spr;
|
||||
u32 pic_w;
|
||||
u32 pic_h;
|
||||
u32 reserved0x20;
|
||||
u32 reserved0x24;
|
||||
u32 dsc_slice_w;
|
||||
u32 dsc_slice_h;
|
||||
u32 chunk_size;
|
||||
u32 dsc_buf_size;
|
||||
u32 dsc_mode;
|
||||
u32 dsc_cfg;
|
||||
u32 dsc_pad;
|
||||
u32 dsc_enc_width;
|
||||
u32 dsc_pic_pre_pad_size;
|
||||
u32 dsc_pic_pre_pad_value;
|
||||
u32 reserved0[6];
|
||||
u32 dsc_dbg_con;
|
||||
u32 dsc_cksm_mon0;
|
||||
u32 dsc_cksm_mon1;
|
||||
u32 dsc_resv;
|
||||
u32 dsc_obuf;
|
||||
u32 dsc_obuf_mon;
|
||||
u32 dsc_mute_con;
|
||||
u32 dsc_ddren;
|
||||
u32 dsc_pps[12];
|
||||
u32 dsc_pps_rc_range_params[8];
|
||||
u32 reserved1[12];
|
||||
u32 dsc_dbg[21];
|
||||
u32 reserved2[3];
|
||||
u32 dsc_enc_dbg[5];
|
||||
u32 dsc_enc_cov;
|
||||
u32 reserved3[34];
|
||||
u32 dsc_shadow;
|
||||
u32 reserved4[7];
|
||||
u32 dsc_dummy;
|
||||
u32 reserved5;
|
||||
u32 shadow_ctrl;
|
||||
u32 reserved6;
|
||||
u32 dsc_up_inten;
|
||||
u32 dsc_up_intsta;
|
||||
u32 dsc_up_intack;
|
||||
};
|
||||
check_member(disp_dsc_regs, dsc_con, 0x0);
|
||||
check_member(disp_dsc_regs, pic_w, 0x18);
|
||||
check_member(disp_dsc_regs, pic_h, 0x1C);
|
||||
check_member(disp_dsc_regs, chunk_size, 0x28);
|
||||
check_member(disp_dsc_regs, dsc_slice_w, 0x20);
|
||||
check_member(disp_dsc_regs, dsc_dbg_con, 0x60);
|
||||
check_member(disp_dsc_regs, dsc_enc_cov, 0x174);
|
||||
check_member(disp_dsc_regs, dsc_shadow, 0x200);
|
||||
check_member(disp_dsc_regs, dsc_up_intack, 0x238);
|
||||
|
||||
static struct disp_mdp_rsz_regs *const disp_mdp_rsz0_reg = (void *)DISP_MDP_RSZ0_BASE;
|
||||
static struct disp_mdp_rsz_regs *const disp_mdp_rsz1_reg = (void *)DISP_MDP_RSZ1_BASE;
|
||||
|
|
|
|||
29
src/soc/mediatek/mt8196/mtk_mipi_dphy.c
Normal file
29
src/soc/mediatek/mt8196/mtk_mipi_dphy.c
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
|
||||
#include <device/mmio.h>
|
||||
#include <soc/dsi.h>
|
||||
|
||||
#define PHY_TIMING_DIV(x) (((x) / 8000) + 1)
|
||||
|
||||
void mtk_dsi_dphy_timing_calculation(u32 data_rate_mhz, struct mtk_phy_timing *timing)
|
||||
{
|
||||
u32 temp;
|
||||
timing->lpx = ALIGN_UP(PHY_TIMING_DIV(80 * data_rate_mhz), 2);
|
||||
timing->da_hs_prepare = ALIGN_UP(PHY_TIMING_DIV(59 * data_rate_mhz + 4 * 1000), 2);
|
||||
timing->da_hs_zero = PHY_TIMING_DIV(163 * data_rate_mhz + 11 * 1000) -
|
||||
timing->da_hs_prepare;
|
||||
|
||||
temp = data_rate_mhz < 740 ? 443 : 420;
|
||||
timing->da_hs_trail = PHY_TIMING_DIV(66 * data_rate_mhz + temp * 100);
|
||||
timing->ta_go = 4 * timing->lpx;
|
||||
timing->ta_sure = 3 * timing->lpx / 2;
|
||||
timing->ta_get = 5 * timing->lpx;
|
||||
timing->da_hs_exit = ALIGN_UP(PHY_TIMING_DIV(118 * data_rate_mhz), 2);
|
||||
timing->da_hs_sync = 1;
|
||||
|
||||
timing->clk_hs_prepare = ALIGN_UP(PHY_TIMING_DIV(57 * data_rate_mhz), 2);
|
||||
timing->clk_hs_post = PHY_TIMING_DIV(65 * data_rate_mhz + 53 * 1000);
|
||||
timing->clk_hs_trail = PHY_TIMING_DIV(65 * data_rate_mhz + 52 * 1000);
|
||||
timing->clk_hs_zero = PHY_TIMING_DIV(330 * data_rate_mhz) - timing->clk_hs_prepare;
|
||||
timing->clk_hs_exit = PHY_TIMING_DIV(118 * data_rate_mhz);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue