Path: blob/master/drivers/gpu/drm/display/drm_hdmi_cec_helper.c
26494 views
// SPDX-License-Identifier: MIT1/*2* Copyright (c) 2024 Linaro Ltd3*/45#include <drm/drm_bridge.h>6#include <drm/drm_connector.h>7#include <drm/drm_managed.h>8#include <drm/display/drm_hdmi_cec_helper.h>910#include <linux/export.h>11#include <linux/mutex.h>1213#include <media/cec.h>1415struct drm_connector_hdmi_cec_data {16struct cec_adapter *adapter;17const struct drm_connector_hdmi_cec_funcs *funcs;18};1920static int drm_connector_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)21{22struct drm_connector *connector = cec_get_drvdata(adap);23struct drm_connector_hdmi_cec_data *data = connector->cec.data;2425return data->funcs->enable(connector, enable);26}2728static int drm_connector_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)29{30struct drm_connector *connector = cec_get_drvdata(adap);31struct drm_connector_hdmi_cec_data *data = connector->cec.data;3233return data->funcs->log_addr(connector, logical_addr);34}3536static int drm_connector_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,37u32 signal_free_time, struct cec_msg *msg)38{39struct drm_connector *connector = cec_get_drvdata(adap);40struct drm_connector_hdmi_cec_data *data = connector->cec.data;4142return data->funcs->transmit(connector, attempts, signal_free_time, msg);43}4445static const struct cec_adap_ops drm_connector_hdmi_cec_adap_ops = {46.adap_enable = drm_connector_hdmi_cec_adap_enable,47.adap_log_addr = drm_connector_hdmi_cec_adap_log_addr,48.adap_transmit = drm_connector_hdmi_cec_adap_transmit,49};5051static void drm_connector_hdmi_cec_adapter_phys_addr_invalidate(struct drm_connector *connector)52{53struct drm_connector_hdmi_cec_data *data = connector->cec.data;5455cec_phys_addr_invalidate(data->adapter);56}5758static void drm_connector_hdmi_cec_adapter_phys_addr_set(struct drm_connector *connector,59u16 addr)60{61struct drm_connector_hdmi_cec_data *data = connector->cec.data;6263cec_s_phys_addr(data->adapter, addr, false);64}6566static void drm_connector_hdmi_cec_adapter_unregister(struct drm_device *dev, void *res)67{68struct drm_connector *connector = res;69struct drm_connector_hdmi_cec_data *data = connector->cec.data;7071cec_unregister_adapter(data->adapter);7273if (data->funcs->uninit)74data->funcs->uninit(connector);7576kfree(data);77connector->cec.data = NULL;78}7980static struct drm_connector_cec_funcs drm_connector_hdmi_cec_adapter_funcs = {81.phys_addr_invalidate = drm_connector_hdmi_cec_adapter_phys_addr_invalidate,82.phys_addr_set = drm_connector_hdmi_cec_adapter_phys_addr_set,83};8485int drmm_connector_hdmi_cec_register(struct drm_connector *connector,86const struct drm_connector_hdmi_cec_funcs *funcs,87const char *name,88u8 available_las,89struct device *dev)90{91struct drm_connector_hdmi_cec_data *data;92struct cec_connector_info conn_info;93struct cec_adapter *cec_adap;94int ret;9596if (!funcs->init || !funcs->enable || !funcs->log_addr || !funcs->transmit)97return -EINVAL;9899data = kzalloc(sizeof(*data), GFP_KERNEL);100if (!data)101return -ENOMEM;102103data->funcs = funcs;104105cec_adap = cec_allocate_adapter(&drm_connector_hdmi_cec_adap_ops, connector, name,106CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO,107available_las ? : CEC_MAX_LOG_ADDRS);108ret = PTR_ERR_OR_ZERO(cec_adap);109if (ret < 0)110goto err_free;111112cec_fill_conn_info_from_drm(&conn_info, connector);113cec_s_conn_info(cec_adap, &conn_info);114115data->adapter = cec_adap;116117mutex_lock(&connector->cec.mutex);118119connector->cec.data = data;120connector->cec.funcs = &drm_connector_hdmi_cec_adapter_funcs;121122ret = funcs->init(connector);123if (ret < 0)124goto err_delete_adapter;125126/*127* NOTE: the CEC adapter will be unregistered by drmm cleanup from128* drm_managed_release(), which is called from drm_dev_release()129* during device unbind.130*131* However, the CEC framework cleans up the CEC adapter only when the132* last user has closed its file descriptor, so we don't need to handle133* it in DRM.134*135* Before that CEC framework makes sure that even if the userspace136* still holds CEC device open, all calls will be shortcut via137* cec_is_registered(), making sure that there is no access to the138* freed memory.139*/140ret = cec_register_adapter(cec_adap, dev);141if (ret < 0)142goto err_delete_adapter;143144mutex_unlock(&connector->cec.mutex);145146return drmm_add_action_or_reset(connector->dev,147drm_connector_hdmi_cec_adapter_unregister,148connector);149150err_delete_adapter:151cec_delete_adapter(cec_adap);152153connector->cec.data = NULL;154155mutex_unlock(&connector->cec.mutex);156157err_free:158kfree(data);159160return ret;161}162EXPORT_SYMBOL(drmm_connector_hdmi_cec_register);163164void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector,165struct cec_msg *msg)166{167struct drm_connector_hdmi_cec_data *data = connector->cec.data;168169cec_received_msg(data->adapter, msg);170}171EXPORT_SYMBOL(drm_connector_hdmi_cec_received_msg);172173void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *connector,174u8 status)175{176struct drm_connector_hdmi_cec_data *data = connector->cec.data;177178cec_transmit_attempt_done(data->adapter, status);179}180EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_attempt_done);181182void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector,183u8 status,184u8 arb_lost_cnt, u8 nack_cnt,185u8 low_drive_cnt, u8 error_cnt)186{187struct drm_connector_hdmi_cec_data *data = connector->cec.data;188189cec_transmit_done(data->adapter, status,190arb_lost_cnt, nack_cnt, low_drive_cnt, error_cnt);191}192EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_done);193194195