/** * opnous 6001 driver * * Copyright (C) 2017-2018 opnous Electronics Co., Ltd. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include // #include // #include // #include #include #include #include "opn6001.h" #include // #include "channel.h" /* #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mipical/mipi_cal.h" #include #include "nvcsi/nvcsi.h" #include "nvcsi/deskew.h" */ #define ENABLE_DMA_BUFFER 1 #define spi_BUFSIZ max(32, SMP_CACHE_BYTES) #define OPN6001_FRAME_WIDTH 320//328//320 #define OPN6001_FRAME_HEIGHT 720//1488//1440 #define OPN6001_LANES 2 #define OPN6001_BITS_PER_SAMPLE 12 #define OPN6001_LINK_FREQ_300MHZ 189000000//300000000//200000000 extern int opn6001_notifier_call_chain(unsigned long val, void *v); struct opn6001_power_work { struct work_struct wk; struct opn6001_state *pdata; struct completion work_fin; }; /** * Opn6001 is used as the Pre-ISP to link on Soc * |-----------------------| * | Tof sensor | * |-----------------------| * |-----------||----------| * |-----------||----------| * |-----------\/----------| * | Pre-ISP OPN6001 | * |-----------------------| * |-----------||----------| * |-----------||----------| * |-----------\/----------| * | Rockchip Soc | * |-----------------------| * Data Transfer As shown above. In OPN6001, the data received from the * extra sensor,and it is passed to the Soc through ISP. */ int opn6001_set_power(struct opn6001_state *pdata, int on) { return 0; } static int opn6001_sensor_power(struct v4l2_subdev *sd, int on) { //TODO to power on sensor. // If opn6001 is not power on/off yet, call opn6001_set_power() to power it on/off return 0; } static int opn6001_s_stream(struct v4l2_subdev *sd, int enable) { return 0; } static int opn6001_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) return -EINVAL; code->code = MEDIA_BUS_FMT_SRGGB12_1X12; return 0; } static int opn6001_enum_frame_sizes(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index != 0) return -EINVAL; fse->min_width = OPN6001_FRAME_WIDTH; fse->max_width = OPN6001_FRAME_WIDTH; fse->max_height = OPN6001_FRAME_HEIGHT; fse->min_height = OPN6001_FRAME_HEIGHT; return 0; } static int opn6001_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; mf->code = MEDIA_BUS_FMT_SRGGB12_1X12; mf->field = V4L2_FIELD_NONE; mf->width = OPN6001_FRAME_WIDTH; mf->height = OPN6001_FRAME_HEIGHT; return 0; } static int opn6001_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; mf->code = MEDIA_BUS_FMT_SRGGB12_1X12; mf->field = V4L2_FIELD_NONE; mf->width = OPN6001_FRAME_WIDTH; mf->height = OPN6001_FRAME_HEIGHT; return 0; } static long opn6001_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { switch (cmd) { //TODO: add more ioctls to opn6001 from v4l2 perspective break; default: return -ENOTTY; } return 0; } #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int opn6001_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { //struct opn6001_state *pdata = to_state(sd); struct v4l2_mbus_framefmt *mf; mf = v4l2_subdev_get_try_format(sd, fh->pad, 0); /* Initialize try_fmt */ mf->code = MEDIA_BUS_FMT_SRGGB12_1X12; mf->field = V4L2_FIELD_NONE; mf->width = OPN6001_FRAME_WIDTH; mf->height = OPN6001_FRAME_HEIGHT; return 0; } static const struct v4l2_subdev_internal_ops opn6001_internal_ops = { .open = opn6001_open, }; #endif static const struct v4l2_subdev_video_ops opn6001_subdev_video_ops = { .s_stream = opn6001_s_stream, }; static const struct v4l2_subdev_core_ops opn6001_core_ops = { .s_power = opn6001_sensor_power, .ioctl = opn6001_ioctl, }; static const struct v4l2_subdev_pad_ops opn6001_subdev_pad_ops = { .enum_mbus_code = opn6001_enum_mbus_code, .enum_frame_size = opn6001_enum_frame_sizes, .get_fmt = opn6001_get_fmt, .set_fmt = opn6001_set_fmt, }; static const struct v4l2_subdev_ops opn6001_subdev_ops = { .core = &opn6001_core_ops, .video = &opn6001_subdev_video_ops, .pad = &opn6001_subdev_pad_ops, }; static const s64 link_freq_menu_items[] = { OPN6001_LINK_FREQ_300MHZ }; static int opn6001_initialize_controls(struct opn6001_state *opn6001) { int ret; s64 pixel_rate; struct v4l2_ctrl *ctrl; struct v4l2_ctrl_handler *handler; handler = &opn6001->ctrl_handler; ret = v4l2_ctrl_handler_init(handler, 8); if (ret) return ret; ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0, link_freq_menu_items); if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; pixel_rate = (link_freq_menu_items[0] * 2 * OPN6001_LANES) / OPN6001_BITS_PER_SAMPLE; ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1, pixel_rate); if (handler->error) { ret = handler->error; dev_err(opn6001->dev, "Failed to init controls(%d)\n", ret); goto err_free_handler; } opn6001->sd.ctrl_handler = handler; return 0; err_free_handler: v4l2_ctrl_handler_free(handler); return ret; } static int opn6001_v4l2_subdev_init(struct opn6001_state *opn6001) { struct v4l2_subdev *sd = &opn6001->sd; int ret; v4l2_i2c_subdev_init(sd, opn6001->client, &opn6001_subdev_ops); ret = opn6001_initialize_controls(opn6001); if (ret) return ret; #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &opn6001_internal_ops; #endif #if defined(CONFIG_MEDIA_CONTROLLER) opn6001->pad.flags = MEDIA_PAD_FL_SOURCE; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.function = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &opn6001->pad); if (ret) return ret; #endif // if到endif snprintf(sd->name, sizeof(sd->name), "opn6001-preisp"); ret = v4l2_async_register_subdev(sd); if (ret) { dev_err(opn6001->dev, "v4l2 async register subdev failed\n"); goto clean_entity; } return 0; clean_entity: #if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); #endif return ret; } static int opn6001_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct opn6001_state *opn6001; int ret = 0; opn6001 = devm_kzalloc(&client->dev, sizeof(*opn6001), GFP_KERNEL); if (!opn6001) return -ENOMEM; opn6001->dev = &client->dev; opn6001->client = client; //opn6001->log_level = LOG_INFO; i2c_set_clientdata(client, opn6001); mutex_init(&opn6001->lock); ret = opn6001_v4l2_subdev_init(opn6001); if (ret) goto cleanup; printk("submit opn6001 video ok\n"); return 0; cleanup: mutex_destroy(&opn6001->lock); return ret; } static int opn6001_remove(struct i2c_client *client) { struct opn6001_state *opn6001 = i2c_get_clientdata(client); mutex_destroy(&opn6001->lock); return 0; } static const struct i2c_device_id opn6001_id[] = { { "opn6001", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, opn6001_id); #if IS_ENABLED(CONFIG_OF) static const struct of_device_id opn6001_of_match[] = { { .compatible = "opnous,6001" }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, opn6001_of_match); #endif static struct i2c_driver opn6001_driver = { .driver = { .of_match_table = opn6001_of_match, .name = "opn6001", }, .probe = opn6001_probe, .remove = opn6001_remove, .id_table = opn6001_id, }; static int __init preisp_mod_init(void) { return i2c_add_driver(&opn6001_driver); } static void __exit preisp_mod_exit(void) { i2c_del_driver(&opn6001_driver); } late_initcall(preisp_mod_init); module_exit(preisp_mod_exit); MODULE_AUTHOR("opnous platform team"); MODULE_DESCRIPTION("A DSP driver for opn6001 chip"); MODULE_LICENSE("GPL v2"); /** * 1.rk头文件注释掉 * 2.加入uapi/linux/media.h, if-endif改成if(1) * 3. * 4.sensor_common函数更改 * 5.spi device -> i2c client * 6.media_entity_init, 前面加tegra_ * 7.entity.type 改成 entity.function * * * * //8.media/v4l2-async.h, 引用于media/v4l2-subdev.h, if-endif改成if(1), 版本更新至4.19.140 * */