448 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
/*
 | 
						|
 * techpoint dev driver
 | 
						|
 *
 | 
						|
 * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
 | 
						|
 *
 | 
						|
 * V0.0X01.0X00 first version.
 | 
						|
 */
 | 
						|
 | 
						|
#include "techpoint_dev.h"
 | 
						|
#include "techpoint_tp9930.h"
 | 
						|
#include "techpoint_tp9950.h"
 | 
						|
#include "techpoint_tp2855.h"
 | 
						|
#include "techpoint_tp2815.h"
 | 
						|
#include "techpoint_tp9951.h"
 | 
						|
 | 
						|
static DEFINE_MUTEX(reg_sem);
 | 
						|
 | 
						|
int techpoint_write_reg(struct i2c_client *client, u8 reg, u8 val)
 | 
						|
{
 | 
						|
	struct i2c_msg msg;
 | 
						|
	u8 buf[2];
 | 
						|
	int ret;
 | 
						|
 | 
						|
	buf[0] = reg & 0xFF;
 | 
						|
	buf[1] = val;
 | 
						|
 | 
						|
	msg.addr = client->addr;
 | 
						|
	msg.flags = client->flags;
 | 
						|
	msg.buf = buf;
 | 
						|
	msg.len = sizeof(buf);
 | 
						|
 | 
						|
	ret = i2c_transfer(client->adapter, &msg, 1);
 | 
						|
	if (ret >= 0) {
 | 
						|
		usleep_range(300, 400);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	dev_err(&client->dev,
 | 
						|
		"techpoint write reg(0x%x val:0x%x) failed !\n", reg, val);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int techpoint_write_array(struct i2c_client *client,
 | 
						|
			  const struct regval *regs, int size)
 | 
						|
{
 | 
						|
	int i, ret = 0;
 | 
						|
 | 
						|
	i = 0;
 | 
						|
 | 
						|
	while (i < size) {
 | 
						|
		ret = techpoint_write_reg(client, regs[i].addr, regs[i].val);
 | 
						|
		if (ret) {
 | 
						|
			dev_err(&client->dev, "%s failed !\n", __func__);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		i++;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int techpoint_read_reg(struct i2c_client *client, u8 reg, u8 *val)
 | 
						|
{
 | 
						|
	struct i2c_msg msg[2];
 | 
						|
	u8 buf[1];
 | 
						|
	int ret;
 | 
						|
 | 
						|
	buf[0] = reg & 0xFF;
 | 
						|
 | 
						|
	msg[0].addr = client->addr;
 | 
						|
	msg[0].flags = client->flags;
 | 
						|
	msg[0].buf = buf;
 | 
						|
	msg[0].len = sizeof(buf);
 | 
						|
 | 
						|
	msg[1].addr = client->addr;
 | 
						|
	msg[1].flags = client->flags | I2C_M_RD;
 | 
						|
	msg[1].buf = buf;
 | 
						|
	msg[1].len = 1;
 | 
						|
 | 
						|
	ret = i2c_transfer(client->adapter, msg, 2);
 | 
						|
	if (ret >= 0) {
 | 
						|
		*val = buf[0];
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	dev_err(&client->dev, "techpoint read reg(0x%x) failed !\n", reg);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int check_chip_id(struct techpoint *techpoint)
 | 
						|
{
 | 
						|
	struct i2c_client *client = techpoint->client;
 | 
						|
	struct device *dev = &client->dev;
 | 
						|
	unsigned char chip_id_h = 0xFF, chip_id_l = 0xFF;
 | 
						|
 | 
						|
	techpoint_read_reg(client, CHIP_ID_H_REG, &chip_id_h);
 | 
						|
	techpoint_read_reg(client, CHIP_ID_L_REG, &chip_id_l);
 | 
						|
	dev_err(dev, "chip_id_h:0x%2x chip_id_l:0x%2x\n", chip_id_h, chip_id_l);
 | 
						|
	if (chip_id_h == TP9930_CHIP_ID_H_VALUE &&
 | 
						|
	    chip_id_l == TP9930_CHIP_ID_L_VALUE) {		//tp2832
 | 
						|
		dev_info(&client->dev,
 | 
						|
			 "techpoint check chip id CHIP_TP9930 !\n");
 | 
						|
		techpoint->chip_id = CHIP_TP9930;
 | 
						|
		techpoint->input_type = TECHPOINT_DVP_BT1120;
 | 
						|
		return 0;
 | 
						|
	} else if (chip_id_h == TP2855_CHIP_ID_H_VALUE &&
 | 
						|
		   chip_id_l == TP2855_CHIP_ID_L_VALUE) {	//tp2855
 | 
						|
		dev_info(&client->dev,
 | 
						|
			 "techpoint check chip id CHIP_TP2855 !\n");
 | 
						|
		techpoint->chip_id = CHIP_TP2855;
 | 
						|
		techpoint->input_type = TECHPOINT_MIPI;
 | 
						|
		return 0;
 | 
						|
	} else if (chip_id_h == TP2815_CHIP_ID_H_VALUE &&
 | 
						|
		   chip_id_l == TP2815_CHIP_ID_L_VALUE) {	//tp2815
 | 
						|
		dev_info(&client->dev,
 | 
						|
			 "techpoint check chip id CHIP_TP2815 !\n");
 | 
						|
		techpoint->chip_id = CHIP_TP2855;
 | 
						|
		techpoint->input_type = TECHPOINT_MIPI;
 | 
						|
		return 0;
 | 
						|
	} else if (chip_id_h == TP9950_CHIP_ID_H_VALUE &&
 | 
						|
		   chip_id_l == TP9950_CHIP_ID_L_VALUE) {	//tp2850
 | 
						|
		dev_info(&client->dev,
 | 
						|
			 "techpoint check chip id CHIP_TP9950 !\n");
 | 
						|
		techpoint->chip_id = CHIP_TP9950;
 | 
						|
		techpoint->input_type = TECHPOINT_MIPI;
 | 
						|
		return 0;
 | 
						|
	} else if (chip_id_h == TP9951_CHIP_ID_H_VALUE &&
 | 
						|
		   chip_id_l == TP9951_CHIP_ID_L_VALUE) {	//tp2860
 | 
						|
		dev_info(&client->dev,
 | 
						|
			 "techpoint check chip id CHIP_TP9951 !\n");
 | 
						|
		techpoint->chip_id = CHIP_TP9951;
 | 
						|
		techpoint->input_type = TECHPOINT_MIPI;
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	dev_info(&client->dev, "techpoint check chip id failed !\n");
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
int techpoint_initialize_devices(struct techpoint *techpoint)
 | 
						|
{
 | 
						|
	if (check_chip_id(techpoint))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
		tp9930_initialize(techpoint);
 | 
						|
	else if (techpoint->chip_id == CHIP_TP2855)
 | 
						|
		tp2855_initialize(techpoint);
 | 
						|
	else if (techpoint->chip_id == CHIP_TP9950)
 | 
						|
		tp9950_initialize(techpoint);
 | 
						|
	else if (techpoint->chip_id == CHIP_TP9951)
 | 
						|
		tp9951_initialize(techpoint);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int detect_thread_function(void *data)
 | 
						|
{
 | 
						|
	struct techpoint *techpoint = (struct techpoint *)data;
 | 
						|
	struct i2c_client *client = techpoint->client;
 | 
						|
	u8 detect_status = 0, i;
 | 
						|
	int need_reset_wait = -1;
 | 
						|
 | 
						|
	if (techpoint->power_on) {
 | 
						|
		mutex_lock(®_sem);
 | 
						|
		if (techpoint->chip_id == CHIP_TP9930) {
 | 
						|
			tp9930_get_all_input_status(techpoint,
 | 
						|
						    techpoint->detect_status);
 | 
						|
			for (i = 0; i < PAD_MAX; i++)
 | 
						|
				tp9930_set_decoder_mode(client, i,
 | 
						|
							techpoint->detect_status[i]);
 | 
						|
		} else if (techpoint->chip_id == CHIP_TP2855) {
 | 
						|
			tp2855_get_all_input_status(techpoint,
 | 
						|
						    techpoint->detect_status);
 | 
						|
			for (i = 0; i < PAD_MAX; i++)
 | 
						|
				tp2855_set_decoder_mode(client, i,
 | 
						|
							techpoint->detect_status[i]);
 | 
						|
		}
 | 
						|
		mutex_unlock(®_sem);
 | 
						|
		techpoint->do_reset = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	while (!kthread_should_stop()) {
 | 
						|
		mutex_lock(®_sem);
 | 
						|
		if (techpoint->power_on) {
 | 
						|
			for (i = 0; i < PAD_MAX; i++) {
 | 
						|
				if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
					detect_status =
 | 
						|
					    tp9930_get_channel_input_status
 | 
						|
					    (techpoint, i);
 | 
						|
				else if (techpoint->chip_id == CHIP_TP2855)
 | 
						|
					detect_status =
 | 
						|
					    tp2855_get_channel_input_status
 | 
						|
					    (techpoint, i);
 | 
						|
				else if (techpoint->chip_id == CHIP_TP9951)
 | 
						|
					detect_status =
 | 
						|
					    tp9951_get_channel_input_status
 | 
						|
					    (techpoint, i);
 | 
						|
 | 
						|
				if (techpoint->detect_status[i] !=
 | 
						|
				    detect_status) {
 | 
						|
					if (!detect_status)
 | 
						|
						dev_info(&client->dev,
 | 
						|
							"detect channel %d video plug out\n",
 | 
						|
							i);
 | 
						|
					else
 | 
						|
						dev_info(&client->dev,
 | 
						|
							"detect channel %d video plug in\n",
 | 
						|
							i);
 | 
						|
 | 
						|
					if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
						tp9930_set_decoder_mode(client, i, detect_status);
 | 
						|
					else if (techpoint->chip_id == CHIP_TP2855)
 | 
						|
						tp2855_set_decoder_mode(client, i, detect_status);
 | 
						|
 | 
						|
					techpoint->detect_status[i] = detect_status;
 | 
						|
					need_reset_wait = 5;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (need_reset_wait > 0) {
 | 
						|
				need_reset_wait--;
 | 
						|
			} else if (need_reset_wait == 0) {
 | 
						|
				need_reset_wait = -1;
 | 
						|
				techpoint->do_reset = 1;
 | 
						|
				dev_info(&client->dev,
 | 
						|
					"trigger reset time up\n");
 | 
						|
			}
 | 
						|
		}
 | 
						|
		mutex_unlock(®_sem);
 | 
						|
		set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
		schedule_timeout(msecs_to_jiffies(200));
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int __maybe_unused detect_thread_start(struct techpoint *techpoint)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
	struct i2c_client *client = techpoint->client;
 | 
						|
 | 
						|
	techpoint->detect_thread = kthread_create(detect_thread_function,
 | 
						|
						  techpoint,
 | 
						|
						  "techpoint_kthread");
 | 
						|
	if (IS_ERR(techpoint->detect_thread)) {
 | 
						|
		dev_err(&client->dev,
 | 
						|
			"kthread_create techpoint_kthread failed\n");
 | 
						|
		ret = PTR_ERR(techpoint->detect_thread);
 | 
						|
		techpoint->detect_thread = NULL;
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
	wake_up_process(techpoint->detect_thread);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int __maybe_unused detect_thread_stop(struct techpoint *techpoint)
 | 
						|
{
 | 
						|
	if (techpoint->detect_thread)
 | 
						|
		kthread_stop(techpoint->detect_thread);
 | 
						|
	techpoint->detect_thread = NULL;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static __maybe_unused int auto_detect_channel_fmt(struct techpoint *techpoint)
 | 
						|
{
 | 
						|
	int ch = 0;
 | 
						|
	enum techpoint_support_reso reso = 0xff;
 | 
						|
	struct i2c_client *client = techpoint->client;
 | 
						|
 | 
						|
	mutex_lock(®_sem);
 | 
						|
 | 
						|
	for (ch = 0; ch < PAD_MAX; ch++) {
 | 
						|
		if (techpoint->chip_id == CHIP_TP9930) {
 | 
						|
			reso = tp9930_get_channel_reso(client, ch);
 | 
						|
			tp9930_set_channel_reso(client, ch, reso);
 | 
						|
		} else if (techpoint->chip_id == CHIP_TP2855) {
 | 
						|
			reso = tp2855_get_channel_reso(client, ch);
 | 
						|
			tp2855_set_channel_reso(client, ch, reso);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (techpoint->chip_id == CHIP_TP9950) {
 | 
						|
		reso = tp9950_get_channel_reso(client, 0);
 | 
						|
		tp9950_set_channel_reso(client, 0, reso);
 | 
						|
	}
 | 
						|
 | 
						|
	if (techpoint->chip_id == CHIP_TP9951) {
 | 
						|
		reso = tp9951_get_channel_reso(client, 0);
 | 
						|
		tp9951_set_channel_reso(client, 0, reso);
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_unlock(®_sem);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void __techpoint_get_vc_fmt_inf(struct techpoint *techpoint,
 | 
						|
				struct rkmodule_vc_fmt_info *inf)
 | 
						|
{
 | 
						|
	int ch = 0;
 | 
						|
	int val = 0;
 | 
						|
	enum techpoint_support_reso reso = 0xff;
 | 
						|
	struct i2c_client *client = techpoint->client;
 | 
						|
 | 
						|
	mutex_lock(®_sem);
 | 
						|
 | 
						|
	for (ch = 0; ch < PAD_MAX; ch++) {
 | 
						|
		if (techpoint->chip_id == CHIP_TP9930) {
 | 
						|
			reso = tp9930_get_channel_reso(client, ch);
 | 
						|
			techpoint->cur_video_mode->channel_reso[ch] = reso;
 | 
						|
		} else if (techpoint->chip_id == CHIP_TP2855) {
 | 
						|
			reso = tp2855_get_channel_reso(client, ch);
 | 
						|
			techpoint->cur_video_mode->channel_reso[ch] = reso;
 | 
						|
		}
 | 
						|
		val = reso;
 | 
						|
		switch (val) {
 | 
						|
		case TECHPOINT_S_RESO_1080P_30:
 | 
						|
			inf->width[ch] = 1920;
 | 
						|
			inf->height[ch] = 1080;
 | 
						|
			inf->fps[ch] = 30;
 | 
						|
			break;
 | 
						|
		case TECHPOINT_S_RESO_1080P_25:
 | 
						|
			inf->width[ch] = 1920;
 | 
						|
			inf->height[ch] = 1080;
 | 
						|
			inf->fps[ch] = 25;
 | 
						|
			break;
 | 
						|
		case TECHPOINT_S_RESO_720P_30:
 | 
						|
			inf->width[ch] = 1280;
 | 
						|
			inf->height[ch] = 720;
 | 
						|
			inf->fps[ch] = 30;
 | 
						|
			break;
 | 
						|
		case TECHPOINT_S_RESO_720P_25:
 | 
						|
			inf->width[ch] = 1280;
 | 
						|
			inf->height[ch] = 720;
 | 
						|
			inf->fps[ch] = 25;
 | 
						|
			break;
 | 
						|
		case TECHPOINT_S_RESO_SD:
 | 
						|
			inf->width[ch] = 720;
 | 
						|
			inf->height[ch] = 560;
 | 
						|
			inf->fps[ch] = 25;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
#if DEF_1080P
 | 
						|
			inf->width[ch] = 1920;
 | 
						|
			inf->height[ch] = 1080;
 | 
						|
			inf->fps[ch] = 25;
 | 
						|
#else
 | 
						|
			inf->width[ch] = 1280;
 | 
						|
			inf->height[ch] = 720;
 | 
						|
			inf->fps[ch] = 25;
 | 
						|
#endif
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_unlock(®_sem);
 | 
						|
}
 | 
						|
 | 
						|
void techpoint_get_vc_fmt_inf(struct techpoint *techpoint,
 | 
						|
			      struct rkmodule_vc_fmt_info *inf)
 | 
						|
{
 | 
						|
	mutex_lock(®_sem);
 | 
						|
 | 
						|
	if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
		tp9930_pll_reset(techpoint->client);
 | 
						|
 | 
						|
	techpoint_write_array(techpoint->client,
 | 
						|
			      techpoint->cur_video_mode->common_reg_list,
 | 
						|
			      techpoint->cur_video_mode->common_reg_size);
 | 
						|
 | 
						|
	if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
		tp9930_do_reset_pll(techpoint->client);
 | 
						|
 | 
						|
	mutex_unlock(®_sem);
 | 
						|
 | 
						|
	__techpoint_get_vc_fmt_inf(techpoint, inf);
 | 
						|
}
 | 
						|
 | 
						|
void techpoint_get_vc_hotplug_inf(struct techpoint *techpoint,
 | 
						|
				  struct rkmodule_vc_hotplug_info *inf)
 | 
						|
{
 | 
						|
	int ch = 0;
 | 
						|
	int detect_status = 0;
 | 
						|
 | 
						|
	memset(inf, 0, sizeof(*inf));
 | 
						|
 | 
						|
	mutex_lock(®_sem);
 | 
						|
 | 
						|
	for (ch = 0; ch < 4; ch++) {
 | 
						|
		if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
			detect_status =
 | 
						|
			    tp9930_get_channel_input_status(techpoint, ch);
 | 
						|
		else if (techpoint->chip_id == CHIP_TP2855)
 | 
						|
			detect_status =
 | 
						|
			    tp2855_get_channel_input_status(techpoint, ch);
 | 
						|
 | 
						|
		inf->detect_status |= detect_status << ch;
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_unlock(®_sem);
 | 
						|
}
 | 
						|
 | 
						|
void techpoint_set_quick_stream(struct techpoint *techpoint, u32 stream)
 | 
						|
{
 | 
						|
	if (techpoint->chip_id == CHIP_TP2855)
 | 
						|
		tp2855_set_quick_stream(techpoint, stream);
 | 
						|
}
 | 
						|
 | 
						|
int techpoint_start_video_stream(struct techpoint *techpoint)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
	struct i2c_client *client = techpoint->client;
 | 
						|
 | 
						|
	mutex_lock(®_sem);
 | 
						|
	if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
		tp9930_pll_reset(techpoint->client);
 | 
						|
	mutex_unlock(®_sem);
 | 
						|
 | 
						|
	auto_detect_channel_fmt(techpoint);
 | 
						|
	ret = techpoint_write_array(techpoint->client,
 | 
						|
				    techpoint->cur_video_mode->common_reg_list,
 | 
						|
				    techpoint->cur_video_mode->common_reg_size);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(&client->dev,
 | 
						|
			"%s common_reg_list failed", __func__);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_lock(®_sem);
 | 
						|
	if (techpoint->chip_id == CHIP_TP9930)
 | 
						|
		tp9930_do_reset_pll(techpoint->client);
 | 
						|
	mutex_unlock(®_sem);
 | 
						|
 | 
						|
	usleep_range(500 * 1000, 1000 * 1000);
 | 
						|
 | 
						|
	detect_thread_start(techpoint);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int techpoint_stop_video_stream(struct techpoint *techpoint)
 | 
						|
{
 | 
						|
	detect_thread_stop(techpoint);
 | 
						|
	return 0;
 | 
						|
}
 |