1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/bitfield.h> 4 #include <linux/cleanup.h> 5 #include <linux/dev_printk.h> 6 #include <linux/string.h> 7 #include <linux/string_choices.h> 8 #include <linux/types.h> 9 10 #include "core.h" 11 #include "out.h" 12 13 /** 14 * zl3073x_out_state_fetch - fetch output state from hardware 15 * @zldev: pointer to zl3073x_dev structure 16 * @index: output index to fetch state for 17 * 18 * Function fetches state of the given output from hardware and stores it 19 * for later use. 20 * 21 * Return: 0 on success, <0 on error 22 */ 23 int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index) 24 { 25 struct zl3073x_out *out = &zldev->out[index]; 26 int rc; 27 28 /* Read output configuration */ 29 rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &out->ctrl); 30 if (rc) 31 return rc; 32 33 dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index, 34 str_enabled_disabled(zl3073x_out_is_enabled(out)), 35 zl3073x_out_synth_get(out)); 36 37 guard(mutex)(&zldev->multiop_lock); 38 39 /* Read output configuration */ 40 rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, 41 ZL_REG_OUTPUT_MB_MASK, BIT(index)); 42 if (rc) 43 return rc; 44 45 /* Read output mode */ 46 rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &out->mode); 47 if (rc) 48 return rc; 49 50 dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index, 51 zl3073x_out_signal_format_get(out)); 52 53 /* Read output divisor */ 54 rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &out->div); 55 if (rc) 56 return rc; 57 58 if (!out->div) { 59 dev_err(zldev->dev, "Zero divisor for OUT%u got from device\n", 60 index); 61 return -EINVAL; 62 } 63 64 dev_dbg(zldev->dev, "OUT%u divisor: %u\n", index, out->div); 65 66 /* Read output width */ 67 rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_WIDTH, &out->width); 68 if (rc) 69 return rc; 70 71 rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, 72 &out->esync_n_period); 73 if (rc) 74 return rc; 75 76 if (!out->esync_n_period) { 77 dev_err(zldev->dev, 78 "Zero esync divisor for OUT%u got from device\n", 79 index); 80 return -EINVAL; 81 } 82 83 rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, 84 &out->esync_n_width); 85 if (rc) 86 return rc; 87 88 rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, 89 &out->phase_comp); 90 if (rc) 91 return rc; 92 93 return rc; 94 } 95 96 /** 97 * zl3073x_out_state_get - get current output state 98 * @zldev: pointer to zl3073x_dev structure 99 * @index: output index to get state for 100 * 101 * Return: pointer to given output state 102 */ 103 const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev, 104 u8 index) 105 { 106 return &zldev->out[index]; 107 } 108 109 /** 110 * zl3073x_out_state_set - commit output state changes to hardware 111 * @zldev: pointer to zl3073x_dev structure 112 * @index: output index to set state for 113 * @out: desired output state 114 * 115 * Validates that invariant fields have not been modified, skips the HW 116 * write if the mutable configuration is unchanged, and otherwise writes 117 * only the changed cfg fields to hardware via the mailbox interface. 118 * 119 * Return: 0 on success, -EINVAL if invariants changed, <0 on HW error 120 */ 121 int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index, 122 const struct zl3073x_out *out) 123 { 124 struct zl3073x_out *dout = &zldev->out[index]; 125 int rc; 126 127 /* Reject attempts to change invariant fields (set at fetch only) */ 128 if (WARN_ON(memcmp(&dout->inv, &out->inv, sizeof(out->inv)))) 129 return -EINVAL; 130 131 /* Skip HW write if configuration hasn't changed */ 132 if (!memcmp(&dout->cfg, &out->cfg, sizeof(out->cfg))) 133 return 0; 134 135 guard(mutex)(&zldev->multiop_lock); 136 137 /* Read output configuration into mailbox */ 138 rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, 139 ZL_REG_OUTPUT_MB_MASK, BIT(index)); 140 if (rc) 141 return rc; 142 143 /* Update mailbox with changed values */ 144 if (dout->div != out->div) 145 rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, out->div); 146 if (!rc && dout->width != out->width) 147 rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, out->width); 148 if (!rc && dout->esync_n_period != out->esync_n_period) 149 rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, 150 out->esync_n_period); 151 if (!rc && dout->esync_n_width != out->esync_n_width) 152 rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, 153 out->esync_n_width); 154 if (!rc && dout->mode != out->mode) 155 rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, out->mode); 156 if (!rc && dout->phase_comp != out->phase_comp) 157 rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, 158 out->phase_comp); 159 if (rc) 160 return rc; 161 162 /* Commit output configuration */ 163 rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, 164 ZL_REG_OUTPUT_MB_MASK, BIT(index)); 165 if (rc) 166 return rc; 167 168 /* After successful commit store new state */ 169 dout->cfg = out->cfg; 170 171 return 0; 172 } 173