// SPDX-License-Identifier: GPL-2.0
|
/*
|
* Maxim Quad GMSL Deserializer Video Pipe driver
|
*
|
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
|
*
|
* Author: Cai Wenzhong <cwz@rock-chips.com>
|
*
|
*/
|
#include "maxim4c_api.h"
|
|
static int maxim4c_video_pipe_select(maxim4c_t *maxim4c)
|
{
|
struct i2c_client *client = maxim4c->client;
|
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
|
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
|
u8 reg_mask = 0, reg_value = 0;
|
u16 reg_addr = 0;
|
int i = 0, shift = 0;
|
int ret = 0;
|
|
// video pipe selection
|
reg_mask = 0;
|
reg_value = 0;
|
for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
|
video_pipe_cfg = &video_pipe->pipe_cfg[i];
|
|
shift = (i % 2) ? 4 : 0;
|
if (video_pipe_cfg->pipe_enable) {
|
reg_mask |= (0xF << shift);
|
reg_value |= ((video_pipe_cfg->pipe_idx & 0x3) << (0 + shift));
|
reg_value |= ((video_pipe_cfg->link_idx & 0x3) << (2 + shift));
|
}
|
|
if ((i % 2 == 1) && (reg_mask != 0)) {
|
reg_addr = 0x00F0 + (i / 2);
|
|
ret |= maxim4c_i2c_update_byte(client,
|
reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
|
reg_mask, reg_value);
|
|
// Prepare for next register
|
reg_mask = 0;
|
reg_value = 0;
|
}
|
}
|
|
return ret;
|
}
|
|
static int maxim4c_video_pipe_run_init_seq(maxim4c_t *maxim4c)
|
{
|
struct i2c_client *client = maxim4c->client;
|
struct device *dev = &client->dev;
|
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
|
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
|
struct maxim4c_i2c_init_seq *init_seq = NULL;
|
int i = 0;
|
int ret = 0;
|
|
// video pipe init sequence
|
for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
|
video_pipe_cfg = &video_pipe->pipe_cfg[i];
|
init_seq = &video_pipe_cfg->pipe_init_seq;
|
ret = maxim4c_i2c_run_init_seq(client, init_seq);
|
if (ret) {
|
dev_err(dev, "pipe id = %d init sequence error\n", i);
|
return ret;
|
}
|
}
|
|
// video pipe parallel mode init sequence
|
init_seq = &video_pipe->parallel_init_seq;
|
ret = maxim4c_i2c_run_init_seq(client, init_seq);
|
if (ret) {
|
dev_err(dev, "pipe parallel init sequence error\n");
|
return ret;
|
}
|
|
return 0;
|
}
|
|
static int maxim4c_video_pipe_config_parse_dt(struct device *dev,
|
maxim4c_video_pipe_t *video_pipe,
|
struct device_node *parent_node)
|
{
|
struct device_node *node = NULL;
|
struct device_node *init_seq_node = NULL;
|
struct maxim4c_i2c_init_seq *init_seq = NULL;
|
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
|
const char *pipe_cfg_name = "video-pipe-config";
|
u32 sub_idx = 0, pipe_id = 0;
|
u32 value = 0;
|
int ret = 0;
|
|
node = NULL;
|
sub_idx = 0;
|
while ((node = of_get_next_child(parent_node, node))) {
|
if (!strncasecmp(node->name,
|
pipe_cfg_name,
|
strlen(pipe_cfg_name))) {
|
if (sub_idx >= MAXIM4C_PIPE_O_ID_MAX) {
|
dev_err(dev, "%pOF: Too many matching %s node\n",
|
parent_node, pipe_cfg_name);
|
|
of_node_put(node);
|
break;
|
}
|
|
if (!of_device_is_available(node)) {
|
dev_info(dev, "%pOF is disabled\n", node);
|
|
sub_idx++;
|
|
continue;
|
}
|
|
/* Video Pipe: pipe id */
|
ret = of_property_read_u32(node, "pipe-id", &pipe_id);
|
if (ret) {
|
// if pipe_id is error, parse next node
|
dev_err(dev, "Can not get pipe-id property!");
|
|
sub_idx++;
|
|
continue;
|
}
|
if (pipe_id >= MAXIM4C_PIPE_O_ID_MAX) {
|
// if pipe_id is error, parse next node
|
dev_err(dev, "Error pipe-id = %d!", pipe_id);
|
|
sub_idx++;
|
|
continue;
|
}
|
|
video_pipe_cfg = &video_pipe->pipe_cfg[pipe_id];
|
|
/* Video Pipe: pipe enable */
|
video_pipe_cfg->pipe_enable = 1;
|
video_pipe->pipe_enable_mask |= BIT(pipe_id);
|
|
dev_info(dev, "video pipe id = %d: pipe enable = %d\n",
|
pipe_id, video_pipe_cfg->pipe_enable);
|
|
/* Video Pipe: other config */
|
ret = of_property_read_u32(node, "pipe-idx", &value);
|
if (ret == 0) {
|
dev_info(dev, "pipe-idx property: %d", value);
|
video_pipe_cfg->pipe_idx = value;
|
}
|
|
ret = of_property_read_u32(node, "link-idx", &value);
|
if (ret == 0) {
|
dev_info(dev, "link-idx property: %d", value);
|
video_pipe_cfg->link_idx = value;
|
}
|
|
init_seq_node = of_get_child_by_name(node, "pipe-init-sequence");
|
if (!IS_ERR_OR_NULL(init_seq_node)) {
|
dev_info(dev, "load pipe-init-sequence\n");
|
|
init_seq = &video_pipe_cfg->pipe_init_seq;
|
maxim4c_i2c_load_init_seq(dev, init_seq_node, init_seq);
|
|
of_node_put(init_seq_node);
|
}
|
|
sub_idx++;
|
}
|
}
|
|
node = of_get_child_by_name(parent_node, "parallel-mode-config");
|
if (!IS_ERR_OR_NULL(node)) {
|
if (!of_device_is_available(node)) {
|
dev_info(dev, "%pOF is disabled\n", node);
|
|
of_node_put(node);
|
return 0;
|
}
|
|
init_seq_node = of_get_child_by_name(node, "parallel-init-sequence");
|
if (!IS_ERR_OR_NULL(init_seq_node)) {
|
dev_info(dev, "load parallel-init-sequence\n");
|
|
init_seq = &video_pipe->parallel_init_seq;
|
maxim4c_i2c_load_init_seq(dev, init_seq_node, init_seq);
|
|
of_node_put(init_seq_node);
|
}
|
|
of_node_put(node);
|
}
|
|
return 0;
|
}
|
|
int maxim4c_video_pipe_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node)
|
{
|
struct device *dev = &maxim4c->client->dev;
|
struct device_node *node = NULL;
|
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
|
int ret = 0;
|
|
dev_info(dev, "=== maxim4c video pipe parse dt ===\n");
|
|
node = of_get_child_by_name(of_node, "video-pipes");
|
if (IS_ERR_OR_NULL(node)) {
|
dev_err(dev, "%pOF has no child node: video-pipes\n",
|
of_node);
|
return -ENODEV;
|
}
|
|
if (!of_device_is_available(node)) {
|
dev_info(dev, "%pOF is disabled\n", node);
|
of_node_put(node);
|
return -ENODEV;
|
}
|
|
ret = maxim4c_video_pipe_config_parse_dt(dev, video_pipe, node);
|
|
of_node_put(node);
|
|
return ret;
|
}
|
EXPORT_SYMBOL(maxim4c_video_pipe_parse_dt);
|
|
int maxim4c_video_pipe_mask_enable(maxim4c_t *maxim4c, u8 video_pipe_mask, bool enable)
|
{
|
struct i2c_client *client = maxim4c->client;
|
struct device *dev = &client->dev;
|
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
|
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
|
u8 reg_mask = 0, reg_value = 0;
|
int i = 0;
|
int ret = 0;
|
|
dev_dbg(dev, "%s, video_pipe_mask = 0x%x, enable = %d\n",
|
__func__, video_pipe_mask, enable);
|
|
reg_mask = 0;
|
reg_value = 0;
|
// video pipe enable
|
for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
|
video_pipe_cfg = &video_pipe->pipe_cfg[i];
|
if (video_pipe_cfg->pipe_enable
|
&& (video_pipe_mask & BIT(i))) {
|
reg_mask |= BIT(i);
|
if (enable)
|
reg_value |= BIT(i);
|
}
|
}
|
|
if (reg_mask != 0) {
|
ret |= maxim4c_i2c_update_byte(client,
|
0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
|
reg_mask, reg_value);
|
}
|
|
return ret;
|
}
|
EXPORT_SYMBOL(maxim4c_video_pipe_mask_enable);
|
|
int maxim4c_video_pipe_linkid_enable(maxim4c_t *maxim4c, u8 link_id, bool enable)
|
{
|
struct i2c_client *client = maxim4c->client;
|
struct device *dev = &client->dev;
|
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
|
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
|
u8 reg_mask = 0, reg_value = 0;
|
int i = 0;
|
int ret = 0;
|
|
dev_dbg(dev, "%s, link_id = %d, enable = %d\n",
|
__func__, link_id, enable);
|
|
reg_mask = 0;
|
reg_value = 0;
|
// video pipe enable
|
for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
|
video_pipe_cfg = &video_pipe->pipe_cfg[i];
|
if (video_pipe_cfg->pipe_enable
|
&& (video_pipe_cfg->link_idx == link_id)) {
|
reg_mask = BIT(i);
|
if (enable)
|
reg_value = BIT(i);
|
}
|
}
|
|
if (reg_mask != 0) {
|
ret = maxim4c_i2c_update_byte(client,
|
0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
|
reg_mask, reg_value);
|
}
|
|
return ret;
|
}
|
EXPORT_SYMBOL(maxim4c_video_pipe_linkid_enable);
|
|
void maxim4c_video_pipe_data_init(maxim4c_t *maxim4c)
|
{
|
maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
|
struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
|
int i = 0;
|
|
video_pipe->pipe_enable_mask = 0;
|
video_pipe->parallel_init_seq.reg_init_seq = NULL;
|
|
for (i = 0; i < 4; i++) {
|
video_pipe_cfg = &video_pipe->pipe_cfg[i];
|
|
video_pipe_cfg->pipe_enable = 0;
|
video_pipe_cfg->pipe_idx = MAXIM4C_PIPE_I_ID_Z;
|
video_pipe_cfg->link_idx = (i % 4);
|
video_pipe_cfg->pipe_init_seq.reg_init_seq = NULL;
|
}
|
|
for (i = 4; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
|
video_pipe_cfg = &video_pipe->pipe_cfg[i];
|
|
video_pipe_cfg->pipe_enable = 0;
|
video_pipe_cfg->pipe_idx = MAXIM4C_PIPE_I_ID_X;
|
video_pipe_cfg->link_idx = (i % 4);
|
video_pipe_cfg->pipe_init_seq.reg_init_seq = NULL;
|
}
|
}
|
EXPORT_SYMBOL(maxim4c_video_pipe_data_init);
|
|
int maxim4c_video_pipe_hw_init(maxim4c_t *maxim4c)
|
{
|
struct device *dev = &maxim4c->client->dev;
|
u8 pipe_enable_mask = 0;
|
int ret = 0;
|
|
ret = maxim4c_video_pipe_select(maxim4c);
|
if (ret) {
|
dev_err(dev, "%s: video pipe select error\n", __func__);
|
return ret;
|
}
|
|
pipe_enable_mask = maxim4c->video_pipe.pipe_enable_mask;
|
ret = maxim4c_video_pipe_mask_enable(maxim4c, pipe_enable_mask, true);
|
if (ret) {
|
dev_err(dev, "%s: video pipe mask enable error\n", __func__);
|
return ret;
|
}
|
|
ret = maxim4c_video_pipe_run_init_seq(maxim4c);
|
if (ret) {
|
dev_err(dev, "%s: video pipe run init seq error\n", __func__);
|
return ret;
|
}
|
|
return 0;
|
}
|
EXPORT_SYMBOL(maxim4c_video_pipe_hw_init);
|