// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include #include #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]; } /** * zl3073x_out_state_set - commit output state changes to hardware * @zldev: pointer to zl3073x_dev structure * @index: output index to set state for * @out: desired output state * * Validates that invariant fields have not been modified, skips the HW * write if the mutable configuration is unchanged, and otherwise writes * only the changed cfg fields to hardware via the mailbox interface. * * Return: 0 on success, -EINVAL if invariants changed, <0 on HW error */ 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; /* Reject attempts to change invariant fields (set at fetch only) */ if (WARN_ON(memcmp(&dout->inv, &out->inv, sizeof(out->inv)))) return -EINVAL; /* Skip HW write if configuration hasn't changed */ if (!memcmp(&dout->cfg, &out->cfg, sizeof(out->cfg))) return 0; 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->cfg = out->cfg; return 0; }