Commit 7c898b71 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'dpll-zl3073x-refactor-state-management'

Ivan Vecera says:

====================
dpll: zl3073x: Refactor state management

This patch set is a refactoring of the zl3073x driver to clean up
state management, improve modularity, and significantly reduce
on-demand I/O.

The driver's dpll.c implementation previously performed on-demand
register reads and writes (wrapped in mailbox operations) to get
or set properties like frequency, phase, and embedded-sync settings.
This cluttered the DPLL logic with low-level I/O, duplicated locking,
and led to inefficient bus traffic.

This series addresses this by:
1. Splitting the monolithic 'core.c' into logical units ('ref.c',
   'out.c', 'synth.c').
2. Implementing a full read/write-back cache for 'zl3073x_ref' and
   'zl3073x_out' structures.

All state is now read once during '_state_fetch()' (and status updated
periodically). DPLL get callbacks read from this cache. Set callbacks
modify a copy of the state, which is then committed via a new
'..._state_set()' function. These '_state_set' functions compare
the new state to the cached state and write *only* the modified
register values back to the hardware, all within a single mailbox
sequence.

The result is a much cleaner 'dpll.c' that is almost entirely
free of direct register I/O, and all state logic is properly
encapsulated in its respective file.

The series is broken down as follows:

* Patch 1: Changes the state structs to store raw register values
  (e.g., 'config', 'ctrl') instead of parsed booleans, centralizing
  parsing logic into the helpers.
* Patch 2: Splits the logic from 'core.c' into new 'ref.c', 'out.c'
  and 'synth.c' files, creating a 'zl3073x_dev_...' abstraction layer.
* Patch 3: Introduces the caching concept by reading and caching
  the reference monitor status periodically, removing scattered
  reads from 'dpll.c'.
* Patch 4: Expands the 'zl3073x_ref' struct to cache *all* reference
  properties and adds 'zl3073x_ref_state_set()' to write back changes.
* Patch 5: Does the same for the 'zl3073x_out' struct, caching all
  output properties and adding 'zl3073x_out_state_set()'.
* Patch 6: A final cleanup that removes the 'zl3073x_dev_...' wrapper
  functions that became redundant after the refactoring.
====================

Link: https://patch.msgid.link/20251113074105.141379-1-ivecera@redhat.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 5b8b343c 01e0e8b6
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0

obj-$(CONFIG_ZL3073X)		+= zl3073x.o
zl3073x-objs			:= core.o devlink.o dpll.o flash.o fw.o prop.o
zl3073x-objs			:= core.o devlink.o dpll.o flash.o fw.o	\
				   out.o prop.o ref.o synth.o

obj-$(CONFIG_ZL3073X_I2C)	+= zl3073x_i2c.o
zl3073x_i2c-objs		:= i2c.o
+18 −225
Original line number Diff line number Diff line
@@ -129,47 +129,6 @@ const struct regmap_config zl3073x_regmap_config = {
};
EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X");

/**
 * zl3073x_ref_freq_factorize - factorize given frequency
 * @freq: input frequency
 * @base: base frequency
 * @mult: multiplier
 *
 * Checks if the given frequency can be factorized using one of the
 * supported base frequencies. If so the base frequency and multiplier
 * are stored into appropriate parameters if they are not NULL.
 *
 * Return: 0 on success, -EINVAL if the frequency cannot be factorized
 */
int
zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
{
	static const u16 base_freqs[] = {
		1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125,
		128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000,
		1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250,
		6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250,
		32000, 40000, 50000, 62500,
	};
	u32 div;
	int i;

	for (i = 0; i < ARRAY_SIZE(base_freqs); i++) {
		div = freq / base_freqs[i];

		if (div <= U16_MAX && (freq % base_freqs[i]) == 0) {
			if (base)
				*base = base_freqs[i];
			if (mult)
				*mult = div;

			return 0;
		}
	}

	return -EINVAL;
}

static bool
zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size)
{
@@ -593,190 +552,6 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
	return rc;
}

/**
 * zl3073x_ref_state_fetch - get input reference state
 * @zldev: pointer to zl3073x_dev structure
 * @index: input reference index to fetch state for
 *
 * Function fetches information for the given input reference that are
 * invariant and stores them for later use.
 *
 * Return: 0 on success, <0 on error
 */
