summaryrefslogtreecommitdiff
path: root/drivers/dpll/dpll_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dpll/dpll_netlink.c')
-rw-r--r--drivers/dpll/dpll_netlink.c50
1 files changed, 42 insertions, 8 deletions
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 09a6c2a1ea92..a6dc3997bf5c 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -615,8 +615,10 @@ static int
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
struct netlink_ext_ack *extack)
{
- u64 freq = nla_get_u64(a);
- struct dpll_pin_ref *ref;
+ u64 freq = nla_get_u64(a), old_freq;
+ struct dpll_pin_ref *ref, *failed;
+ const struct dpll_pin_ops *ops;
+ struct dpll_device *dpll;
unsigned long i;
int ret;
@@ -626,19 +628,51 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
}
xa_for_each(&pin->dpll_refs, i, ref) {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
-
- if (!ops->frequency_set)
+ ops = dpll_pin_ops(ref);
+ if (!ops->frequency_set || !ops->frequency_get) {
+ NL_SET_ERR_MSG(extack, "frequency set not supported by the device");
return -EOPNOTSUPP;
+ }
+ }
+ ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+ ops = dpll_pin_ops(ref);
+ dpll = ref->dpll;
+ ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+ dpll_priv(dpll), &old_freq, extack);
+ if (ret) {
+ NL_SET_ERR_MSG(extack, "unable to get old frequency value");
+ return ret;
+ }
+ if (freq == old_freq)
+ return 0;
+
+ xa_for_each(&pin->dpll_refs, i, ref) {
+ ops = dpll_pin_ops(ref);
+ dpll = ref->dpll;
ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), freq, extack);
- if (ret)
- return ret;
+ if (ret) {
+ failed = ref;
+ NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",
+ dpll->id);
+ goto rollback;
+ }
}
__dpll_pin_change_ntf(pin);
return 0;
+
+rollback:
+ xa_for_each(&pin->dpll_refs, i, ref) {
+ if (ref == failed)
+ break;
+ ops = dpll_pin_ops(ref);
+ dpll = ref->dpll;
+ if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+ dpll, dpll_priv(dpll), old_freq, extack))
+ NL_SET_ERR_MSG(extack, "set frequency rollback failed");
+ }
+ return ret;
}
static int