|
@@ -2622,223 +2622,6 @@ static void smiapp_create_subdev(struct smiapp_sensor *sensor,
|
|
|
v4l2_set_subdevdata(&ssd->sd, client);
|
|
|
}
|
|
|
|
|
|
-static int smiapp_init(struct smiapp_sensor *sensor)
|
|
|
-{
|
|
|
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
|
|
|
- struct smiapp_pll *pll = &sensor->pll;
|
|
|
- unsigned int i;
|
|
|
- int rval;
|
|
|
-
|
|
|
- sensor->vana = devm_regulator_get(&client->dev, "vana");
|
|
|
- if (IS_ERR(sensor->vana)) {
|
|
|
- dev_err(&client->dev, "could not get regulator for vana\n");
|
|
|
- return PTR_ERR(sensor->vana);
|
|
|
- }
|
|
|
-
|
|
|
- sensor->ext_clk = devm_clk_get(&client->dev, NULL);
|
|
|
- if (IS_ERR(sensor->ext_clk)) {
|
|
|
- dev_err(&client->dev, "could not get clock (%ld)\n",
|
|
|
- PTR_ERR(sensor->ext_clk));
|
|
|
- return -EPROBE_DEFER;
|
|
|
- }
|
|
|
-
|
|
|
- rval = clk_set_rate(sensor->ext_clk,
|
|
|
- sensor->hwcfg->ext_clk);
|
|
|
- if (rval < 0) {
|
|
|
- dev_err(&client->dev,
|
|
|
- "unable to set clock freq to %u\n",
|
|
|
- sensor->hwcfg->ext_clk);
|
|
|
- return rval;
|
|
|
- }
|
|
|
-
|
|
|
- sensor->xshutdown = devm_gpiod_get_optional(&client->dev, "xshutdown",
|
|
|
- GPIOD_OUT_LOW);
|
|
|
- if (IS_ERR(sensor->xshutdown))
|
|
|
- return PTR_ERR(sensor->xshutdown);
|
|
|
-
|
|
|
- rval = smiapp_power_on(sensor);
|
|
|
- if (rval)
|
|
|
- return -ENODEV;
|
|
|
-
|
|
|
- rval = smiapp_identify_module(sensor);
|
|
|
- if (rval) {
|
|
|
- rval = -ENODEV;
|
|
|
- goto out_power_off;
|
|
|
- }
|
|
|
-
|
|
|
- rval = smiapp_get_all_limits(sensor);
|
|
|
- if (rval) {
|
|
|
- rval = -ENODEV;
|
|
|
- goto out_power_off;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Handle Sensor Module orientation on the board.
|
|
|
- *
|
|
|
- * The application of H-FLIP and V-FLIP on the sensor is modified by
|
|
|
- * the sensor orientation on the board.
|
|
|
- *
|
|
|
- * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set
|
|
|
- * both H-FLIP and V-FLIP for normal operation which also implies
|
|
|
- * that a set/unset operation for user space HFLIP and VFLIP v4l2
|
|
|
- * controls will need to be internally inverted.
|
|
|
- *
|
|
|
- * Rotation also changes the bayer pattern.
|
|
|
- */
|
|
|
- if (sensor->hwcfg->module_board_orient ==
|
|
|
- SMIAPP_MODULE_BOARD_ORIENT_180)
|
|
|
- sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |
|
|
|
- SMIAPP_IMAGE_ORIENTATION_VFLIP;
|
|
|
-
|
|
|
- rval = smiapp_call_quirk(sensor, limits);
|
|
|
- if (rval) {
|
|
|
- dev_err(&client->dev, "limits quirks failed\n");
|
|
|
- goto out_power_off;
|
|
|
- }
|
|
|
-
|
|
|
- if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) {
|
|
|
- u32 val;
|
|
|
-
|
|
|
- rval = smiapp_read(sensor,
|
|
|
- SMIAPP_REG_U8_BINNING_SUBTYPES, &val);
|
|
|
- if (rval < 0) {
|
|
|
- rval = -ENODEV;
|
|
|
- goto out_power_off;
|
|
|
- }
|
|
|
- sensor->nbinning_subtypes = min_t(u8, val,
|
|
|
- SMIAPP_BINNING_SUBTYPES);
|
|
|
-
|
|
|
- for (i = 0; i < sensor->nbinning_subtypes; i++) {
|
|
|
- rval = smiapp_read(
|
|
|
- sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val);
|
|
|
- if (rval < 0) {
|
|
|
- rval = -ENODEV;
|
|
|
- goto out_power_off;
|
|
|
- }
|
|
|
- sensor->binning_subtypes[i] =
|
|
|
- *(struct smiapp_binning_subtype *)&val;
|
|
|
-
|
|
|
- dev_dbg(&client->dev, "binning %xx%x\n",
|
|
|
- sensor->binning_subtypes[i].horizontal,
|
|
|
- sensor->binning_subtypes[i].vertical);
|
|
|
- }
|
|
|
- }
|
|
|
- sensor->binning_horizontal = 1;
|
|
|
- sensor->binning_vertical = 1;
|
|
|
-
|
|
|
- if (device_create_file(&client->dev, &dev_attr_ident) != 0) {
|
|
|
- dev_err(&client->dev, "sysfs ident entry creation failed\n");
|
|
|
- rval = -ENOENT;
|
|
|
- goto out_power_off;
|
|
|
- }
|
|
|
- /* SMIA++ NVM initialization - it will be read from the sensor
|
|
|
- * when it is first requested by userspace.
|
|
|
- */
|
|
|
- if (sensor->minfo.smiapp_version && sensor->hwcfg->nvm_size) {
|
|
|
- sensor->nvm = devm_kzalloc(&client->dev,
|
|
|
- sensor->hwcfg->nvm_size, GFP_KERNEL);
|
|
|
- if (sensor->nvm == NULL) {
|
|
|
- dev_err(&client->dev, "nvm buf allocation failed\n");
|
|
|
- rval = -ENOMEM;
|
|
|
- goto out_cleanup;
|
|
|
- }
|
|
|
-
|
|
|
- if (device_create_file(&client->dev, &dev_attr_nvm) != 0) {
|
|
|
- dev_err(&client->dev, "sysfs nvm entry failed\n");
|
|
|
- rval = -EBUSY;
|
|
|
- goto out_cleanup;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* We consider this as profile 0 sensor if any of these are zero. */
|
|
|
- if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||
|
|
|
- !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] ||
|
|
|
- !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] ||
|
|
|
- !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) {
|
|
|
- sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0;
|
|
|
- } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
|
|
|
- != SMIAPP_SCALING_CAPABILITY_NONE) {
|
|
|
- if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
|
|
|
- == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
|
|
|
- sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1;
|
|
|
- else
|
|
|
- sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2;
|
|
|
- sensor->scaler = &sensor->ssds[sensor->ssds_used];
|
|
|
- sensor->ssds_used++;
|
|
|
- } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
|
|
|
- == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
|
|
|
- sensor->scaler = &sensor->ssds[sensor->ssds_used];
|
|
|
- sensor->ssds_used++;
|
|
|
- }
|
|
|
- sensor->binner = &sensor->ssds[sensor->ssds_used];
|
|
|
- sensor->ssds_used++;
|
|
|
- sensor->pixel_array = &sensor->ssds[sensor->ssds_used];
|
|
|
- sensor->ssds_used++;
|
|
|
-
|
|
|
- sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
|
|
|
-
|
|
|
- /* prepare PLL configuration input values */
|
|
|
- pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
|
|
|
- pll->csi2.lanes = sensor->hwcfg->lanes;
|
|
|
- pll->ext_clk_freq_hz = sensor->hwcfg->ext_clk;
|
|
|
- pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
|
|
|
- /* Profile 0 sensors have no separate OP clock branch. */
|
|
|
- if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
|
|
|
- pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
|
|
|
-
|
|
|
- smiapp_create_subdev(sensor, sensor->scaler, "scaler", 2);
|
|
|
- smiapp_create_subdev(sensor, sensor->binner, "binner", 2);
|
|
|
- smiapp_create_subdev(sensor, sensor->pixel_array, "pixel_array", 1);
|
|
|
-
|
|
|
- dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile);
|
|
|
-
|
|
|
- sensor->pixel_array->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
|
|
-
|
|
|
- /* final steps */
|
|
|
- smiapp_read_frame_fmt(sensor);
|
|
|
- rval = smiapp_init_controls(sensor);
|
|
|
- if (rval < 0)
|
|
|
- goto out_cleanup;
|
|
|
-
|
|
|
- rval = smiapp_call_quirk(sensor, init);
|
|
|
- if (rval)
|
|
|
- goto out_cleanup;
|
|
|
-
|
|
|
- rval = smiapp_get_mbus_formats(sensor);
|
|
|
- if (rval) {
|
|
|
- rval = -ENODEV;
|
|
|
- goto out_cleanup;
|
|
|
- }
|
|
|
-
|
|
|
- rval = smiapp_init_late_controls(sensor);
|
|
|
- if (rval) {
|
|
|
- rval = -ENODEV;
|
|
|
- goto out_cleanup;
|
|
|
- }
|
|
|
-
|
|
|
- mutex_lock(&sensor->mutex);
|
|
|
- rval = smiapp_update_mode(sensor);
|
|
|
- mutex_unlock(&sensor->mutex);
|
|
|
- if (rval) {
|
|
|
- dev_err(&client->dev, "update mode failed\n");
|
|
|
- goto out_cleanup;
|
|
|
- }
|
|
|
-
|
|
|
- sensor->streaming = false;
|
|
|
- sensor->dev_init_done = true;
|
|
|
-
|
|
|
- smiapp_power_off(sensor);
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-out_cleanup:
|
|
|
- smiapp_cleanup(sensor);
|
|
|
-
|
|
|
-out_power_off:
|
|
|
- smiapp_power_off(sensor);
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
|
|
{
|
|
|
struct smiapp_subdev *ssd = to_smiapp_subdev(sd);
|
|
@@ -3061,6 +2844,7 @@ static int smiapp_probe(struct i2c_client *client,
|
|
|
{
|
|
|
struct smiapp_sensor *sensor;
|
|
|
struct smiapp_hwconfig *hwcfg = smiapp_get_hwconfig(&client->dev);
|
|
|
+ unsigned int i;
|
|
|
int rval;
|
|
|
|
|
|
if (hwcfg == NULL)
|
|
@@ -3081,9 +2865,205 @@ static int smiapp_probe(struct i2c_client *client,
|
|
|
sensor->src->sensor = sensor;
|
|
|
sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE;
|
|
|
|
|
|
- rval = smiapp_init(sensor);
|
|
|
+ sensor->vana = devm_regulator_get(&client->dev, "vana");
|
|
|
+ if (IS_ERR(sensor->vana)) {
|
|
|
+ dev_err(&client->dev, "could not get regulator for vana\n");
|
|
|
+ return PTR_ERR(sensor->vana);
|
|
|
+ }
|
|
|
+
|
|
|
+ sensor->ext_clk = devm_clk_get(&client->dev, NULL);
|
|
|
+ if (IS_ERR(sensor->ext_clk)) {
|
|
|
+ dev_err(&client->dev, "could not get clock (%ld)\n",
|
|
|
+ PTR_ERR(sensor->ext_clk));
|
|
|
+ return -EPROBE_DEFER;
|
|
|
+ }
|
|
|
+
|
|
|
+ rval = clk_set_rate(sensor->ext_clk,
|
|
|
+ sensor->hwcfg->ext_clk);
|
|
|
+ if (rval < 0) {
|
|
|
+ dev_err(&client->dev,
|
|
|
+ "unable to set clock freq to %u\n",
|
|
|
+ sensor->hwcfg->ext_clk);
|
|
|
+ return rval;
|
|
|
+ }
|
|
|
+
|
|
|
+ sensor->xshutdown = devm_gpiod_get_optional(&client->dev, "xshutdown",
|
|
|
+ GPIOD_OUT_LOW);
|
|
|
+ if (IS_ERR(sensor->xshutdown))
|
|
|
+ return PTR_ERR(sensor->xshutdown);
|
|
|
+
|
|
|
+ rval = smiapp_power_on(sensor);
|
|
|
if (rval)
|
|
|
- goto out_media_entity_cleanup;
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ rval = smiapp_identify_module(sensor);
|
|
|
+ if (rval) {
|
|
|
+ rval = -ENODEV;
|
|
|
+ goto out_power_off;
|
|
|
+ }
|
|
|
+
|
|
|
+ rval = smiapp_get_all_limits(sensor);
|
|
|
+ if (rval) {
|
|
|
+ rval = -ENODEV;
|
|
|
+ goto out_power_off;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Handle Sensor Module orientation on the board.
|
|
|
+ *
|
|
|
+ * The application of H-FLIP and V-FLIP on the sensor is modified by
|
|
|
+ * the sensor orientation on the board.
|
|
|
+ *
|
|
|
+ * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set
|
|
|
+ * both H-FLIP and V-FLIP for normal operation which also implies
|
|
|
+ * that a set/unset operation for user space HFLIP and VFLIP v4l2
|
|
|
+ * controls will need to be internally inverted.
|
|
|
+ *
|
|
|
+ * Rotation also changes the bayer pattern.
|
|
|
+ */
|
|
|
+ if (sensor->hwcfg->module_board_orient ==
|
|
|
+ SMIAPP_MODULE_BOARD_ORIENT_180)
|
|
|
+ sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |
|
|
|
+ SMIAPP_IMAGE_ORIENTATION_VFLIP;
|
|
|
+
|
|
|
+ rval = smiapp_call_quirk(sensor, limits);
|
|
|
+ if (rval) {
|
|
|
+ dev_err(&client->dev, "limits quirks failed\n");
|
|
|
+ goto out_power_off;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) {
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ rval = smiapp_read(sensor,
|
|
|
+ SMIAPP_REG_U8_BINNING_SUBTYPES, &val);
|
|
|
+ if (rval < 0) {
|
|
|
+ rval = -ENODEV;
|
|
|
+ goto out_power_off;
|
|
|
+ }
|
|
|
+ sensor->nbinning_subtypes = min_t(u8, val,
|
|
|
+ SMIAPP_BINNING_SUBTYPES);
|
|
|
+
|
|
|
+ for (i = 0; i < sensor->nbinning_subtypes; i++) {
|
|
|
+ rval = smiapp_read(
|
|
|
+ sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val);
|
|
|
+ if (rval < 0) {
|
|
|
+ rval = -ENODEV;
|
|
|
+ goto out_power_off;
|
|
|
+ }
|
|
|
+ sensor->binning_subtypes[i] =
|
|
|
+ *(struct smiapp_binning_subtype *)&val;
|
|
|
+
|
|
|
+ dev_dbg(&client->dev, "binning %xx%x\n",
|
|
|
+ sensor->binning_subtypes[i].horizontal,
|
|
|
+ sensor->binning_subtypes[i].vertical);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sensor->binning_horizontal = 1;
|
|
|
+ sensor->binning_vertical = 1;
|
|
|
+
|
|
|
+ if (device_create_file(&client->dev, &dev_attr_ident) != 0) {
|
|
|
+ dev_err(&client->dev, "sysfs ident entry creation failed\n");
|
|
|
+ rval = -ENOENT;
|
|
|
+ goto out_power_off;
|
|
|
+ }
|
|
|
+ /* SMIA++ NVM initialization - it will be read from the sensor
|
|
|
+ * when it is first requested by userspace.
|
|
|
+ */
|
|
|
+ if (sensor->minfo.smiapp_version && sensor->hwcfg->nvm_size) {
|
|
|
+ sensor->nvm = devm_kzalloc(&client->dev,
|
|
|
+ sensor->hwcfg->nvm_size, GFP_KERNEL);
|
|
|
+ if (sensor->nvm == NULL) {
|
|
|
+ dev_err(&client->dev, "nvm buf allocation failed\n");
|
|
|
+ rval = -ENOMEM;
|
|
|
+ goto out_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (device_create_file(&client->dev, &dev_attr_nvm) != 0) {
|
|
|
+ dev_err(&client->dev, "sysfs nvm entry failed\n");
|
|
|
+ rval = -EBUSY;
|
|
|
+ goto out_cleanup;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We consider this as profile 0 sensor if any of these are zero. */
|
|
|
+ if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||
|
|
|
+ !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] ||
|
|
|
+ !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] ||
|
|
|
+ !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) {
|
|
|
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0;
|
|
|
+ } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
|
|
|
+ != SMIAPP_SCALING_CAPABILITY_NONE) {
|
|
|
+ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
|
|
|
+ == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
|
|
|
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1;
|
|
|
+ else
|
|
|
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2;
|
|
|
+ sensor->scaler = &sensor->ssds[sensor->ssds_used];
|
|
|
+ sensor->ssds_used++;
|
|
|
+ } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
|
|
|
+ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
|
|
|
+ sensor->scaler = &sensor->ssds[sensor->ssds_used];
|
|
|
+ sensor->ssds_used++;
|
|
|
+ }
|
|
|
+ sensor->binner = &sensor->ssds[sensor->ssds_used];
|
|
|
+ sensor->ssds_used++;
|
|
|
+ sensor->pixel_array = &sensor->ssds[sensor->ssds_used];
|
|
|
+ sensor->ssds_used++;
|
|
|
+
|
|
|
+ sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
|
|
|
+
|
|
|
+ /* prepare PLL configuration input values */
|
|
|
+ sensor->pll.bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
|
|
|
+ sensor->pll.csi2.lanes = sensor->hwcfg->lanes;
|
|
|
+ sensor->pll.ext_clk_freq_hz = sensor->hwcfg->ext_clk;
|
|
|
+ sensor->pll.scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
|
|
|
+ /* Profile 0 sensors have no separate OP clock branch. */
|
|
|
+ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
|
|
|
+ sensor->pll.flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
|
|
|
+
|
|
|
+ smiapp_create_subdev(sensor, sensor->scaler, "scaler", 2);
|
|
|
+ smiapp_create_subdev(sensor, sensor->binner, "binner", 2);
|
|
|
+ smiapp_create_subdev(sensor, sensor->pixel_array, "pixel_array", 1);
|
|
|
+
|
|
|
+ dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile);
|
|
|
+
|
|
|
+ sensor->pixel_array->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
|
|
+
|
|
|
+ /* final steps */
|
|
|
+ smiapp_read_frame_fmt(sensor);
|
|
|
+ rval = smiapp_init_controls(sensor);
|
|
|
+ if (rval < 0)
|
|
|
+ goto out_cleanup;
|
|
|
+
|
|
|
+ rval = smiapp_call_quirk(sensor, init);
|
|
|
+ if (rval)
|
|
|
+ goto out_cleanup;
|
|
|
+
|
|
|
+ rval = smiapp_get_mbus_formats(sensor);
|
|
|
+ if (rval) {
|
|
|
+ rval = -ENODEV;
|
|
|
+ goto out_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ rval = smiapp_init_late_controls(sensor);
|
|
|
+ if (rval) {
|
|
|
+ rval = -ENODEV;
|
|
|
+ goto out_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&sensor->mutex);
|
|
|
+ rval = smiapp_update_mode(sensor);
|
|
|
+ mutex_unlock(&sensor->mutex);
|
|
|
+ if (rval) {
|
|
|
+ dev_err(&client->dev, "update mode failed\n");
|
|
|
+ goto out_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ sensor->streaming = false;
|
|
|
+ sensor->dev_init_done = true;
|
|
|
+
|
|
|
+ smiapp_power_off(sensor);
|
|
|
|
|
|
rval = media_entity_pads_init(&sensor->src->sd.entity, 2,
|
|
|
sensor->src->pads);
|
|
@@ -3099,6 +3079,11 @@ static int smiapp_probe(struct i2c_client *client,
|
|
|
out_media_entity_cleanup:
|
|
|
media_entity_cleanup(&sensor->src->sd.entity);
|
|
|
|
|
|
+out_cleanup:
|
|
|
+ smiapp_cleanup(sensor);
|
|
|
+
|
|
|
+out_power_off:
|
|
|
+ smiapp_power_off(sensor);
|
|
|
return rval;
|
|
|
}
|
|
|
|