diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.c b/drivers/media/platform/msm/ais/sensor/msm_sensor.c index a276b03e5294..9655fad5b62b 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.c @@ -10,6 +10,12 @@ * GNU General Public License for more details. */ +#include +#include +#include +#include +#include +#include #include "msm_sensor.h" #include "msm_sd.h" #include "msm_cci.h" @@ -21,6 +27,7 @@ #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) +#define MAX_SENSOR_V4l2_EVENTS 100 static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl; static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl; @@ -405,12 +412,26 @@ static long msm_sensor_subdev_do_ioctl( { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - + struct v4l2_fh *vfh = file->private_data; switch (cmd) { case VIDIOC_MSM_SENSOR_CFG32: cmd = VIDIOC_MSM_SENSOR_CFG; + case VIDIOC_DQEVENT: { + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return -ENOIOCTLCMD; + return v4l2_event_dequeue(vfh, arg, + file->f_flags & O_NONBLOCK); + } + break; + case VIDIOC_SUBSCRIBE_EVENT: + pr_debug("msm_sensor_subdev_do_ioctl:VIDIOC_SUBSCRIBE_EVENT"); + return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); default: - return msm_sensor_subdev_ioctl(sd, cmd, arg); + pr_debug("msm_sensor.c msm_sensor_subdev_do_ioctl"); + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } } @@ -1459,8 +1480,108 @@ static int msm_sensor_power(struct v4l2_subdev *sd, int on) return rc; } + +static u32 msm_sensor_evt_mask_to_sensor_event(u32 evt_mask) +{ + u32 evt_id = SENSOR_EVENT_SUBS_MASK_NONE; + + switch (evt_mask) { + case SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS: + evt_id = SENSOR_EVENT_SIGNAL_STATUS; + break; + default: + evt_id = SENSOR_EVENT_SUBS_MASK_NONE; + break; + } + + return evt_id; +} + +static int msm_sensor_subscribe_event_mask(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub, int evt_mask_index, + u32 evt_id, bool subscribe_flag) +{ + int rc = 0; + + sub->type = evt_id; + + if (subscribe_flag) + rc = v4l2_event_subscribe(fh, sub, + MAX_SENSOR_V4l2_EVENTS, NULL); + else + rc = v4l2_event_unsubscribe(fh, sub); + if (rc != 0) { + pr_err("%s: Subs event_type =0x%x failed\n", + __func__, sub->type); + return rc; + } + return rc; +} + +static int msm_sensor_process_event_subscription(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub, bool subscribe_flag) +{ + int rc = 0, evt_mask_index = 0; + u32 evt_mask = sub->type; + u32 evt_id = 0; + + if (SENSOR_EVENT_SUBS_MASK_NONE == evt_mask) { + pr_err("%s: Subs event_type is None=0x%x\n", + __func__, evt_mask); + return 0; + } + + evt_mask_index = SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS; + if (evt_mask & (1<msm_sd.sd.devnode, &sensor_event); + return 0; +} + +static int msm_sensor_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return msm_sensor_process_event_subscription(fh, sub, true); +} + +static int msm_sensor_unsubscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return msm_sensor_process_event_subscription(fh, sub, false); +} + static struct v4l2_subdev_core_ops msm_sensor_subdev_core_ops = { .ioctl = msm_sensor_subdev_ioctl, + .subscribe_event = msm_sensor_subscribe_event, + .unsubscribe_event = msm_sensor_unsubscribe_event, .s_power = msm_sensor_power, }; diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.h b/drivers/media/platform/msm/ais/sensor/msm_sensor.h index eacd3b05420c..b742d06d3baa 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor.h +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.h @@ -90,10 +90,18 @@ struct msm_sensor_ctrl_t { uint32_t set_mclk_23880000; uint8_t is_csid_tg_mode; uint32_t is_secure; - + /* Interrupt GPIOs */ + struct gpio gpio_array[1]; + /* device status and Flags */ + int irq; struct msm_sensor_init_t s_init; + /* worker to handle interrupts */ + struct delayed_work irq_delayed_work; }; +int msm_sensor_send_event(struct msm_sensor_ctrl_t *s_ctrl, + uint32_t event_type, struct msm_sensor_event_data *event_data); + int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp); int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl); diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c index 58bddb1a3fba..5e34016d199c 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c @@ -132,6 +132,7 @@ static int32_t msm_sensor_driver_create_v4l_subdev s_ctrl->sensordata->sensor_name); v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, s_ctrl->pdev); s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0); s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR; @@ -143,6 +144,8 @@ static int32_t msm_sensor_driver_create_v4l_subdev return rc; } msm_cam_copy_v4l2_subdev_fops(&msm_sensor_v4l2_subdev_fops); + msm_sensor_v4l2_subdev_fops.unlocked_ioctl = + msm_sensor_subdev_fops_ioctl; #ifdef CONFIG_COMPAT msm_sensor_v4l2_subdev_fops.compat_ioctl32 = msm_sensor_subdev_fops_ioctl; @@ -632,6 +635,56 @@ static void msm_sensor_fill_sensor_info(struct msm_sensor_ctrl_t *s_ctrl, strlcpy(entity_name, s_ctrl->msm_sd.sd.entity.name, MAX_SENSOR_NAME); } +static irqreturn_t bridge_irq(int irq, void *dev) +{ + struct msm_sensor_ctrl_t *s_ctrl = dev; + + pr_err("msm_sensor_driver: received bridge interrupt:0x%x", + s_ctrl->sensordata->slave_info->sensor_slave_addr); + schedule_delayed_work(&s_ctrl->irq_delayed_work, + msecs_to_jiffies(0)); + return IRQ_HANDLED; +} + +static void bridge_irq_delay_work(struct work_struct *work) +{ + struct msm_sensor_ctrl_t *s_ctrl; + struct msm_camera_i2c_client *sensor_i2c_client; + struct msm_camera_slave_info *slave_info; + const char *sensor_name; + + struct msm_sensor_event_data sensor_event; + + s_ctrl = container_of(work, struct msm_sensor_ctrl_t, + irq_delayed_work.work); + if (!s_ctrl) { + pr_err("%s:%d failed: %pK\n", + __func__, __LINE__, s_ctrl); + goto exit_queue; + } + sensor_i2c_client = s_ctrl->sensor_i2c_client; + slave_info = s_ctrl->sensordata->slave_info; + sensor_name = s_ctrl->sensordata->sensor_name; + + if (!sensor_i2c_client || !slave_info || !sensor_name) { + pr_err("%s:%d failed: %pK %pK %pK\n", + __func__, __LINE__, sensor_i2c_client, slave_info, + sensor_name); + goto exit_queue; + } + + mutex_lock(s_ctrl->msm_sensor_mutex); + /* Fill the sensor event */ + sensor_event.sensor_slave_addr = + slave_info->sensor_slave_addr; + /* Queue the event */ + msm_sensor_send_event(s_ctrl, SENSOR_EVENT_SIGNAL_STATUS, + &sensor_event); + mutex_unlock(s_ctrl->msm_sensor_mutex); +exit_queue: + pr_err("Work IRQ exit"); +} + /* static function definition */ int32_t msm_sensor_driver_probe(void *setting, struct msm_sensor_info_t *probed_info, char *entity_name) @@ -934,12 +987,66 @@ CSID_TG: msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name); - /* Set probe succeeded flag to 1 so that no other camera shall - * probed on this slot - */ + if (slave_info->gpio_intr_config.gpio_num != -1) { + /* Configure INTB interrupt */ + s_ctrl->gpio_array[0].gpio = + slave_info->gpio_intr_config.gpio_num; + s_ctrl->gpio_array[0].flags = 0; + /* Only setup IRQ1 for now... */ + INIT_DELAYED_WORK(&s_ctrl->irq_delayed_work, + bridge_irq_delay_work); + rc = gpio_request_array(&s_ctrl->gpio_array[0], 1); + if (rc < 0) { + pr_err("%s: Failed to request irq_gpio %d", + __func__, rc); + goto cancel_work; + } + + if (gpio_is_valid(s_ctrl->gpio_array[0].gpio)) { + rc |= gpio_direction_input( + s_ctrl->gpio_array[0].gpio); + if (rc) { + pr_err("%s: Failed gpio_direction irq %d", + __func__, rc); + goto cancel_work; + } else { + pr_err("sensor probe IRQ direction succeeded"); + } + } + + s_ctrl->irq = gpio_to_irq(s_ctrl->gpio_array[0].gpio); + if (s_ctrl->irq) { + rc = request_irq(s_ctrl->irq, bridge_irq, + IRQF_ONESHOT | + (slave_info-> + gpio_intr_config.gpio_trigger), + "qcom,camera", s_ctrl); + if (rc) { + pr_err("%s: Failed request_irq %d", + __func__, rc); + goto cancel_work; + } + + } else { + pr_err("%s: Failed gpio_to_irq %d", + __func__, rc); + rc = -EINVAL; + goto cancel_work; + } + + /* Keep irq enabled */ + pr_err("msm_sensor_driver.c irq number = %d", s_ctrl->irq); + } + + /* + Set probe succeeded flag to 1 so that no other camera shall + * probed on this slot + */ s_ctrl->is_probe_succeed = 1; return rc; +cancel_work: + cancel_delayed_work(&s_ctrl->irq_delayed_work); free_camera_info: kfree(camera_info); free_slave_info: @@ -1125,7 +1232,6 @@ static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl) /* Store sensor control structure in static database */ g_sctrl[s_ctrl->id] = s_ctrl; CDBG("g_sctrl[%d] %pK", s_ctrl->id, g_sctrl[s_ctrl->id]); - return rc; FREE_DT_DATA: @@ -1178,7 +1284,6 @@ static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev) /* Fill platform device id*/ pdev->id = s_ctrl->id; - /* Fill device in power info */ s_ctrl->sensordata->power_info.dev = &pdev->dev; diff --git a/include/uapi/media/ais/msm_ais_sensor.h b/include/uapi/media/ais/msm_ais_sensor.h index f8b98def850a..eb9c24024383 100644 --- a/include/uapi/media/ais/msm_ais_sensor.h +++ b/include/uapi/media/ais/msm_ais_sensor.h @@ -178,6 +178,27 @@ enum cci_i2c_master_t { MASTER_MAX, }; +struct msm_sensor_event_data { + uint16_t sensor_slave_addr; +}; + +enum msm_sensor_event_mask_index { + SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS = 2, +}; + +#define SENSOR_EVENT_SUBS_MASK_NONE 0 + +#define SENSOR_EVENT_SUBS_MASK_SIGNAL_STATUS \ + (1 << SENSOR_EVENT_MASK_INDEX_SIGNAL_STATUS) + +enum msm_sensor_event_idx { + SENSOR_SIGNAL_STATUS = 2, + SENSOR_EVENT_MAX = 15 +}; + +#define SENSOR_EVENT_BASE (V4L2_EVENT_PRIVATE_START) +#define SENSOR_EVENT_SIGNAL_STATUS (SENSOR_EVENT_BASE + SENSOR_SIGNAL_STATUS) + struct msm_camera_i2c_array_write_config { struct msm_camera_i2c_reg_setting conf_array; uint16_t slave_addr; diff --git a/include/uapi/media/ais/msm_ais_sensor_sdk.h b/include/uapi/media/ais/msm_ais_sensor_sdk.h index c2a93a51a985..3f63bde39cf3 100644 --- a/include/uapi/media/ais/msm_ais_sensor_sdk.h +++ b/include/uapi/media/ais/msm_ais_sensor_sdk.h @@ -285,6 +285,11 @@ struct msm_sensor_id_info_t { unsigned short sensor_id_mask; }; +struct msm_camera_sensor_gpio_intr_config { + int gpio_num; + uint32_t gpio_trigger; +}; + struct msm_camera_sensor_slave_info { char sensor_name[32]; char eeprom_name[32]; @@ -300,6 +305,9 @@ struct msm_camera_sensor_slave_info { unsigned char is_init_params_valid; struct msm_sensor_init_params sensor_init_params; enum msm_sensor_output_format_t output_format; + struct msm_camera_sensor_gpio_intr_config + gpio_intr_config; + unsigned int camera_sensor_device_id; }; struct msm_camera_i2c_reg_array {