static int
zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
	struct zl3073x_ref *input = &zldev->ref[index];
	u8 ref_config;
	int rc;

	/* If the input is differential then the configuration for N-pin
	 * reference is ignored and P-pin config is used for both.
	 */
	if (zl3073x_is_n_pin(index) &&
	    zl3073x_ref_is_diff(zldev, index - 1)) {
		input->enabled = zl3073x_ref_is_enabled(zldev, index - 1);
		input->diff = true;

		return 0;
	}

	guard(mutex)(&zldev->multiop_lock);

	/* Read reference configuration */
	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
			   ZL_REG_REF_MB_MASK, BIT(index));
	if (rc)
		return rc;

	/* Read ref_config register */
	rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config);
	if (rc)
		return rc;

	input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config);
	input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config);

	dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
		str_enabled_disabled(input->enabled),
		input->diff ? "differential" : "single-ended");

	return rc;
}

/**
 * zl3073x_out_state_fetch - get output state
 * @zldev: pointer to zl3073x_dev structure
 * @index: output index to fetch state for
 *
 * Function fetches information for the given output (not output pin)
 * that are invariant and stores them for later use.
 *
 * Return: 0 on success, <0 on error
 */
static int
zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
	struct zl3073x_out *out = &zldev->out[index];
	u8 output_ctrl, output_mode;
	int rc;

	/* Read output configuration */
	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl);
	if (rc)
		return rc;

	/* Store info about output enablement and synthesizer the output
	 * is connected to.
	 */
	out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl);
	out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl);

	dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
		str_enabled_disabled(out->enabled), out->synth);

	guard(mutex)(&zldev->multiop_lock);

	/* Read output configuration */
	rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
			   ZL_REG_OUTPUT_MB_MASK, BIT(index));
	if (rc)
		return rc;

	/* Read output_mode */
	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
	if (rc)
		return rc;

	/* Extract and store output signal format */
	out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT,
				       output_mode);

	dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
		out->signal_format);

	return rc;
}

/**
 * zl3073x_synth_state_fetch - get synth state
 * @zldev: pointer to zl3073x_dev structure
 * @index: synth index to fetch state for
 *
 * Function fetches information for the given synthesizer that are
 * invariant and stores them for later use.
 *
 * Return: 0 on success, <0 on error
 */
static int
zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
	struct zl3073x_synth *synth = &zldev->synth[index];
	u16 base, m, n;
	u8 synth_ctrl;
	u32 mult;
	int rc;

	/* Read synth control register */
	rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl);
	if (rc)
		return rc;

	/* Store info about synth enablement and DPLL channel the synth is
	 * driven by.
	 */
	synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
	synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);

	dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
		str_enabled_disabled(synth->enabled), synth->dpll);

	guard(mutex)(&zldev->multiop_lock);

	/* Read synth configuration */
	rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
			   ZL_REG_SYNTH_MB_MASK, BIT(index));
	if (rc)
		return rc;

	/* The output frequency is determined by the following formula:
	 * base * multiplier * numerator / denominator
	 *
	 * Read registers with these values
	 */
	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
	if (rc)
		return rc;

	rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
	if (rc)
		return rc;

	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
	if (rc)
		return rc;

	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
	if (rc)
		return rc;

	/* Check denominator for zero to avoid div by 0 */
	if (!n) {
		dev_err(zldev->dev,
			"Zero divisor for SYNTH%u retrieved from device\n",
			index);
		return -EINVAL;
	}

	/* Compute and store synth frequency */
	zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);

	dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
		zldev->synth[index].freq);

	return rc;
}

static int
zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
{
@@ -816,6 +591,21 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
	return rc;
}

static void
zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
{
	int i, rc;

	for (i = 0; i < ZL3073X_NUM_REFS; i++) {
		rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
				     &zldev->ref[i].mon_status);
		if (rc)
			dev_warn(zldev->dev,
				 "Failed to get REF%u status: %pe\n", i,
				 ERR_PTR(rc));
	}
}

