162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vimc-lens.c Virtual Media Controller Driver
462306a36Sopenharmony_ci * Copyright (C) 2022 Google, Inc
562306a36Sopenharmony_ci * Author: yunkec@google.com (Yunke Cao)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
962306a36Sopenharmony_ci#include <media/v4l2-event.h>
1062306a36Sopenharmony_ci#include <media/v4l2-subdev.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "vimc-common.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define VIMC_LENS_MAX_FOCUS_POS	1023
1562306a36Sopenharmony_ci#define VIMC_LENS_MAX_FOCUS_STEP	1
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct vimc_lens_device {
1862306a36Sopenharmony_ci	struct vimc_ent_device ved;
1962306a36Sopenharmony_ci	struct v4l2_subdev sd;
2062306a36Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
2162306a36Sopenharmony_ci	u32 focus_absolute;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops vimc_lens_core_ops = {
2562306a36Sopenharmony_ci	.log_status = v4l2_ctrl_subdev_log_status,
2662306a36Sopenharmony_ci	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
2762306a36Sopenharmony_ci	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const struct v4l2_subdev_ops vimc_lens_ops = {
3162306a36Sopenharmony_ci	.core = &vimc_lens_core_ops
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int vimc_lens_s_ctrl(struct v4l2_ctrl *ctrl)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct vimc_lens_device *vlens =
3762306a36Sopenharmony_ci		container_of(ctrl->handler, struct vimc_lens_device, hdl);
3862306a36Sopenharmony_ci	if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
3962306a36Sopenharmony_ci		vlens->focus_absolute = ctrl->val;
4062306a36Sopenharmony_ci		return 0;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci	return -EINVAL;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops vimc_lens_ctrl_ops = {
4662306a36Sopenharmony_ci	.s_ctrl = vimc_lens_s_ctrl,
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic struct vimc_ent_device *vimc_lens_add(struct vimc_device *vimc,
5062306a36Sopenharmony_ci					     const char *vcfg_name)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = &vimc->v4l2_dev;
5362306a36Sopenharmony_ci	struct vimc_lens_device *vlens;
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Allocate the vlens struct */
5762306a36Sopenharmony_ci	vlens = kzalloc(sizeof(*vlens), GFP_KERNEL);
5862306a36Sopenharmony_ci	if (!vlens)
5962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&vlens->hdl, 1);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	v4l2_ctrl_new_std(&vlens->hdl, &vimc_lens_ctrl_ops,
6462306a36Sopenharmony_ci			  V4L2_CID_FOCUS_ABSOLUTE, 0,
6562306a36Sopenharmony_ci			  VIMC_LENS_MAX_FOCUS_POS, VIMC_LENS_MAX_FOCUS_STEP, 0);
6662306a36Sopenharmony_ci	vlens->sd.ctrl_handler = &vlens->hdl;
6762306a36Sopenharmony_ci	if (vlens->hdl.error) {
6862306a36Sopenharmony_ci		ret = vlens->hdl.error;
6962306a36Sopenharmony_ci		goto err_free_vlens;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci	vlens->ved.dev = vimc->mdev.dev;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	ret = vimc_ent_sd_register(&vlens->ved, &vlens->sd, v4l2_dev,
7462306a36Sopenharmony_ci				   vcfg_name, MEDIA_ENT_F_LENS, 0,
7562306a36Sopenharmony_ci				   NULL, &vimc_lens_ops);
7662306a36Sopenharmony_ci	if (ret)
7762306a36Sopenharmony_ci		goto err_free_hdl;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return &vlens->ved;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cierr_free_hdl:
8262306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&vlens->hdl);
8362306a36Sopenharmony_cierr_free_vlens:
8462306a36Sopenharmony_ci	kfree(vlens);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return ERR_PTR(ret);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void vimc_lens_release(struct vimc_ent_device *ved)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct vimc_lens_device *vlens =
9262306a36Sopenharmony_ci		container_of(ved, struct vimc_lens_device, ved);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&vlens->hdl);
9562306a36Sopenharmony_ci	media_entity_cleanup(vlens->ved.ent);
9662306a36Sopenharmony_ci	kfree(vlens);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct vimc_ent_type vimc_lens_type = {
10062306a36Sopenharmony_ci	.add = vimc_lens_add,
10162306a36Sopenharmony_ci	.release = vimc_lens_release
10262306a36Sopenharmony_ci};
103