oleavr-rgl-a500-mini-linux-.../drivers/media/platform/sunxi-vin/vin-mipi/sunxi_mipi.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

676 lines
18 KiB
C

/*
* mipi subdev driver module
*
* Copyright (c) 2017 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Authors: Zhao Wei <zhaowei@allwinnertech.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#include "bsp_mipi_csi.h"
#include "combo_rx/combo_rx_reg.h"
#include "combo_rx/combo_rx_reg_i.h"
#include "sunxi_mipi.h"
#include "../platform/platform_cfg.h"
#define MIPI_MODULE_NAME "vin_mipi"
#define IS_FLAG(x, y) (((x)&(y)) == y)
static LIST_HEAD(mipi_drv_list);
static struct combo_format sunxi_mipi_formats[] = {
{
.code = V4L2_MBUS_FMT_SBGGR8_1X8,
.bit_width = RAW8,
}, {
.code = V4L2_MBUS_FMT_SGBRG8_1X8,
.bit_width = RAW8,
}, {
.code = V4L2_MBUS_FMT_SGRBG8_1X8,
.bit_width = RAW8,
}, {
.code = V4L2_MBUS_FMT_SRGGB8_1X8,
.bit_width = RAW8,
}, {
.code = V4L2_MBUS_FMT_SBGGR10_1X10,
.bit_width = RAW10,
}, {
.code = V4L2_MBUS_FMT_SGBRG10_1X10,
.bit_width = RAW10,
}, {
.code = V4L2_MBUS_FMT_SGRBG10_1X10,
.bit_width = RAW10,
}, {
.code = V4L2_MBUS_FMT_SRGGB10_1X10,
.bit_width = RAW10,
}, {
.code = V4L2_MBUS_FMT_SBGGR12_1X12,
.bit_width = RAW12,
}, {
.code = V4L2_MBUS_FMT_SGBRG12_1X12,
.bit_width = RAW12,
}, {
.code = V4L2_MBUS_FMT_SGRBG12_1X12,
.bit_width = RAW12,
}, {
.code = V4L2_MBUS_FMT_SRGGB12_1X12,
.bit_width = RAW12,
}
};
void combo_rx_mipi_init(struct v4l2_subdev *sd)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
struct mipi_ctr mipi_ctr;
struct mipi_lane_map mipi_map;
memset(&mipi_ctr, 0, sizeof(mipi_ctr));
mipi_ctr.mipi_lane_num = mipi->cmb_cfg.lvds_ln;
if (mipi->cmb_mipi_mode == MIPI_NORMAL_MODE) {
mipi_ctr.mipi_wdr_mode_sel = 0;
} else if (mipi->cmb_mipi_mode == MIPI_VC_WDR_MODE) {
mipi_ctr.mipi_wdr_mode_sel = 0;
mipi_ctr.mipi_open_multi_ch = 1;
mipi_ctr.mipi_ch0_height = 0xF0;
mipi_ctr.mipi_ch1_height = 0xF0;
mipi_ctr.mipi_ch2_height = 0xF0;
mipi_ctr.mipi_ch3_height = 0xF0;
} else if (mipi->cmb_mipi_mode == MIPI_DOL_WDR_MODE) {
mipi_ctr.mipi_wdr_mode_sel = 2;
}
mipi_map.mipi_lane0 = MIPI_IN_L0_USE_PAD_LANE0;
mipi_map.mipi_lane1 = MIPI_IN_L1_USE_PAD_LANE1;
mipi_map.mipi_lane2 = MIPI_IN_L2_USE_PAD_LANE2;
mipi_map.mipi_lane3 = MIPI_IN_L3_USE_PAD_LANE3;
cmb_rx_mode_sel(mipi->id, D_PHY);
cmb_rx_app_pixel_out(mipi->id, ONE_PIXEL);
cmb_rx_mipi_ctr(mipi->id, &mipi_ctr);
cmb_rx_mipi_dphy_mapping(mipi->id, &mipi_map);
}
void combo_rx_sub_lvds_init(struct v4l2_subdev *sd)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
struct lvds_lane_map lvds_map;
struct lvds_sync_code lvds_sync_code;
struct lvds_ctr lvds_ctr;
int i;
memset(&lvds_ctr, 0, sizeof(lvds_ctr));
lvds_ctr.lvds_bit_width = mipi->cmb_fmt->bit_width;
lvds_ctr.lvds_lane_num = mipi->cmb_cfg.lvds_ln;
if (mipi->cmb_lvds_mode == LVDS_NORMAL_MODE) {
lvds_ctr.lvds_line_code_mode = 1;
} else if (mipi->cmb_lvds_mode == LVDS_4CODE_WDR_MODE) {
lvds_ctr.lvds_line_code_mode = 1;
lvds_ctr.lvds_wdr_lbl_sel = 1;
lvds_ctr.lvds_code_mask = 0x0030;
lvds_ctr.lvds_wdr_fid_mode_sel = 1;
lvds_ctr.lvds_wdr_fid_map_en = 0x3;
lvds_ctr.lvds_wdr_fid0_map_sel = 0xe;
lvds_ctr.lvds_wdr_fid1_map_sel = 0xf;
lvds_ctr.lvds_wdr_fid2_map_sel = 0;
lvds_ctr.lvds_wdr_fid3_map_sel = 0;
} else if (mipi->cmb_lvds_mode == LVDS_5CODE_WDR_MODE) {
lvds_ctr.lvds_line_code_mode = 1;
lvds_ctr.lvds_wdr_lbl_sel = 2;
}
memset(&lvds_map, 0, sizeof(lvds_map));
lvds_map.lvds_lane0 = LVDS_IN_L0_USE_PAD_LANE0;
lvds_map.lvds_lane1 = LVDS_IN_L1_USE_PAD_LANE1;
lvds_map.lvds_lane2 = LVDS_IN_L2_USE_PAD_LANE2;
lvds_map.lvds_lane3 = LVDS_IN_L3_USE_PAD_LANE3;
lvds_map.lvds_lane4 = LVDS_IN_L4_USE_PAD_LANE4;
lvds_map.lvds_lane5 = LVDS_IN_L5_USE_PAD_LANE5;
lvds_map.lvds_lane6 = LVDS_IN_L6_USE_PAD_LANE6;
lvds_map.lvds_lane7 = LVDS_IN_L7_USE_PAD_LANE7;
memset(&lvds_sync_code, 0, sizeof(lvds_sync_code));
for (i = 0; i < mipi->cmb_cfg.lane_num; i++) {
lvds_sync_code.lvds_lane_sof[i].low_bit = 0x0000AB00;
lvds_sync_code.lvds_lane_sof[i].high_bit = 0xFFFF0000;
lvds_sync_code.lvds_lane_sol[i].low_bit = 0x00008000;
lvds_sync_code.lvds_lane_sol[i].high_bit = 0xFFFF0000;
lvds_sync_code.lvds_lane_eol[i].low_bit = 0x00009D00;
lvds_sync_code.lvds_lane_eol[i].high_bit = 0xFFFF0000;
lvds_sync_code.lvds_lane_eof[i].low_bit = 0x0000B600;
lvds_sync_code.lvds_lane_eof[i].high_bit = 0xFFFF0000;
}
cmb_rx_mode_sel(mipi->id, SUB_LVDS);
cmb_rx_app_pixel_out(mipi->id, ONE_PIXEL);
cmb_rx_lvds_ctr(mipi->id, &lvds_ctr);
cmb_rx_lvds_mapping(mipi->id, &lvds_map);
cmb_rx_lvds_sync_code(mipi->id, &lvds_sync_code);
}
void combo_rx_hispi_init(struct v4l2_subdev *sd)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
struct lvds_lane_map lvds_map;
struct lvds_sync_code lvds_sync_code;
struct lvds_ctr lvds_ctr;
struct hispi_ctr hispi_ctr;
int i;
memset(&hispi_ctr, 0, sizeof(hispi_ctr));
memset(&lvds_ctr, 0, sizeof(lvds_ctr));
lvds_ctr.lvds_bit_width = mipi->cmb_fmt->bit_width;
lvds_ctr.lvds_lane_num = mipi->cmb_cfg.lvds_ln;
if (mipi->cmb_hispi_mode == HISPI_NORMAL_MODE) {
lvds_ctr.lvds_line_code_mode = 0;
lvds_ctr.lvds_pix_lsb = 1;
hispi_ctr.hispi_normal = 1;
hispi_ctr.hispi_trans_mode = PACKETIZED_SP;
} else if (mipi->cmb_hispi_mode == HISPI_WDR_MODE) {
lvds_ctr.lvds_line_code_mode = 0;
lvds_ctr.lvds_pix_lsb = 1;
lvds_ctr.lvds_wdr_lbl_sel = 1;
lvds_ctr.lvds_wdr_fid_mode_sel = 1;
lvds_ctr.lvds_wdr_fid_map_en = 0x3;
lvds_ctr.lvds_wdr_fid0_map_sel = 0xe;
lvds_ctr.lvds_wdr_fid1_map_sel = 0xf;
lvds_ctr.lvds_wdr_fid2_map_sel = 0;
lvds_ctr.lvds_wdr_fid3_map_sel = 0;
hispi_ctr.hispi_normal = 1;
hispi_ctr.hispi_trans_mode = PACKETIZED_SP;
hispi_ctr.hispi_wdr_en = 1;
hispi_ctr.hispi_wdr_sof_fild = 0x0030;
hispi_ctr.hispi_wdr_eof_fild = 0x1070;
hispi_ctr.hispi_code_mask = 0xc000;
}
memset(&lvds_map, 0, sizeof(lvds_map));
lvds_map.lvds_lane0 = LVDS_IN_L0_USE_PAD_LANE0;
lvds_map.lvds_lane1 = LVDS_IN_L1_USE_PAD_LANE1;
lvds_map.lvds_lane2 = LVDS_IN_L2_USE_PAD_LANE2;
lvds_map.lvds_lane3 = LVDS_IN_L3_USE_PAD_LANE3;
memset(&lvds_sync_code, 0, sizeof(lvds_sync_code));
for (i = 0; i < mipi->cmb_cfg.lane_num; i++) {
lvds_sync_code.lvds_lane_sof[i].low_bit = 0x00000030;
lvds_sync_code.lvds_lane_sof[i].high_bit = 0xFFFF0000;
lvds_sync_code.lvds_lane_sol[i].low_bit = 0x00000010;
lvds_sync_code.lvds_lane_sol[i].high_bit = 0xFFFF0000;
lvds_sync_code.lvds_lane_eol[i].low_bit = 0x00000050;
lvds_sync_code.lvds_lane_eol[i].high_bit = 0xFFFF0000;
lvds_sync_code.lvds_lane_eof[i].low_bit = 0x00000070;
lvds_sync_code.lvds_lane_eof[i].high_bit = 0xFFFF0000;
}
cmb_rx_mode_sel(mipi->id, SUB_LVDS);
cmb_rx_app_pixel_out(mipi->id, ONE_PIXEL);
cmb_rx_lvds_ctr(mipi->id, &lvds_ctr);
cmb_rx_lvds_mapping(mipi->id, &lvds_map);
cmb_rx_lvds_sync_code(mipi->id, &lvds_sync_code);
cmb_rx_hispi_ctr(mipi->id, &hispi_ctr);
}
void combo_rx_init(struct v4l2_subdev *sd)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
/*comnbo rx phya init*/
cmb_rx_phya_config(mipi->id);
switch (mipi->if_type) {
case V4L2_MBUS_PARALLEL:
cmb_rx_mode_sel(mipi->id, CMOS);
cmb_rx_app_pixel_out(mipi->id, ONE_PIXEL);
break;
case V4L2_MBUS_CSI2:
combo_rx_mipi_init(sd);
break;
case V4L2_MBUS_SUBLVDS:
combo_rx_sub_lvds_init(sd);
break;
case V4L2_MBUS_HISPI:
combo_rx_hispi_init(sd);
break;
default:
combo_rx_mipi_init(sd);
break;
}
cmb_rx_enable(mipi->id);
}
static enum pkt_fmt get_pkt_fmt(u32 code)
{
switch (code) {
case V4L2_MBUS_FMT_RGB565_2X8_BE:
return MIPI_RGB565;
case V4L2_MBUS_FMT_UYVY8_1X16:
return MIPI_YUV422;
case V4L2_MBUS_FMT_YUYV10_2X10:
return MIPI_YUV422_10;
case V4L2_MBUS_FMT_SBGGR8_1X8:
case V4L2_MBUS_FMT_SGBRG8_1X8:
case V4L2_MBUS_FMT_SGRBG8_1X8:
case V4L2_MBUS_FMT_SRGGB8_1X8:
return MIPI_RAW8;
case V4L2_MBUS_FMT_SBGGR10_1X10:
case V4L2_MBUS_FMT_SGBRG10_1X10:
case V4L2_MBUS_FMT_SGRBG10_1X10:
case V4L2_MBUS_FMT_SRGGB10_1X10:
return MIPI_RAW10;
case V4L2_MBUS_FMT_SBGGR12_1X12:
case V4L2_MBUS_FMT_SGBRG12_1X12:
case V4L2_MBUS_FMT_SGRBG12_1X12:
case V4L2_MBUS_FMT_SRGGB12_1X12:
return MIPI_RAW12;
default:
return MIPI_RAW8;
}
}
static int sunxi_mipi_subdev_s_power(struct v4l2_subdev *sd, int enable)
{
return 0;
}
static int sunxi_mipi_subdev_s_stream(struct v4l2_subdev *sd, int enable)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf = &mipi->format;
struct mbus_framefmt_res *res = (void *)mf->reserved;
int i;
mipi->csi2_cfg.bps = res->res_mipi_bps;
mipi->csi2_cfg.auto_check_bps = 0;
mipi->csi2_cfg.dphy_freq = DPHY_CLK;
for (i = 0; i < mipi->csi2_cfg.total_rx_ch; i++) {
mipi->csi2_fmt.packet_fmt[i] = get_pkt_fmt(mf->code);
mipi->csi2_fmt.field[i] = mf->field;
mipi->csi2_fmt.vc[i] = i;
}
if (enable) {
combo_rx_init(sd);
bsp_mipi_csi_dphy_init(mipi->id);
bsp_mipi_csi_set_para(mipi->id, &mipi->csi2_cfg);
bsp_mipi_csi_set_fmt(mipi->id, mipi->csi2_cfg.total_rx_ch,
&mipi->csi2_fmt);
/*for dphy clock async*/
bsp_mipi_csi_dphy_disable(mipi->id);
bsp_mipi_csi_dphy_enable(mipi->id);
bsp_mipi_csi_protocol_enable(mipi->id);
} else {
cmb_rx_disable(mipi->id);
bsp_mipi_csi_dphy_disable(mipi->id);
bsp_mipi_csi_protocol_disable(mipi->id);
bsp_mipi_csi_dphy_exit(mipi->id);
}
vin_print("%s %d*%d %x %d\n", __func__, mf->width, mf->height,
mf->code, mf->field);
return 0;
}
static int sunxi_mipi_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
return 0;
}
static struct v4l2_mbus_framefmt *__mipi_get_format(
struct mipi_dev *mipi, struct v4l2_subdev_fh *fh,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
return &mipi->format;
}
static int sunxi_mipi_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
mf = __mipi_get_format(mipi, fh, fmt->which);
if (!mf)
return -EINVAL;
mutex_lock(&mipi->subdev_lock);
fmt->format = *mf;
mutex_unlock(&mipi->subdev_lock);
return 0;
}
static struct combo_format *__mipi_find_format(
struct v4l2_mbus_framefmt *mf)
{
int i;
for (i = 0; i < ARRAY_SIZE(sunxi_mipi_formats); i++)
if (mf->code == sunxi_mipi_formats[i].code)
return &sunxi_mipi_formats[i];
return NULL;
}
static struct combo_format *__mipi_try_format(struct v4l2_mbus_framefmt *mf)
{
struct combo_format *mipi_fmt;
mipi_fmt = __mipi_find_format(mf);
if (mipi_fmt == NULL)
mipi_fmt = &sunxi_mipi_formats[0];
mf->code = mipi_fmt->code;
return mipi_fmt;
}
static int sunxi_mipi_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
struct combo_format *mipi_fmt;
vin_log(VIN_LOG_FMT, "%s %d*%d %x %d\n", __func__,
fmt->format.width, fmt->format.height,
fmt->format.code, fmt->format.field);
mf = __mipi_get_format(mipi, fh, fmt->which);
if (fmt->pad == MIPI_PAD_SOURCE) {
if (mf) {
mutex_lock(&mipi->subdev_lock);
fmt->format = *mf;
mutex_unlock(&mipi->subdev_lock);
}
return 0;
}
mipi_fmt = __mipi_try_format(&fmt->format);
if (mf) {
mutex_lock(&mipi->subdev_lock);
*mf = fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
mipi->cmb_fmt = mipi_fmt;
mutex_unlock(&mipi->subdev_lock);
}
return 0;
}
int sunxi_mipi_subdev_init(struct v4l2_subdev *sd, u32 val)
{
return 0;
}
static int sunxi_mipi_s_mbus_config(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg)
{
struct mipi_dev *mipi = v4l2_get_subdevdata(sd);
if (cfg->type == V4L2_MBUS_CSI2) {
mipi->if_type = V4L2_MBUS_CSI2;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_4_LANE))
mipi->csi2_cfg.lane_num = 4;
else if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_3_LANE))
mipi->csi2_cfg.lane_num = 3;
else if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_2_LANE))
mipi->csi2_cfg.lane_num = 2;
else
mipi->csi2_cfg.lane_num = 1;
mipi->csi2_cfg.total_rx_ch = 0;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_0))
mipi->csi2_cfg.total_rx_ch++;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_1))
mipi->csi2_cfg.total_rx_ch++;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_2))
mipi->csi2_cfg.total_rx_ch++;
if (IS_FLAG(cfg->flags, V4L2_MBUS_CSI2_CHANNEL_3))
mipi->csi2_cfg.total_rx_ch++;
} else if (cfg->type == V4L2_MBUS_SUBLVDS) {
mipi->if_type = V4L2_MBUS_SUBLVDS;
switch (cfg->flags) {
case V4L2_MBUS_SUBLVDS_12_LANE:
mipi->cmb_cfg.lane_num = 12;
mipi->cmb_cfg.lvds_ln = LVDS_12LANE;
break;
case V4L2_MBUS_SUBLVDS_10_LANE:
mipi->cmb_cfg.lane_num = 10;
mipi->cmb_cfg.lvds_ln = LVDS_10LANE;
break;
case V4L2_MBUS_SUBLVDS_8_LANE:
mipi->cmb_cfg.lane_num = 8;
mipi->cmb_cfg.lvds_ln = LVDS_8LANE;
break;
case V4L2_MBUS_SUBLVDS_4_LANE:
mipi->cmb_cfg.lane_num = 4;
mipi->cmb_cfg.lvds_ln = LVDS_4LANE;
break;
case V4L2_MBUS_SUBLVDS_2_LANE:
mipi->cmb_cfg.lane_num = 2;
mipi->cmb_cfg.lvds_ln = LVDS_2LANE;
break;
default:
mipi->cmb_cfg.lane_num = 8;
mipi->cmb_cfg.lvds_ln = LVDS_8LANE;
break;
}
} else if (cfg->type == V4L2_MBUS_HISPI) {
mipi->if_type = V4L2_MBUS_HISPI;
switch (cfg->flags) {
case V4L2_MBUS_SUBLVDS_12_LANE:
mipi->cmb_cfg.lane_num = 12;
mipi->cmb_cfg.lvds_ln = LVDS_12LANE;
break;
case V4L2_MBUS_SUBLVDS_10_LANE:
mipi->cmb_cfg.lane_num = 10;
mipi->cmb_cfg.lvds_ln = LVDS_10LANE;
break;
case V4L2_MBUS_SUBLVDS_8_LANE:
mipi->cmb_cfg.lane_num = 8;
mipi->cmb_cfg.lvds_ln = LVDS_8LANE;
break;
case V4L2_MBUS_SUBLVDS_4_LANE:
mipi->cmb_cfg.lane_num = 4;
mipi->cmb_cfg.lvds_ln = LVDS_4LANE;
break;
case V4L2_MBUS_SUBLVDS_2_LANE:
mipi->cmb_cfg.lane_num = 2;
mipi->cmb_cfg.lvds_ln = LVDS_2LANE;
break;
default:
mipi->cmb_cfg.lane_num = 4;
mipi->cmb_cfg.lvds_ln = LVDS_4LANE;
break;
}
} else if (cfg->type == V4L2_MBUS_PARALLEL) {
mipi->if_type = V4L2_MBUS_PARALLEL;
}
return 0;
}
static const struct v4l2_subdev_core_ops sunxi_mipi_core_ops = {
.s_power = sunxi_mipi_subdev_s_power,
.init = sunxi_mipi_subdev_init,
};
static const struct v4l2_subdev_video_ops sunxi_mipi_subdev_video_ops = {
.s_stream = sunxi_mipi_subdev_s_stream,
.s_mbus_config = sunxi_mipi_s_mbus_config,
};
static const struct v4l2_subdev_pad_ops sunxi_mipi_subdev_pad_ops = {
.enum_mbus_code = sunxi_mipi_enum_mbus_code,
.get_fmt = sunxi_mipi_subdev_get_fmt,
.set_fmt = sunxi_mipi_subdev_set_fmt,
};
static struct v4l2_subdev_ops sunxi_mipi_subdev_ops = {
.core = &sunxi_mipi_core_ops,
.video = &sunxi_mipi_subdev_video_ops,
.pad = &sunxi_mipi_subdev_pad_ops,
};
static int __mipi_init_subdev(struct mipi_dev *mipi)
{
struct v4l2_subdev *sd = &mipi->subdev;
int ret;
v4l2_subdev_init(sd, &sunxi_mipi_subdev_ops);
sd->grp_id = VIN_GRP_ID_MIPI;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "sunxi_mipi.%u", mipi->id);
v4l2_set_subdevdata(sd, mipi);
/*sd->entity->ops = &isp_media_ops;*/
mipi->mipi_pads[MIPI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
mipi->mipi_pads[MIPI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
ret = media_entity_init(&sd->entity, MIPI_PAD_NUM, mipi->mipi_pads, 0);
if (ret < 0)
return ret;
return 0;
}
static int mipi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mipi_dev *mipi = NULL;
int ret = 0;
if (np == NULL) {
vin_err("MIPI failed to get of node\n");
return -ENODEV;
}
mipi = kzalloc(sizeof(struct mipi_dev), GFP_KERNEL);
if (!mipi) {
ret = -ENOMEM;
goto ekzalloc;
}
of_property_read_u32(np, "device_id", &pdev->id);
if (pdev->id < 0) {
vin_err("MIPI failed to get device id\n");
ret = -EINVAL;
goto freedev;
}
mipi->base = of_iomap(np, 0);
if (!mipi->base) {
ret = -EIO;
goto freedev;
}
mipi->id = pdev->id;
mipi->pdev = pdev;
spin_lock_init(&mipi->slock);
cmb_rx_set_base_addr(mipi->id, (unsigned long)mipi->base);
bsp_mipi_csi_set_base_addr(mipi->id, (unsigned long)mipi->base);
bsp_mipi_dphy_set_base_addr(mipi->id, (unsigned long)mipi->base + 0x1000);
list_add_tail(&mipi->mipi_list, &mipi_drv_list);
ret = __mipi_init_subdev(mipi);
if (ret < 0) {
vin_err("mipi init error!\n");
goto unmap;
}
platform_set_drvdata(pdev, mipi);
vin_print("mipi%d probe end!\n", mipi->id);
return 0;
unmap:
iounmap(mipi->base);
freedev:
kfree(mipi);
ekzalloc:
vin_print("mipi probe err!\n");
return ret;
}
static int mipi_remove(struct platform_device *pdev)
{
struct mipi_dev *mipi = platform_get_drvdata(pdev);
struct v4l2_subdev *sd = &mipi->subdev;
platform_set_drvdata(pdev, NULL);
v4l2_set_subdevdata(sd, NULL);
if (mipi->base)
iounmap(mipi->base);
list_del(&mipi->mipi_list);
kfree(mipi);
return 0;
}
static const struct of_device_id sunxi_mipi_match[] = {
{.compatible = "allwinner,sunxi-mipi",},
{},
};
static struct platform_driver mipi_platform_driver = {
.probe = mipi_probe,
.remove = mipi_remove,
.driver = {
.name = MIPI_MODULE_NAME,
.owner = THIS_MODULE,
.of_match_table = sunxi_mipi_match,
}
};
struct v4l2_subdev *sunxi_mipi_get_subdev(int id)
{
struct mipi_dev *mipi;
list_for_each_entry(mipi, &mipi_drv_list, mipi_list) {
if (mipi->id == id) {
mipi->use_cnt++;
return &mipi->subdev;
}
}
return NULL;
}
int sunxi_mipi_platform_register(void)
{
return platform_driver_register(&mipi_platform_driver);
}
void sunxi_mipi_platform_unregister(void)
{
platform_driver_unregister(&mipi_platform_driver);
vin_print("mipi_exit end\n");
}