mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
media: i2c: lt7911uxc: update driver to V0.0X01.0X07
1.fix driver probe sequence. 2.set default timing 3.fix dcphy params 4.fix hotplug event report Change-Id: Ibd97f1498169798ff2a73e23330fdcb51b0a27ef Signed-off-by: Jianwei Fan <jianwei.fan@rock-chips.com>
This commit is contained in:
@@ -13,6 +13,11 @@
|
||||
* V0.0X01.0X04 add 5K60 support for CPHY.
|
||||
* V0.0X01.0X05 add CSI BGR888 fmt.
|
||||
* V0.0X01.0X06 fix dcphy params and add more fmt.
|
||||
* V0.0X01.0X07
|
||||
* 1.fix driver probe sequence.
|
||||
* 2.set default timing
|
||||
* 3.fix dcphy params
|
||||
* 4.fix hotplug event report
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -40,7 +45,7 @@
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
|
||||
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x06)
|
||||
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x07)
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
@@ -180,11 +185,11 @@ struct lt7911uxc_mode {
|
||||
static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
|
||||
.vendor = PHY_VENDOR_SAMSUNG,
|
||||
.lp_vol_ref = 3,
|
||||
.lp_hys_sw = {3, 3, 3, 0},
|
||||
.lp_escclk_pol_sel = {1, 0, 0, 0},
|
||||
.skew_data_cal_clk = {0, 0, 0, 3},
|
||||
.clk_hs_term_sel = 2,
|
||||
.data_hs_term_sel = {2, 2, 2, 2},
|
||||
.lp_hys_sw = {3, 0, 3, 0},
|
||||
.lp_escclk_pol_sel = {1, 1, 0, 0},
|
||||
.skew_data_cal_clk = {0, 0, 0, 0},
|
||||
.clk_hs_term_sel = 0,
|
||||
.data_hs_term_sel = {0, 0, 0, 0},
|
||||
.reserved = {0},
|
||||
};
|
||||
|
||||
@@ -573,7 +578,7 @@ static int lt7911uxc_get_detected_timings(struct v4l2_subdev *sd,
|
||||
u32 pixel_clock, fps, halt_pix_clk;
|
||||
u8 clk_h, clk_m, clk_l;
|
||||
u8 val_h, val_l;
|
||||
u32 byte_clk, mipi_clk, mipi_data_rate;
|
||||
u64 byte_clk, mipi_clk, mipi_data_rate;
|
||||
|
||||
memset(timings, 0, sizeof(struct v4l2_dv_timings));
|
||||
|
||||
@@ -623,7 +628,7 @@ static int lt7911uxc_get_detected_timings(struct v4l2_subdev *sd,
|
||||
|
||||
v4l2_info(sd, "act:%dx%d, total:%dx%d, pixclk:%d, fps:%d\n",
|
||||
hact, vact, htotal, vtotal, pixel_clock, fps);
|
||||
v4l2_info(sd, "byte_clk:%d, mipi_clk:%d, mipi_data_rate:%d\n",
|
||||
v4l2_info(sd, "byte_clk:%u, mipi_clk:%u, mipi_data_rate:%u\n",
|
||||
byte_clk, mipi_clk, mipi_data_rate);
|
||||
v4l2_info(sd, "inerlaced:%d\n", bt->interlaced);
|
||||
|
||||
@@ -640,6 +645,18 @@ static void lt7911uxc_delayed_work_hotplug(struct work_struct *work)
|
||||
lt7911uxc_s_ctrl_detect_tx_5v(sd);
|
||||
}
|
||||
|
||||
static void lt7911uxc_s_ctrl_detect_event(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct lt7911uxc *lt7911uxc = to_lt7911uxc(sd);
|
||||
u8 val;
|
||||
|
||||
val = i2c_rd8(sd, 0xe084);
|
||||
if (val == 0x01)
|
||||
v4l2_ctrl_s_ctrl(lt7911uxc->detect_tx_5v_ctrl, 1);
|
||||
else if (val == 0x00)
|
||||
v4l2_ctrl_s_ctrl(lt7911uxc->detect_tx_5v_ctrl, 0);
|
||||
}
|
||||
|
||||
static void lt7911uxc_delayed_work_res_change(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
@@ -647,6 +664,7 @@ static void lt7911uxc_delayed_work_res_change(struct work_struct *work)
|
||||
struct lt7911uxc, delayed_work_res_change);
|
||||
struct v4l2_subdev *sd = <7911uxc->sd;
|
||||
|
||||
lt7911uxc_s_ctrl_detect_event(sd);
|
||||
lt7911uxc_format_change(sd);
|
||||
}
|
||||
|
||||
@@ -731,13 +749,55 @@ static inline void enable_stream(struct v4l2_subdev *sd, bool enable)
|
||||
__func__, enable ? "en" : "dis");
|
||||
}
|
||||
|
||||
static int lt7911uxc_get_reso_dist(const struct lt7911uxc_mode *mode,
|
||||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct v4l2_bt_timings *bt = &timings->bt;
|
||||
u32 cur_fps, dist_fps;
|
||||
|
||||
cur_fps = fps_calc(bt);
|
||||
dist_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator);
|
||||
|
||||
return abs(mode->width - bt->width) +
|
||||
abs(mode->height - bt->height) + abs(dist_fps - cur_fps);
|
||||
}
|
||||
|
||||
static const struct lt7911uxc_mode *
|
||||
lt7911uxc_find_best_fit(struct lt7911uxc *lt7911uxc)
|
||||
{
|
||||
int dist;
|
||||
int cur_best_fit = 0;
|
||||
int cur_best_fit_dist = -1;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < lt7911uxc->cfg_num; i++) {
|
||||
dist = lt7911uxc_get_reso_dist(<7911uxc->support_modes[i], <7911uxc->timings);
|
||||
if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
|
||||
cur_best_fit_dist = dist;
|
||||
cur_best_fit = i;
|
||||
}
|
||||
}
|
||||
dev_dbg(<7911uxc->i2c_client->dev,
|
||||
"find current mode: support_mode[%d], %dx%d@%dfps\n",
|
||||
cur_best_fit, lt7911uxc->support_modes[cur_best_fit].width,
|
||||
lt7911uxc->support_modes[cur_best_fit].height,
|
||||
DIV_ROUND_CLOSEST(lt7911uxc->support_modes[cur_best_fit].max_fps.denominator,
|
||||
lt7911uxc->support_modes[cur_best_fit].max_fps.numerator));
|
||||
|
||||
return <7911uxc->support_modes[cur_best_fit];
|
||||
}
|
||||
|
||||
static void lt7911uxc_print_dv_timings(struct v4l2_subdev *sd, const char *prefix)
|
||||
{
|
||||
struct lt7911uxc *lt7911uxc = to_lt7911uxc(sd);
|
||||
struct device *dev = <7911uxc->i2c_client->dev;
|
||||
const struct v4l2_bt_timings *bt = <7911uxc->timings.bt;
|
||||
const struct lt7911uxc_mode *mode;
|
||||
u32 htot, vtot;
|
||||
u32 fps;
|
||||
|
||||
mode = lt7911uxc_find_best_fit(lt7911uxc);
|
||||
lt7911uxc->cur_mode = mode;
|
||||
htot = lt7911uxc->cur_mode->hts_def;
|
||||
vtot = lt7911uxc->cur_mode->vts_def;
|
||||
if (bt->interlaced)
|
||||
@@ -749,7 +809,7 @@ static void lt7911uxc_print_dv_timings(struct v4l2_subdev *sd, const char *prefi
|
||||
if (prefix == NULL)
|
||||
prefix = "";
|
||||
|
||||
v4l2_info(sd, "%s: %s%ux%u%s%u.%02u (%ux%u)\n", sd->name, prefix,
|
||||
dev_info(dev, "%s: %s%ux%u%s%u.%02u (%ux%u)\n", sd->name, prefix,
|
||||
bt->width, bt->height, bt->interlaced ? "i" : "p",
|
||||
fps / 100, fps % 100, htot, vtot);
|
||||
}
|
||||
@@ -992,33 +1052,6 @@ static int lt7911uxc_enum_frame_sizes(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt7911uxc_get_reso_dist(const struct lt7911uxc_mode *mode,
|
||||
struct v4l2_mbus_framefmt *framefmt)
|
||||
{
|
||||
return abs(mode->width - framefmt->width) +
|
||||
abs(mode->height - framefmt->height);
|
||||
}
|
||||
|
||||
static const struct lt7911uxc_mode *
|
||||
lt7911uxc_find_best_fit(struct lt7911uxc *lt7911uxc, struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *framefmt = &fmt->format;
|
||||
int dist;
|
||||
int cur_best_fit = 0;
|
||||
int cur_best_fit_dist = -1;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < lt7911uxc->cfg_num; i++) {
|
||||
dist = lt7911uxc_get_reso_dist(<7911uxc->support_modes[i], framefmt);
|
||||
if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
|
||||
cur_best_fit_dist = dist;
|
||||
cur_best_fit = i;
|
||||
}
|
||||
}
|
||||
|
||||
return <7911uxc->support_modes[cur_best_fit];
|
||||
}
|
||||
|
||||
static int lt7911uxc_get_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_format *format)
|
||||
@@ -1036,7 +1069,7 @@ static int lt7911uxc_get_fmt(struct v4l2_subdev *sd,
|
||||
format->format.colorspace = V4L2_COLORSPACE_SRGB;
|
||||
mutex_unlock(<7911uxc->confctl_mutex);
|
||||
|
||||
mode = lt7911uxc_find_best_fit(lt7911uxc, format);
|
||||
mode = lt7911uxc_find_best_fit(lt7911uxc);
|
||||
lt7911uxc->cur_mode = mode;
|
||||
|
||||
__v4l2_ctrl_s_ctrl_int64(lt7911uxc->pixel_rate,
|
||||
@@ -1100,7 +1133,7 @@ static int lt7911uxc_set_fmt(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
|
||||
lt7911uxc->mbus_fmt_code = format->format.code;
|
||||
mode = lt7911uxc_find_best_fit(lt7911uxc, format);
|
||||
mode = lt7911uxc_find_best_fit(lt7911uxc);
|
||||
lt7911uxc->cur_mode = mode;
|
||||
|
||||
enable_stream(sd, false);
|
||||
@@ -1480,11 +1513,6 @@ static int lt7911uxc_probe_of(struct lt7911uxc *lt7911uxc)
|
||||
|
||||
lt7911uxc->enable_hdcp = false;
|
||||
|
||||
gpiod_set_value(lt7911uxc->power_gpio, 1);
|
||||
//delay 2~3ms before reset
|
||||
usleep_range(2000, 3000);
|
||||
gpiod_set_value(lt7911uxc->reset_gpio, 0);
|
||||
|
||||
ret = 0;
|
||||
|
||||
put_node:
|
||||
@@ -1497,6 +1525,61 @@ static inline int lt7911uxc_probe_of(struct lt7911uxc *state)
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __lt7911uxc_power_on(struct lt7911uxc *lt7911uxc)
|
||||
{
|
||||
struct device *dev = <7911uxc->i2c_client->dev;
|
||||
|
||||
dev_info(dev, "lt7911uxc power on\n");
|
||||
gpiod_set_value(lt7911uxc->reset_gpio, 1);
|
||||
usleep_range(20000, 25000);
|
||||
gpiod_set_value(lt7911uxc->power_gpio, 1);
|
||||
//delay 20ms before reset
|
||||
usleep_range(25000, 30000);
|
||||
gpiod_set_value(lt7911uxc->reset_gpio, 0);
|
||||
usleep_range(25000, 30000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __lt7911uxc_power_off(struct lt7911uxc *lt7911uxc)
|
||||
{
|
||||
struct device *dev = <7911uxc->i2c_client->dev;
|
||||
|
||||
dev_info(dev, "lt7911uxc power off\n");
|
||||
|
||||
if (!IS_ERR(lt7911uxc->reset_gpio))
|
||||
gpiod_set_value(lt7911uxc->reset_gpio, 1);
|
||||
|
||||
if (!IS_ERR(lt7911uxc->power_gpio))
|
||||
gpiod_set_value(lt7911uxc->power_gpio, 0);
|
||||
}
|
||||
|
||||
static int lt7911uxc_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct lt7911uxc *lt7911uxc = to_lt7911uxc(sd);
|
||||
|
||||
return __lt7911uxc_power_on(lt7911uxc);
|
||||
}
|
||||
|
||||
static int lt7911uxc_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct lt7911uxc *lt7911uxc = to_lt7911uxc(sd);
|
||||
|
||||
__lt7911uxc_power_off(lt7911uxc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops lt7911uxc_pm_ops = {
|
||||
.suspend = lt7911uxc_suspend,
|
||||
.resume = lt7911uxc_resume,
|
||||
};
|
||||
|
||||
static int lt7911uxc_check_chip_id(struct lt7911uxc *lt7911uxc)
|
||||
{
|
||||
struct device *dev = <7911uxc->i2c_client->dev;
|
||||
@@ -1524,6 +1607,8 @@ static int lt7911uxc_check_chip_id(struct lt7911uxc *lt7911uxc)
|
||||
static int lt7911uxc_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct v4l2_dv_timings default_timing =
|
||||
V4L2_DV_BT_CEA_640X480P59_94;
|
||||
struct lt7911uxc *lt7911uxc;
|
||||
struct v4l2_subdev *sd;
|
||||
struct device *dev = &client->dev;
|
||||
@@ -1549,47 +1634,14 @@ static int lt7911uxc_probe(struct i2c_client *client,
|
||||
return err;
|
||||
}
|
||||
|
||||
lt7911uxc->timings = default_timing;
|
||||
lt7911uxc->cur_mode = <7911uxc->support_modes[0];
|
||||
|
||||
__lt7911uxc_power_on(lt7911uxc);
|
||||
err = lt7911uxc_check_chip_id(lt7911uxc);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_init(<7911uxc->confctl_mutex);
|
||||
err = lt7911uxc_init_v4l2_ctrls(lt7911uxc);
|
||||
if (err)
|
||||
goto err_free_hdl;
|
||||
|
||||
client->flags |= I2C_CLIENT_SCCB;
|
||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||
v4l2_i2c_subdev_init(sd, client, <7911uxc_ops);
|
||||
sd->internal_ops = <7911uxc_internal_ops;
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||
lt7911uxc->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||
err = media_entity_pads_init(&sd->entity, 1, <7911uxc->pad);
|
||||
if (err < 0) {
|
||||
v4l2_err(sd, "media entity init failed! err:%d\n", err);
|
||||
goto err_free_hdl;
|
||||
}
|
||||
#endif
|
||||
memset(facing, 0, sizeof(facing));
|
||||
if (strcmp(lt7911uxc->module_facing, "back") == 0)
|
||||
facing[0] = 'b';
|
||||
else
|
||||
facing[0] = 'f';
|
||||
|
||||
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||
lt7911uxc->module_index, facing,
|
||||
LT7911UXC_NAME, dev_name(sd->dev));
|
||||
err = v4l2_async_register_subdev_sensor_common(sd);
|
||||
if (err < 0) {
|
||||
v4l2_err(sd, "v4l2 register subdev failed! err:%d\n", err);
|
||||
goto err_clean_entity;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(<7911uxc->delayed_work_hotplug,
|
||||
lt7911uxc_delayed_work_hotplug);
|
||||
INIT_DELAYED_WORK(<7911uxc->delayed_work_res_change,
|
||||
@@ -1626,23 +1678,56 @@ static int lt7911uxc_probe(struct i2c_client *client,
|
||||
if (err)
|
||||
dev_err(dev, "failed to register plugin det irq (%d), maybe no use\n", err);
|
||||
|
||||
mutex_init(<7911uxc->confctl_mutex);
|
||||
err = lt7911uxc_init_v4l2_ctrls(lt7911uxc);
|
||||
if (err)
|
||||
goto err_free_hdl;
|
||||
|
||||
client->flags |= I2C_CLIENT_SCCB;
|
||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||
v4l2_i2c_subdev_init(sd, client, <7911uxc_ops);
|
||||
sd->internal_ops = <7911uxc_internal_ops;
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||
lt7911uxc->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||
err = media_entity_pads_init(&sd->entity, 1, <7911uxc->pad);
|
||||
if (err < 0) {
|
||||
v4l2_err(sd, "media entity init failed! err:%d\n", err);
|
||||
goto err_free_hdl;
|
||||
}
|
||||
#endif
|
||||
memset(facing, 0, sizeof(facing));
|
||||
if (strcmp(lt7911uxc->module_facing, "back") == 0)
|
||||
facing[0] = 'b';
|
||||
else
|
||||
facing[0] = 'f';
|
||||
|
||||
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||
lt7911uxc->module_index, facing,
|
||||
LT7911UXC_NAME, dev_name(sd->dev));
|
||||
err = v4l2_async_register_subdev_sensor_common(sd);
|
||||
if (err < 0) {
|
||||
v4l2_err(sd, "v4l2 register subdev failed! err:%d\n", err);
|
||||
goto err_clean_entity;
|
||||
}
|
||||
|
||||
err = v4l2_ctrl_handler_setup(sd->ctrl_handler);
|
||||
if (err) {
|
||||
v4l2_err(sd, "v4l2 ctrl handler setup failed! err:%d\n", err);
|
||||
goto err_work_queues;
|
||||
goto err_clean_entity;
|
||||
}
|
||||
lt7911uxc_format_change(sd);
|
||||
|
||||
schedule_delayed_work(<7911uxc->delayed_work_res_change, 100);
|
||||
|
||||
enable_stream(sd, false);
|
||||
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
|
||||
client->addr << 1, client->adapter->name);
|
||||
|
||||
return 0;
|
||||
|
||||
err_work_queues:
|
||||
if (!lt7911uxc->i2c_client->irq)
|
||||
flush_work(<7911uxc->work_i2c_poll);
|
||||
cancel_delayed_work(<7911uxc->delayed_work_hotplug);
|
||||
cancel_delayed_work(<7911uxc->delayed_work_res_change);
|
||||
err_clean_entity:
|
||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||
media_entity_cleanup(&sd->entity);
|
||||
@@ -1650,6 +1735,12 @@ err_clean_entity:
|
||||
err_free_hdl:
|
||||
v4l2_ctrl_handler_free(<7911uxc->hdl);
|
||||
mutex_destroy(<7911uxc->confctl_mutex);
|
||||
err_work_queues:
|
||||
if (!lt7911uxc->i2c_client->irq)
|
||||
flush_work(<7911uxc->work_i2c_poll);
|
||||
cancel_delayed_work(<7911uxc->delayed_work_hotplug);
|
||||
cancel_delayed_work(<7911uxc->delayed_work_res_change);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1687,6 +1778,7 @@ MODULE_DEVICE_TABLE(of, lt7911uxc_of_match);
|
||||
static struct i2c_driver lt7911uxc_driver = {
|
||||
.driver = {
|
||||
.name = LT7911UXC_NAME,
|
||||
.pm = <7911uxc_pm_ops,
|
||||
.of_match_table = of_match_ptr(lt7911uxc_of_match),
|
||||
},
|
||||
.probe = lt7911uxc_probe,
|
||||
|
||||
Reference in New Issue
Block a user