/**
 * zl3073x_ref_phase_offsets_update - update reference phase offsets
 * @zldev: pointer to zl3073x_dev structure
@@ -935,6 +725,9 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
	struct zl3073x_dpll *zldpll;
	int rc;

	/* Update input references status */
	zl3073x_dev_ref_status_update(zldev);

	/* Update DPLL-to-connected-ref phase offsets registers */
	rc = zl3073x_ref_phase_offsets_update(zldev, -1);
	if (rc)
+62 −122
Original line number Diff line number Diff line
@@ -9,7 +9,10 @@
#include <linux/mutex.h>
#include <linux/types.h>

#include "out.h"
#include "ref.h"
#include "regs.h"
#include "synth.h"

struct device;
struct regmap;
@@ -27,42 +30,6 @@ struct zl3073x_dpll;
#define ZL3073X_NUM_PINS	(ZL3073X_NUM_INPUT_PINS + \
				 ZL3073X_NUM_OUTPUT_PINS)

/**
 * struct zl3073x_ref - input reference invariant info
 * @enabled: input reference is enabled or disabled
 * @diff: true if input reference is differential
 * @ffo: current fractional frequency offset
 */
struct zl3073x_ref {
	bool	enabled;
	bool	diff;
	s64	ffo;
};

/**
 * struct zl3073x_out - output invariant info
 * @enabled: out is enabled or disabled
 * @synth: synthesizer the out is connected to
 * @signal_format: out signal format
 */
struct zl3073x_out {
	bool	enabled;
	u8	synth;
	u8	signal_format;
};

/**
 * struct zl3073x_synth - synthesizer invariant info
 * @freq: synthesizer frequency
 * @dpll: ID of DPLL the synthesizer is driven by
 * @enabled: synth is enabled or disabled
 */
struct zl3073x_synth {
	u32	freq;
	u8	dpll;
	bool	enabled;
};

/**
 * struct zl3073x_dev - zl3073x device
 * @dev: pointer to device
@@ -175,7 +142,6 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
 * Misc operations
 *****************/

int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);

static inline bool
@@ -217,172 +183,141 @@ zl3073x_output_pin_out_get(u8 id)
}

/**
 * zl3073x_ref_ffo_get - get current fractional frequency offset
 * zl3073x_dev_ref_freq_get - get input reference frequency
 * @zldev: pointer to zl3073x device
 * @index: input reference index
 *
 * Return: the latest measured fractional frequency offset
 * Return: frequency of given input reference
 */
static inline s64
zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index)
static inline u32
zl3073x_dev_ref_freq_get(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->ref[index].ffo;
	const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);

	return zl3073x_ref_freq_get(ref);
}

/**
 * zl3073x_ref_is_diff - check if the given input reference is differential
 * zl3073x_dev_ref_is_diff - check if the given input reference is differential
 * @zldev: pointer to zl3073x device
 * @index: input reference index
 *
 * Return: true if reference is differential, false if reference is single-ended
 */
static inline bool
zl3073x_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
zl3073x_dev_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->ref[index].diff;
	const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);

	return zl3073x_ref_is_diff(ref);
}

/**
 * zl3073x_ref_is_enabled - check if the given input reference is enabled
/*
 * zl3073x_dev_ref_is_status_ok - check the given input reference status
 * @zldev: pointer to zl3073x device
 * @index: input reference index
 *
 * Return: true if input refernce is enabled, false otherwise
 * Return: true if the status is ok, false otherwise
 */
static inline bool
zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index)
zl3073x_dev_ref_is_status_ok(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->ref[index].enabled;
}
	const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);

/**
 * zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
 * @zldev: pointer to zl3073x device
 * @index: synth index
 *
 * Return: ID of DPLL the given synthetizer is driven by
 */
static inline u8
zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->synth[index].dpll;
	return zl3073x_ref_is_status_ok(ref);
}

/**
 * zl3073x_synth_freq_get - get synth current freq
 * zl3073x_dev_synth_freq_get - get synth current freq
 * @zldev: pointer to zl3073x device
 * @index: synth index
 *
 * Return: frequency of given synthetizer
 */
static inline u32
zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
zl3073x_dev_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->synth[index].freq;
}
	const struct zl3073x_synth *synth;

