static int ccs_probe()

in i2c/ccs/ccs-core.c [3282:3666]


static int ccs_probe(struct i2c_client *client)
{
	struct ccs_sensor *sensor;
	const struct firmware *fw;
	char filename[40];
	unsigned int i;
	int rval;

	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
	if (sensor == NULL)
		return -ENOMEM;

	rval = ccs_get_hwconfig(sensor, &client->dev);
	if (rval)
		return rval;

	sensor->src = &sensor->ssds[sensor->ssds_used];

	v4l2_i2c_subdev_init(&sensor->src->sd, client, &ccs_ops);
	sensor->src->sd.internal_ops = &ccs_internal_src_ops;

	sensor->regulators = devm_kcalloc(&client->dev,
					  ARRAY_SIZE(ccs_regulators),
					  sizeof(*sensor->regulators),
					  GFP_KERNEL);
	if (!sensor->regulators)
		return -ENOMEM;

	for (i = 0; i < ARRAY_SIZE(ccs_regulators); i++)
		sensor->regulators[i].supply = ccs_regulators[i];

	rval = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(ccs_regulators),
				       sensor->regulators);
	if (rval) {
		dev_err(&client->dev, "could not get regulators\n");
		return rval;
	}

	sensor->ext_clk = devm_clk_get(&client->dev, NULL);
	if (PTR_ERR(sensor->ext_clk) == -ENOENT) {
		dev_info(&client->dev, "no clock defined, continuing...\n");
		sensor->ext_clk = NULL;
	} else if (IS_ERR(sensor->ext_clk)) {
		dev_err(&client->dev, "could not get clock (%ld)\n",
			PTR_ERR(sensor->ext_clk));
		return -EPROBE_DEFER;
	}

	if (sensor->ext_clk) {
		if (sensor->hwcfg.ext_clk) {
			unsigned long rate;

			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;
			}

			rate = clk_get_rate(sensor->ext_clk);
			if (rate != sensor->hwcfg.ext_clk) {
				dev_err(&client->dev,
					"can't set clock freq, asked for %u but got %lu\n",
					sensor->hwcfg.ext_clk, rate);
				return -EINVAL;
			}
		} else {
			sensor->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk);
			dev_dbg(&client->dev, "obtained clock freq %u\n",
				sensor->hwcfg.ext_clk);
		}
	} else if (sensor->hwcfg.ext_clk) {
		dev_dbg(&client->dev, "assuming clock freq %u\n",
			sensor->hwcfg.ext_clk);
	} else {
		dev_err(&client->dev, "unable to obtain clock freq\n");
		return -EINVAL;
	}

	if (!sensor->hwcfg.ext_clk) {
		dev_err(&client->dev, "cannot work with xclk frequency 0\n");
		return -EINVAL;
	}

	sensor->reset = devm_gpiod_get_optional(&client->dev, "reset",
						GPIOD_OUT_HIGH);
	if (IS_ERR(sensor->reset))
		return PTR_ERR(sensor->reset);
	/* Support old users that may have used "xshutdown" property. */
	if (!sensor->reset)
		sensor->xshutdown = devm_gpiod_get_optional(&client->dev,
							    "xshutdown",
							    GPIOD_OUT_LOW);
	if (IS_ERR(sensor->xshutdown))
		return PTR_ERR(sensor->xshutdown);

	rval = ccs_power_on(&client->dev);
	if (rval < 0)
		return rval;

	mutex_init(&sensor->mutex);

	rval = ccs_identify_module(sensor);
	if (rval) {
		rval = -ENODEV;
		goto out_power_off;
	}

	rval = snprintf(filename, sizeof(filename),
			"ccs/ccs-sensor-%4.4x-%4.4x-%4.4x.fw",
			sensor->minfo.sensor_mipi_manufacturer_id,
			sensor->minfo.sensor_model_id,
			sensor->minfo.sensor_revision_number);
	if (rval >= sizeof(filename)) {
		rval = -ENOMEM;
		goto out_power_off;
	}

	rval = request_firmware(&fw, filename, &client->dev);
	if (!rval) {
		ccs_data_parse(&sensor->sdata, fw->data, fw->size, &client->dev,
			       true);
		release_firmware(fw);
	}

	rval = snprintf(filename, sizeof(filename),
			"ccs/ccs-module-%4.4x-%4.4x-%4.4x.fw",
			sensor->minfo.mipi_manufacturer_id,
			sensor->minfo.model_id,
			sensor->minfo.revision_number);
	if (rval >= sizeof(filename)) {
		rval = -ENOMEM;
		goto out_release_sdata;
	}

	rval = request_firmware(&fw, filename, &client->dev);
	if (!rval) {
		ccs_data_parse(&sensor->mdata, fw->data, fw->size, &client->dev,
			       true);
		release_firmware(fw);
	}

	rval = ccs_read_all_limits(sensor);
	if (rval)
		goto out_release_mdata;

	rval = ccs_read_frame_fmt(sensor);
	if (rval) {
		rval = -ENODEV;
		goto out_free_ccs_limits;
	}

	rval = ccs_update_phy_ctrl(sensor);
	if (rval < 0)
		goto out_free_ccs_limits;

	/*
	 * 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 CCS_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 ==
	    CCS_MODULE_BOARD_ORIENT_180)
		sensor->hvflip_inv_mask =
			CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR |
			CCS_IMAGE_ORIENTATION_VERTICAL_FLIP;

	rval = ccs_call_quirk(sensor, limits);
	if (rval) {
		dev_err(&client->dev, "limits quirks failed\n");
		goto out_free_ccs_limits;
	}

	if (CCS_LIM(sensor, BINNING_CAPABILITY)) {
		sensor->nbinning_subtypes =
			min_t(u8, CCS_LIM(sensor, BINNING_SUB_TYPES),
			      CCS_LIM_BINNING_SUB_TYPE_MAX_N);

		for (i = 0; i < sensor->nbinning_subtypes; i++) {
			sensor->binning_subtypes[i].horizontal =
				CCS_LIM_AT(sensor, BINNING_SUB_TYPE, i) >>
				CCS_BINNING_SUB_TYPE_COLUMN_SHIFT;
			sensor->binning_subtypes[i].vertical =
				CCS_LIM_AT(sensor, BINNING_SUB_TYPE, i) &
				CCS_BINNING_SUB_TYPE_ROW_MASK;

			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_free_ccs_limits;
	}

	if (sensor->minfo.smiapp_version &&
	    CCS_LIM(sensor, DATA_TRANSFER_IF_CAPABILITY) &
	    CCS_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED) {
		if (device_create_file(&client->dev, &dev_attr_nvm) != 0) {
			dev_err(&client->dev, "sysfs nvm entry failed\n");
			rval = -EBUSY;
			goto out_cleanup;
		}
	}

	if (!CCS_LIM(sensor, MIN_OP_SYS_CLK_DIV) ||
	    !CCS_LIM(sensor, MAX_OP_SYS_CLK_DIV) ||
	    !CCS_LIM(sensor, MIN_OP_PIX_CLK_DIV) ||
	    !CCS_LIM(sensor, MAX_OP_PIX_CLK_DIV)) {
		/* No OP clock branch */
		sensor->pll.flags |= CCS_PLL_FLAG_NO_OP_CLOCKS;
	} else if (CCS_LIM(sensor, SCALING_CAPABILITY)
		   != CCS_SCALING_CAPABILITY_NONE ||
		   CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY)
		   == CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
		/* We have a scaler or digital 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 = CCS_LIM(sensor, SCALER_N_MIN);

	/* prepare PLL configuration input values */
	sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
	sensor->pll.csi2.lanes = sensor->hwcfg.lanes;
	if (CCS_LIM(sensor, CLOCK_CALCULATION) &
	    CCS_CLOCK_CALCULATION_LANE_SPEED) {
		sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL;
		if (CCS_LIM(sensor, CLOCK_CALCULATION) &
		    CCS_CLOCK_CALCULATION_LINK_DECOUPLED) {
			sensor->pll.vt_lanes =
				CCS_LIM(sensor, NUM_OF_VT_LANES) + 1;
			sensor->pll.op_lanes =
				CCS_LIM(sensor, NUM_OF_OP_LANES) + 1;
			sensor->pll.flags |= CCS_PLL_FLAG_LINK_DECOUPLED;
		} else {
			sensor->pll.vt_lanes = sensor->pll.csi2.lanes;
			sensor->pll.op_lanes = sensor->pll.csi2.lanes;
		}
	}
	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
	    CCS_CLOCK_TREE_PLL_CAPABILITY_EXT_DIVIDER)
		sensor->pll.flags |= CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER;
	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
	    CCS_CLOCK_TREE_PLL_CAPABILITY_FLEXIBLE_OP_PIX_CLK_DIV)
		sensor->pll.flags |= CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV;
	if (CCS_LIM(sensor, FIFO_SUPPORT_CAPABILITY) &
	    CCS_FIFO_SUPPORT_CAPABILITY_DERATING)
		sensor->pll.flags |= CCS_PLL_FLAG_FIFO_DERATING;
	if (CCS_LIM(sensor, FIFO_SUPPORT_CAPABILITY) &
	    CCS_FIFO_SUPPORT_CAPABILITY_DERATING_OVERRATING)
		sensor->pll.flags |= CCS_PLL_FLAG_FIFO_DERATING |
				     CCS_PLL_FLAG_FIFO_OVERRATING;
	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
	    CCS_CLOCK_TREE_PLL_CAPABILITY_DUAL_PLL) {
		if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
		    CCS_CLOCK_TREE_PLL_CAPABILITY_SINGLE_PLL) {
			u32 v;

			/* Use sensor default in PLL mode selection */
			rval = ccs_read(sensor, PLL_MODE, &v);
			if (rval)
				goto out_cleanup;

			if (v == CCS_PLL_MODE_DUAL)
				sensor->pll.flags |= CCS_PLL_FLAG_DUAL_PLL;
		} else {
			sensor->pll.flags |= CCS_PLL_FLAG_DUAL_PLL;
		}
		if (CCS_LIM(sensor, CLOCK_CALCULATION) &
		    CCS_CLOCK_CALCULATION_DUAL_PLL_OP_SYS_DDR)
			sensor->pll.flags |= CCS_PLL_FLAG_OP_SYS_DDR;
		if (CCS_LIM(sensor, CLOCK_CALCULATION) &
		    CCS_CLOCK_CALCULATION_DUAL_PLL_OP_PIX_DDR)
			sensor->pll.flags |= CCS_PLL_FLAG_OP_PIX_DDR;
	}
	sensor->pll.op_bits_per_lane = CCS_LIM(sensor, OP_BITS_PER_LANE);
	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);

	ccs_create_subdev(sensor, sensor->scaler, " scaler", 2,
			  MEDIA_ENT_F_PROC_VIDEO_SCALER);
	ccs_create_subdev(sensor, sensor->binner, " binner", 2,
			  MEDIA_ENT_F_PROC_VIDEO_SCALER);
	ccs_create_subdev(sensor, sensor->pixel_array, " pixel_array", 1,
			  MEDIA_ENT_F_CAM_SENSOR);

	rval = ccs_init_controls(sensor);
	if (rval < 0)
		goto out_cleanup;

	rval = ccs_call_quirk(sensor, init);
	if (rval)
		goto out_cleanup;

	rval = ccs_get_mbus_formats(sensor);
	if (rval) {
		rval = -ENODEV;
		goto out_cleanup;
	}

	rval = ccs_init_late_controls(sensor);
	if (rval) {
		rval = -ENODEV;
		goto out_cleanup;
	}

	mutex_lock(&sensor->mutex);
	rval = ccs_pll_blanking_update(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;

	rval = media_entity_pads_init(&sensor->src->sd.entity, 2,
				 sensor->src->pads);
	if (rval < 0)
		goto out_media_entity_cleanup;

	rval = ccs_write_msr_regs(sensor);
	if (rval)
		goto out_media_entity_cleanup;

	pm_runtime_set_active(&client->dev);
	pm_runtime_get_noresume(&client->dev);
	pm_runtime_enable(&client->dev);

	rval = v4l2_async_register_subdev_sensor(&sensor->src->sd);
	if (rval < 0)
		goto out_disable_runtime_pm;

	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
	pm_runtime_use_autosuspend(&client->dev);
	pm_runtime_put_autosuspend(&client->dev);

	return 0;

out_disable_runtime_pm:
	pm_runtime_put_noidle(&client->dev);
	pm_runtime_disable(&client->dev);

out_media_entity_cleanup:
	media_entity_cleanup(&sensor->src->sd.entity);

out_cleanup:
	ccs_cleanup(sensor);

out_release_mdata:
	kvfree(sensor->mdata.backing);

out_release_sdata:
	kvfree(sensor->sdata.backing);

out_free_ccs_limits:
	kfree(sensor->ccs_limits);

out_power_off:
	ccs_power_off(&client->dev);
	mutex_destroy(&sensor->mutex);

	return rval;
}