root/drivers/media/test-drivers/vimc/vimc-lens.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * vimc-lens.c Virtual Media Controller Driver
 * Copyright (C) 2022 Google, Inc
 * Author: yunkec@google.com (Yunke Cao)
 */

#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-subdev.h>

#include "vimc-common.h"

#define VIMC_LENS_MAX_FOCUS_POS 1023
#define VIMC_LENS_MAX_FOCUS_STEP        1

struct vimc_lens_device {
        struct vimc_ent_device ved;
        struct v4l2_subdev sd;
        struct v4l2_ctrl_handler hdl;
        u32 focus_absolute;
};

static const struct v4l2_subdev_core_ops vimc_lens_core_ops = {
        .log_status = v4l2_ctrl_subdev_log_status,
        .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
        .unsubscribe_event = v4l2_event_subdev_unsubscribe,
};

static const struct v4l2_subdev_ops vimc_lens_ops = {
        .core = &vimc_lens_core_ops
};

static int vimc_lens_s_ctrl(struct v4l2_ctrl *ctrl)
{
        struct vimc_lens_device *vlens =
                container_of(ctrl->handler, struct vimc_lens_device, hdl);
        if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
                vlens->focus_absolute = ctrl->val;
                return 0;
        }
        return -EINVAL;
}

static const struct v4l2_ctrl_ops vimc_lens_ctrl_ops = {
        .s_ctrl = vimc_lens_s_ctrl,
};

static struct vimc_ent_device *vimc_lens_add(struct vimc_device *vimc,
                                             const char *vcfg_name)
{
        struct v4l2_device *v4l2_dev = &vimc->v4l2_dev;
        struct vimc_lens_device *vlens;
        int ret;

        /* Allocate the vlens struct */
        vlens = kzalloc_obj(*vlens);
        if (!vlens)
                return ERR_PTR(-ENOMEM);

        v4l2_ctrl_handler_init(&vlens->hdl, 1);

        v4l2_ctrl_new_std(&vlens->hdl, &vimc_lens_ctrl_ops,
                          V4L2_CID_FOCUS_ABSOLUTE, 0,
                          VIMC_LENS_MAX_FOCUS_POS, VIMC_LENS_MAX_FOCUS_STEP, 0);
        vlens->sd.ctrl_handler = &vlens->hdl;
        if (vlens->hdl.error) {
                ret = vlens->hdl.error;
                goto err_free_vlens;
        }
        vlens->ved.dev = vimc->mdev.dev;

        ret = vimc_ent_sd_register(&vlens->ved, &vlens->sd, v4l2_dev,
                                   vcfg_name, MEDIA_ENT_F_LENS, 0,
                                   NULL, NULL, &vimc_lens_ops);
        if (ret)
                goto err_free_hdl;

        return &vlens->ved;

err_free_hdl:
        v4l2_ctrl_handler_free(&vlens->hdl);
err_free_vlens:
        kfree(vlens);

        return ERR_PTR(ret);
}

static void vimc_lens_release(struct vimc_ent_device *ved)
{
        struct vimc_lens_device *vlens =
                container_of(ved, struct vimc_lens_device, ved);

        v4l2_ctrl_handler_free(&vlens->hdl);
        v4l2_subdev_cleanup(&vlens->sd);
        media_entity_cleanup(vlens->ved.ent);
        kfree(vlens);
}

const struct vimc_ent_type vimc_lens_type = {
        .add = vimc_lens_add,
        .release = vimc_lens_release
};