[RFC PATCH v3 3/6] dpll: add support for source selection modes

Vadim Fedorenko vfedorenko at novek.ru
Sun Oct 9 18:18:01 PDT 2022


From: Arkadiusz Kubalewski <arkadiusz.kubalewski at intel.com>

Allow to configure dpll device for different source selection modes.
Allow to configure priority of a sources for autmoatic selection mode.

Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski at intel.com>
---
 drivers/dpll/dpll_netlink.c | 170 ++++++++++++++++++++++++++++++++++--
 drivers/dpll/dpll_netlink.h |   2 +
 include/linux/dpll.h        |   7 ++
 include/uapi/linux/dpll.h   |  22 ++++-
 4 files changed, 192 insertions(+), 9 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 6dc92b5b712e..a5779871537a 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -23,6 +23,7 @@ static const struct nla_policy dpll_genl_get_policy[] = {
 	[DPLLA_DEVICE_ID]	= { .type = NLA_U32 },
 	[DPLLA_DEVICE_NAME]	= { .type = NLA_STRING,
 				    .len = DPLL_NAME_LENGTH },
+	[DPLLA_DEVICE_SRC_SELECT_MODE] = { .type = NLA_U32 },
 	[DPLLA_FLAGS]		= { .type = NLA_U32 },
 };
 
@@ -38,13 +39,26 @@ static const struct nla_policy dpll_genl_set_output_policy[] = {
 	[DPLLA_OUTPUT_TYPE]	= { .type = NLA_U32 },
 };
 
