131 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
 | |
|  *
 | |
|  * Author: Guochun Huang <hero.huang@rock-chips.com>
 | |
|  */
 | |
| 
 | |
| #include <asm/unaligned.h>
 | |
| #include <dt-bindings/mfd/rockchip-serdes.h>
 | |
| #include "rkx110_reg.h"
 | |
| #include "rkx110_x120.h"
 | |
| #include "rkx110_dsi_rx.h"
 | |
| #include "serdes_combphy.h"
 | |
| 
 | |
| #define DSI_RX_MIPI_IDX_CTRL0(x)	(0x0100 + x * 8)
 | |
| #define SW_COMMAND_MODE_EN		BIT(26)
 | |
| #define SW_DT(x)			UPDATE(x, 15, 10)
 | |
| #define SW_VC(x)			UPDATE(x, 9, 8)
 | |
| #define SW_CAP_EN			BIT(0)
 | |
| #define DSI_RX_MIPI_IDX_CTRL1(x)	(0X0104 + x * 8)
 | |
| #define SW_HEIGHT(x)			UPDATE(x, 29, 16)
 | |
| #define SW_WIDTH(x)			UPDATE(x, 13, 0)
 | |
| #define DSI_RX_MIPI_INTEN		0x0174
 | |
| #define DSI_RX_MIPI_INTSTAT		0x0178
 | |
| #define DSI_RX_MIPI_SIZE_NUM(x)		(0x01c0 + x * 4)
 | |
| 
 | |
| enum {
 | |
| 	VSYNC_START = 0x01,
 | |
| 	VSYNC_END = 0x11,
 | |
| 	HSYNC_START = 0x21,
 | |
| 	HSYNC_END = 0x31,
 | |
| };
 | |
| 
 | |
| static inline int dsi_rx_write(struct rk_serdes *ser, u32 reg, u32 val)
 | |
| {
 | |
| 	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
 | |
| 
 | |
| 	return ser->i2c_write_reg(client, reg, val);
 | |
| }
 | |
| 
 | |
| static inline int dsi_rx_read(struct rk_serdes *ser, u32 reg, u32 *val)
 | |
| {
 | |
| 	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
 | |
| 
 | |
| 	return ser->i2c_read_reg(client, reg, val);
 | |
| }
 | |
| 
 | |
| static inline int dsi_rx_update_bits(struct rk_serdes *ser,
 | |
| 				     u32 reg, u32 mask, u32 val)
 | |
| {
 | |
| 	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
 | |
| 
 | |
| 	return ser->i2c_update_bits(client, reg, mask, val);
 | |
| }
 | |
| 
 | |
| void rkx110_dsi_rx_enable(struct rk_serdes *ser, struct rk_serdes_route *route, int id)
 | |
| {
 | |
| 	struct rk_serdes_panel *sd_panel = container_of(route, struct rk_serdes_panel, route);
 | |
| 	struct rkx110_dsi_rx *dsi = &sd_panel->dsi_rx;
 | |
| 	struct rkx110_combrxphy *combrxphy = &sd_panel->combrxphy;
 | |
| 	const struct videomode *vm = &route->vm;
 | |
| 	unsigned long pixelclock;
 | |
| 	u32 hactive, vactive;
 | |
| 	u64 rate;
 | |
| 	u32 val = 0;
 | |
| 	u32 csi_base, dsirx_base;
 | |
| 
 | |
| 	switch (route->frame_mode) {
 | |
| 	case SERDES_SP_PIXEL_INTERLEAVED:
 | |
| 		fallthrough;
 | |
| 	case SERDES_SP_LEFT_RIGHT_SPLIT:
 | |
| 		pixelclock = vm->pixelclock * 2;
 | |
| 		hactive = vm->hactive * 2;
 | |
| 		vactive = vm->vactive;
 | |
| 		break;
 | |
| 	case SERDES_SP_LINE_INTERLEAVED:
 | |
| 		pixelclock = vm->pixelclock * 2;
 | |
| 		vactive = vm->vactive * 2;
 | |
| 		hactive = vm->hactive;
 | |
| 		break;
 | |
| 	case SERDES_FRAME_NORMAL_MODE:
 | |
| 		fallthrough;
 | |
| 	default:
 | |
| 		pixelclock = vm->pixelclock;
 | |
| 		hactive = vm->hactive;
 | |
| 		vactive = vm->vactive;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	rate = DIV_ROUND_CLOSEST_ULL(pixelclock, dsi->lanes);
 | |
| 
 | |
| 	rkx110_combrxphy_set_mode(combrxphy, COMBRX_PHY_MODE_VIDEO_MIPI);
 | |
| 	rkx110_combrxphy_set_rate(combrxphy, rate * MSEC_PER_SEC);
 | |
| 	rkx110_combrxphy_set_lanes(combrxphy, dsi->lanes);
 | |
| 	rkx110_combrxphy_power_on(ser, combrxphy, DEVICE_LOCAL, id ? COMBPHY_1 : COMBPHY_0);
 | |
| 
 | |
| 	csi_base = id ? RKX110_CSI2HOST1_BASE : RKX110_CSI2HOST0_BASE;
 | |
| 	dsirx_base = id ? RKX110_DSI_RX1_BASE : RKX110_DSI_RX0_BASE;
 | |
| 
 | |
| 
 | |
| 	dsi_rx_write(ser, csi_base + CSI2HOST_N_LANES, dsi->lanes - 1);
 | |
| 	dsi_rx_write(ser, csi_base + CSI2HOST_RESETN, 0);
 | |
| 
 | |
| 	val |= SW_DSI_EN | SW_DATETYPE_FE(VSYNC_END) | SW_DATETYPE_FS(VSYNC_START);
 | |
| 	dsi_rx_update_bits(ser, csi_base + CSI2HOST_CONTROL,
 | |
| 			   SW_DATETYPE_FE_MASK |
 | |
| 			   SW_DATETYPE_FS_MASK |
 | |
| 			   SW_DSI_EN, val);
 | |
| 
 | |
| 	dsi_rx_write(ser, csi_base + CSI2HOST_RESETN, 1);
 | |
| 
 | |
| 	val = SW_CAP_EN | SW_VC(0);
 | |
| 	/*
 | |
| 	 * video mode only support rgb888(0x3e), command mode
 | |
| 	 * only support DCS Long Write(0x39)
 | |
| 	 */
 | |
| 	val |= (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO) ?
 | |
| 	       (0 | SW_DT(0x3e)) : (SW_COMMAND_MODE_EN | SW_DT(0x39));
 | |
| 	dsi_rx_write(ser, dsirx_base + DSI_RX_MIPI_IDX_CTRL0(0), val);
 | |
| 	dsi_rx_write(ser, dsirx_base + DSI_RX_MIPI_IDX_CTRL1(0),
 | |
| 		     SW_HEIGHT(vactive) | SW_WIDTH(hactive));
 | |
| }
 | |
| 
 | |
| void rkx110_dsi_rx_disable(struct rk_serdes *ser, struct rk_serdes_route *route, int id)
 | |
| {
 | |
| 	struct rk_serdes_panel *sd_panel = container_of(route, struct rk_serdes_panel, route);
 | |
| 	struct rkx110_combrxphy *combrxphy = &sd_panel->combrxphy;
 | |
| 
 | |
| 	rkx110_combrxphy_power_off(ser, combrxphy, DEVICE_LOCAL, id ? COMBPHY_1 : COMBPHY_0);
 | |
| }
 |