/**
 * zl3073x_synth_is_enabled - check if the given synth is enabled
 * @zldev: pointer to zl3073x device
 * @index: synth index
 *
 * Return: true if synth is enabled, false otherwise
 */
static inline bool
zl3073x_synth_is_enabled(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->synth[index].enabled;
	synth = zl3073x_synth_state_get(zldev, index);
	return zl3073x_synth_freq_get(synth);
}

/**
 * zl3073x_out_synth_get - get synth connected to given output
 * zl3073x_dev_out_synth_get - get synth connected to given output
 * @zldev: pointer to zl3073x device
 * @index: output index
 *
 * Return: index of synth connected to given output.
 */
static inline u8
zl3073x_out_synth_get(struct zl3073x_dev *zldev, u8 index)
zl3073x_dev_out_synth_get(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->out[index].synth;
	const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);

	return zl3073x_out_synth_get(out);
}

/**
 * zl3073x_out_is_enabled - check if the given output is enabled
 * zl3073x_dev_out_is_enabled - check if the given output is enabled
 * @zldev: pointer to zl3073x device
 * @index: output index
 *
 * Return: true if the output is enabled, false otherwise
 */
static inline bool
zl3073x_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
zl3073x_dev_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
{
	u8 synth;
	const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
	const struct zl3073x_synth *synth;
	u8 synth_id;

	/* Output is enabled only if associated synth is enabled */
	synth = zl3073x_out_synth_get(zldev, index);
	if (zl3073x_synth_is_enabled(zldev, synth))
		return zldev->out[index].enabled;
	synth_id = zl3073x_out_synth_get(out);
	synth = zl3073x_synth_state_get(zldev, synth_id);

	return false;
	return zl3073x_synth_is_enabled(synth) && zl3073x_out_is_enabled(out);
}

/**
 * zl3073x_out_signal_format_get - get output signal format
 * @zldev: pointer to zl3073x device
 * @index: output index
 *
 * Return: signal format of given output
 */
static inline u8
zl3073x_out_signal_format_get(struct zl3073x_dev *zldev, u8 index)
{
	return zldev->out[index].signal_format;
}

/**
 * zl3073x_out_dpll_get - get DPLL ID the output is driven by
 * zl3073x_dev_out_dpll_get - get DPLL ID the output is driven by
 * @zldev: pointer to zl3073x device
 * @index: output index
 *
 * Return: ID of DPLL the given output is driven by
 */
static inline
u8 zl3073x_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
u8 zl3073x_dev_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
{
	u8 synth;
	const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
	const struct zl3073x_synth *synth;
	u8 synth_id;

	/* Get synthesizer connected to given output */
	synth = zl3073x_out_synth_get(zldev, index);
	synth_id = zl3073x_out_synth_get(out);
	synth = zl3073x_synth_state_get(zldev, synth_id);

	/* Return DPLL that drives the synth */
	return zl3073x_synth_dpll_get(zldev, synth);
	return zl3073x_synth_dpll_get(synth);
}

/**
 * zl3073x_out_is_diff - check if the given output is differential
 * zl3073x_dev_out_is_diff - check if the given output is differential
 * @zldev: pointer to zl3073x device
 * @index: output index
 *
 * Return: true if output is differential, false if output is single-ended
 */
static inline bool
zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
zl3073x_dev_out_is_diff(struct zl3073x_dev *zldev, u8 index)
{
	switch (zl3073x_out_signal_format_get(zldev, index)) {
	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF:
	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
		return true;
	default:
		break;
	}
	const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);

	return false;
	return zl3073x_out_is_diff(out);
}

/**
 * zl3073x_output_pin_is_enabled - check if the given output pin is enabled
 * zl3073x_dev_output_pin_is_enabled - check if the given output pin is enabled
 * @zldev: pointer to zl3073x device
 * @id: output pin id
 *
@@ -392,16 +327,21 @@ zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
 * Return: true if output pin is enabled, false if output pin is disabled
 */