+static const struct nla_policy dpll_genl_set_src_select_mode_policy[] = {
+	[DPLLA_DEVICE_ID]		  = { .type = NLA_U32 },
+	[DPLLA_DEVICE_SRC_SELECT_MODE] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy dpll_genl_set_source_prio_policy[] = {
+	[DPLLA_DEVICE_ID]	= { .type = NLA_U32 },
+	[DPLLA_SOURCE_ID]	= { .type = NLA_U32 },
+	[DPLLA_SOURCE_PRIO]	= { .type = NLA_U32 },
+};
+
 struct param {
 	struct netlink_callback *cb;
 	struct dpll_device *dpll;
 	struct sk_buff *msg;
 	int dpll_id;
+	int dpll_src_select_mode;
 	int dpll_source_id;
 	int dpll_source_type;
+	int dpll_source_prio;
 	int dpll_output_id;
 	int dpll_output_type;
 	int dpll_status;
@@ -84,8 +98,8 @@ static int __dpll_cmd_device_dump_one(struct dpll_device *dpll,
 static int __dpll_cmd_dump_sources(struct dpll_device *dpll,
 					   struct sk_buff *msg)
 {
+	int i, ret = 0, type, prio;
 	struct nlattr *src_attr;
-	int i, ret = 0, type;
 
 	for (i = 0; i < dpll->sources_count; i++) {
 		src_attr = nla_nest_start(msg, DPLLA_SOURCE);
@@ -110,6 +124,14 @@ static int __dpll_cmd_dump_sources(struct dpll_device *dpll,
 			}
 			ret = 0;
 		}
+		if (dpll->ops->get_source_prio) {
+			prio = dpll->ops->get_source_prio(dpll, i);
+			if (nla_put_u32(msg, DPLLA_SOURCE_PRIO, prio)) {
+				nla_nest_cancel(msg, src_attr);
+				ret = -EMSGSIZE;
+				break;
+			}
+		}
 		nla_nest_end(msg, src_attr);
 	}
 
@@ -154,26 +176,51 @@ static int __dpll_cmd_dump_outputs(struct dpll_device *dpll,
 static int __dpll_cmd_dump_status(struct dpll_device *dpll,
 					   struct sk_buff *msg)
 {
-	int ret;
+	struct dpll_device_ops *ops = dpll->ops;
+	int ret, type, attr;
 
-	if (dpll->ops->get_status) {
-		ret = dpll->ops->get_status(dpll);
+	if (ops->get_status) {
+		ret = ops->get_status(dpll);
 		if (nla_put_u32(msg, DPLLA_STATUS, ret))
 			return -EMSGSIZE;
 	}
 
-	if (dpll->ops->get_temp) {
-		ret = dpll->ops->get_temp(dpll);
+	if (ops->get_temp) {
+		ret = ops->get_temp(dpll);
 		if (nla_put_u32(msg, DPLLA_TEMP, ret))
 			return -EMSGSIZE;
 	}
 
-	if (dpll->ops->get_lock_status) {
-		ret = dpll->ops->get_lock_status(dpll);
+	if (ops->get_lock_status) {
+		ret = ops->get_lock_status(dpll);
 		if (nla_put_u32(msg, DPLLA_LOCK_STATUS, ret))
 			return -EMSGSIZE;
 	}
 
+	if (ops->get_source_select_mode) {
+		ret = ops->get_source_select_mode(dpll);
+		if (nla_put_u32(msg, DPLLA_DEVICE_SRC_SELECT_MODE, ret))
+			return -EMSGSIZE;
+	} else {
+		if (nla_put_u32(msg, DPLLA_DEVICE_SRC_SELECT_MODE,
+				DPLL_SRC_SELECT_FORCED))
+			return -EMSGSIZE;
+	}
+
+	if (ops->get_source_select_mode_supported) {
+		attr = DPLLA_DEVICE_SRC_SELECT_MODE_SUPPORTED;
+		for (type = 0; type <= DPLL_SRC_SELECT_MAX; type++) {
+			ret = ops->get_source_select_mode_supported(dpll,
+								    type);
+			if (ret && nla_put_u32(msg, attr, type))
+				return -EMSGSIZE;
+		}
+	} else {
+		if (nla_put_u32(msg, DPLLA_DEVICE_SRC_SELECT_MODE_SUPPORTED,
+				DPLL_SRC_SELECT_FORCED))
+			return -EMSGSIZE;
+	}
+
 	return 0;
 }
 
@@ -275,6 +322,56 @@ static int dpll_genl_cmd_set_output(struct sk_buff *skb, struct genl_info *info)
 	return ret;
 }
 
+static int dpll_genl_cmd_set_source_prio(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct nlattr **attrs = info->attrs;
+	int ret = 0, src_id, prio;
+
+	if (!attrs[DPLLA_SOURCE_ID] ||
+	    !attrs[DPLLA_SOURCE_PRIO])
+		return -EINVAL;
+
+	if (!dpll->ops->set_source_prio)
+		return -EOPNOTSUPP;
+
+	src_id = nla_get_u32(attrs[DPLLA_SOURCE_ID]);
+	prio = nla_get_u32(attrs[DPLLA_SOURCE_PRIO]);
+
+	mutex_lock(&dpll->lock);
+	ret = dpll->ops->set_source_prio(dpll, src_id, prio);
+	mutex_unlock(&dpll->lock);
+
+	if (!ret)
+		dpll_notify_source_prio_change(dpll->id, src_id, prio);
+
+	return ret;
+}
+
+static int dpll_genl_cmd_set_select_mode(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct nlattr **attrs = info->attrs;
+	int ret = 0, mode;
+
+	if (!attrs[DPLLA_DEVICE_SRC_SELECT_MODE])
+		return -EINVAL;
+
+	if (!dpll->ops->set_source_select_mode)
+		return -EOPNOTSUPP;
+
+	mode = nla_get_u32(attrs[DPLLA_DEVICE_SRC_SELECT_MODE]);
+
+	mutex_lock(&dpll->lock);
+	ret = dpll->ops->set_source_select_mode(dpll, mode);
+	mutex_unlock(&dpll->lock);
+
+	if (!ret)
+		dpll_notify_source_select_mode_change(dpll->id, mode);
+
+	return ret;
+}
+
 static int dpll_device_loop_cb(struct dpll_device *dpll, void *data)
 {
 	struct dpll_dump_ctx *ctx;
@@ -397,6 +494,20 @@ static const struct genl_ops dpll_genl_ops[] = {
 		.policy	= dpll_genl_set_output_policy,
 		.maxattr = ARRAY_SIZE(dpll_genl_set_output_policy) - 1,
 	},
+	{
+		.cmd	= DPLL_CMD_SET_SRC_SELECT_MODE,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= dpll_genl_cmd_set_select_mode,
+		.policy	= dpll_genl_set_src_select_mode_policy,
+		.maxattr = ARRAY_SIZE(dpll_genl_set_src_select_mode_policy) - 1,
+	},
+	{
+		.cmd	= DPLL_CMD_SET_SOURCE_PRIO,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= dpll_genl_cmd_set_source_prio,
+		.policy	= dpll_genl_set_source_prio_policy,
+		.maxattr = ARRAY_SIZE(dpll_genl_set_source_prio_policy) - 1,
+	},
 };
 
 static struct genl_family dpll_gnl_family __ro_after_init = {
@@ -456,6 +567,26 @@ static int dpll_event_output_change(struct param *p)
 	return 0;
 }
 
+static int dpll_event_source_prio(struct param *p)
+{
+	if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) ||
+	    nla_put_u32(p->msg, DPLLA_SOURCE_ID, p->dpll_source_id) ||
+	    nla_put_u32(p->msg, DPLLA_SOURCE_PRIO, p->dpll_source_prio))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_event_select_mode(struct param *p)
+{
+	if (nla_put_u32(p->msg, DPLLA_DEVICE_ID, p->dpll_id) ||
+	    nla_put_u32(p->msg, DPLLA_DEVICE_SRC_SELECT_MODE,
+		    p->dpll_src_select_mode))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
 static const cb_t event_cb[] = {
 	[DPLL_EVENT_DEVICE_CREATE]	= dpll_event_device_create,
 	[DPLL_EVENT_DEVICE_DELETE]	= dpll_event_device_delete,
@@ -463,7 +594,10 @@ static const cb_t event_cb[] = {
 	[DPLL_EVENT_STATUS_UNLOCKED]	= dpll_event_status,
 	[DPLL_EVENT_SOURCE_CHANGE]	= dpll_event_source_change,
 	[DPLL_EVENT_OUTPUT_CHANGE]	= dpll_event_output_change,
+	[DPLL_EVENT_SOURCE_PRIO]        = dpll_event_source_prio,
+	[DPLL_EVENT_SELECT_MODE]        = dpll_event_select_mode,
 };
+
 /*
  * Generic netlink DPLL event encoding
  */
@@ -552,6 +686,26 @@ int dpll_notify_output_change(int dpll_id, int output_id, int output_type)
 }
 EXPORT_SYMBOL_GPL(dpll_notify_output_change);
 
+int dpll_notify_source_select_mode_change(int dpll_id, int new_mode)
+{
+	struct param p =  { .dpll_id = dpll_id,
+			    .dpll_src_select_mode = new_mode,
+			    .dpll_event_group = 0 };
+
+	return dpll_send_event(DPLL_EVENT_SELECT_MODE, &p);
+}
+EXPORT_SYMBOL_GPL(dpll_notify_source_select_mode_change);
+
+int dpll_notify_source_prio_change(int dpll_id, int source_id, int prio)
+{
+	struct param p =  { .dpll_id = dpll_id, .dpll_source_id = source_id,
+			    .dpll_source_prio = prio,
+			    .dpll_event_group = 1 };
+
+	return dpll_send_event(DPLL_EVENT_SOURCE_PRIO, &p);
+}
+EXPORT_SYMBOL_GPL(dpll_notify_source_prio_change);
+
 int __init dpll_netlink_init(void)
 {
 	return genl_register_family(&dpll_gnl_family);
diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
index 5c1d1072e818..a4962fa0c8c2 100644
--- a/drivers/dpll/dpll_netlink.h
+++ b/drivers/dpll/dpll_netlink.h
@@ -5,6 +5,8 @@
 
 int dpll_notify_device_create(int dpll_id, const char *name);
 int dpll_notify_device_delete(int dpll_id);
+int dpll_notify_source_prio(int dpll_id, int source_id, int prio);
+int dpll_notify_select_mode(int dpll_id, int mode);
 
 int __init dpll_netlink_init(void);
 void dpll_netlink_finish(void);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 32558965cd41..3fe957a06b90 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -12,12 +12,17 @@ struct dpll_device_ops {
 	int (*get_status)(struct dpll_device *dpll);
 	int (*get_temp)(struct dpll_device *dpll);
 	int (*get_lock_status)(struct dpll_device *dpll);
+	int (*get_source_select_mode)(struct dpll_device *dpll);
+	int (*get_source_select_mode_supported)(struct dpll_device *dpll, int type);
 	int (*get_source_type)(struct dpll_device *dpll, int id);
 	int (*get_source_supported)(struct dpll_device *dpll, int id, int type);
+	int (*get_source_prio)(struct dpll_device *dpll, int id);
 	int (*get_output_type)(struct dpll_device *dpll, int id);
 	int (*get_output_supported)(struct dpll_device *dpll, int id, int type);
 	int (*set_source_type)(struct dpll_device *dpll, int id, int val);
 	int (*set_output_type)(struct dpll_device *dpll, int id, int val);
+	int (*set_source_select_mode)(struct dpll_device *dpll, int mode);
+	int (*set_source_prio)(struct dpll_device *dpll, int id, int prio);
 };
 
 struct dpll_device *dpll_device_alloc(struct dpll_device_ops *ops, const char *name,
@@ -31,4 +36,6 @@ int dpll_notify_status_locked(int dpll_id);
 int dpll_notify_status_unlocked(int dpll_id);
 int dpll_notify_source_change(int dpll_id, int source_id, int source_type);
 int dpll_notify_output_change(int dpll_id, int output_id, int output_type);
+int dpll_notify_source_select_mode_change(int dpll_id, int source_select_mode);
+int dpll_notify_source_prio_change(int dpll_id, int source_id, int prio);
 #endif
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
index fcbea5a5e4d6..f6b674e5cf01 100644
--- a/include/uapi/linux/dpll.h
+++ b/include/uapi/linux/dpll.h
@@ -21,10 +21,13 @@ enum dpll_genl_attr {
 	DPLLA_UNSPEC,
 	DPLLA_DEVICE_ID,
 	DPLLA_DEVICE_NAME,
+	DPLLA_DEVICE_SRC_SELECT_MODE,
+	DPLLA_DEVICE_SRC_SELECT_MODE_SUPPORTED,
 	DPLLA_SOURCE,
 	DPLLA_SOURCE_ID,
 	DPLLA_SOURCE_TYPE,
 	DPLLA_SOURCE_SUPPORTED,
+	DPLLA_SOURCE_PRIO,
 	DPLLA_OUTPUT,
 	DPLLA_OUTPUT_ID,
 	DPLLA_OUTPUT_TYPE,
@@ -82,6 +85,8 @@ enum dpll_genl_event {
 	DPLL_EVENT_STATUS_UNLOCKED,	/* DPLL device freerun */
 	DPLL_EVENT_SOURCE_CHANGE,		/* DPLL device source changed */
 	DPLL_EVENT_OUTPUT_CHANGE,		/* DPLL device output changed */
+	DPLL_EVENT_SOURCE_PRIO,
+	DPLL_EVENT_SELECT_MODE,
 
 	__DPLL_EVENT_MAX,
 };
@@ -90,12 +95,27 @@ enum dpll_genl_event {
 /* Commands supported by the dpll_genl_family */
 enum dpll_genl_cmd {
 	DPLL_CMD_UNSPEC,
-	DPLL_CMD_DEVICE_GET,	/* List of DPLL devices id */
+	DPLL_CMD_DEVICE_GET,		/* List of DPLL devices id */
 	DPLL_CMD_SET_SOURCE_TYPE,	/* Set the DPLL device source type */
 	DPLL_CMD_SET_OUTPUT_TYPE,	/* Set the DPLL device output type */
+	DPLL_CMD_SET_SRC_SELECT_MODE,/* Set mode for selection of a source */
+	DPLL_CMD_SET_SOURCE_PRIO,	/* Set priority of a source */
 
 	__DPLL_CMD_MAX,
 };
 #define DPLL_CMD_MAX (__DPLL_CMD_MAX - 1)
 
+/* Source select modes of dpll */
+enum dpll_genl_source_select_mode {
+	DPLL_SRC_SELECT_UNSPEC,
+	DPLL_SRC_SELECT_FORCED,   /* Source forced by DPLL_CMD_SET_SOURCE_TYPE */
+	DPLL_SRC_SELECT_AUTOMATIC,/* highest prio, valid source, auto selected by dpll */
+	DPLL_SRC_SELECT_HOLDOVER, /* forced holdover */
+	DPLL_SRC_SELECT_FREERUN,  /* dpll driven on system clk, no holdover available */
+	DPLL_SRC_SELECT_NCO,	     /* Set the DPLL device output type */
+
+	__DPLL_SRC_SELECT_MAX,
+};
+#define DPLL_SRC_SELECT_MAX (__DPLL_SRC_SELECT_MAX - 1)
+
 #endif /* _UAPI_LINUX_DPLL_H */
-- 
2.27.0




More information about the linux-arm-kernel mailing list