static inline bool
zl3073x_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
zl3073x_dev_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
{
	u8 output = zl3073x_output_pin_out_get(id);
	u8 out_id = zl3073x_output_pin_out_get(id);
	const struct zl3073x_out *out;

	out = zl3073x_out_state_get(zldev, out_id);

	/* Check if the whole output is enabled */
	if (!zl3073x_out_is_enabled(zldev, output))
	/* Check if the output is enabled - call _dev_ helper that
	 * additionally checks for attached synth enablement.
	 */
	if (!zl3073x_dev_out_is_enabled(zldev, out_id))
		return false;

	/* Check signal format */
	switch (zl3073x_out_signal_format_get(zldev, output)) {
	switch (zl3073x_out_signal_format_get(out)) {
	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED:
		/* Both output pins are disabled by signal format */
		return false;
+184 −592

File changed.

Preview size limit exceeded, changes collapsed.

+157 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/dev_printk.h>
#include <linux/string.h>
#include <linux/string_choices.h>
#include <linux/types.h>

#include "core.h"
#include "out.h"

/**
 * zl3073x_out_state_fetch - fetch output state from hardware
 * @zldev: pointer to zl3073x_dev structure
 * @index: output index to fetch state for
 *
 * Function fetches state of the given output from hardware and stores it
 * for later use.
 *
 * Return: 0 on success, <0 on error
 */
int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
	struct zl3073x_out *out = &zldev->out[index];
	int rc;

	/* Read output configuration */
	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &out->ctrl);
	if (rc)
		return rc;

	dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
		str_enabled_disabled(zl3073x_out_is_enabled(out)),
		zl3073x_out_synth_get(out));

	guard(mutex)(&zldev->multiop_lock);

	/* Read output configuration */
	rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
			   ZL_REG_OUTPUT_MB_MASK, BIT(index));
	if (rc)
		return rc;

	/* Read output mode */
	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &out->mode);
	if (rc)
		return rc;

	dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
		zl3073x_out_signal_format_get(out));

	/* Read output divisor */
	rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &out->div);
	if (rc)
		return rc;

	if (!out->div) {
		dev_err(zldev->dev, "Zero divisor for OUT%u got from device\n",
			index);
		return -EINVAL;
	}

	dev_dbg(zldev->dev, "OUT%u divisor: %u\n", index, out->div);

	/* Read output width */
	rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_WIDTH, &out->width);
	if (rc)
		return rc;

	rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
			      &out->esync_n_period);
	if (rc)
		return rc;

	if (!out->esync_n_period) {
		dev_err(zldev->dev,
			"Zero esync divisor for OUT%u got from device\n",
			index);
		return -EINVAL;
	}

	rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
			      &out->esync_n_width);
	if (rc)
		return rc;

	rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
			      &out->phase_comp);
	if (rc)
		return rc;

	return rc;
}

/**
 * zl3073x_out_state_get - get current output state
 * @zldev: pointer to zl3073x_dev structure
 * @index: output index to get state for
 *
 * Return: pointer to given output state
 */
const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
						u8 index)
{
	return &zldev->out[index];
}

int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
			  const struct zl3073x_out *out)
{
	struct zl3073x_out *dout = &zldev->out[index];
	int rc;

	guard(mutex)(&zldev->multiop_lock);

	/* Read output configuration into mailbox */
	rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
			   ZL_REG_OUTPUT_MB_MASK, BIT(index));
	if (rc)
		return rc;

	/* Update mailbox with changed values */
	if (dout->div != out->div)
		rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, out->div);
	if (!rc && dout->width != out->width)
		rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, out->width);
	if (!rc && dout->esync_n_period != out->esync_n_period)
		rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
				       out->esync_n_period);
	if (!rc && dout->esync_n_width != out->esync_n_width)
		rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
				       out->esync_n_width);
	if (!rc && dout->mode != out->mode)
		rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, out->mode);
	if (!rc && dout->phase_comp != out->phase_comp)
		rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
				       out->phase_comp);
	if (rc)
		return rc;

	/* Commit output configuration */
	rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
			   ZL_REG_OUTPUT_MB_MASK, BIT(index));
	if (rc)
		return rc;

	/* After successful commit store new state */
	dout->div = out->div;
	dout->width = out->width;
	dout->esync_n_period = out->esync_n_period;
	dout->esync_n_width = out->esync_n_width;
	dout->mode = out->mode;
	dout->phase_comp = out->phase_comp;

	return 0;
}
Loading