1 /* 2 * mix.c 3 * 4 * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 #include "rsnd.h" 11 12 #define MIX_NAME_SIZE 16 13 #define MIX_NAME "mix" 14 15 struct rsnd_mix { 16 struct rsnd_mod mod; 17 }; 18 19 #define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id) 20 #define rsnd_mix_nr(priv) ((priv)->mix_nr) 21 #define for_each_rsnd_mix(pos, priv, i) \ 22 for ((i) = 0; \ 23 ((i) < rsnd_mix_nr(priv)) && \ 24 ((pos) = (struct rsnd_mix *)(priv)->mix + i); \ 25 i++) 26 27 static void rsnd_mix_activation(struct rsnd_mod *mod) 28 { 29 rsnd_mod_write(mod, MIX_SWRSR, 0); 30 rsnd_mod_write(mod, MIX_SWRSR, 1); 31 } 32 33 static void rsnd_mix_halt(struct rsnd_mod *mod) 34 { 35 rsnd_mod_write(mod, MIX_MIXIR, 1); 36 rsnd_mod_write(mod, MIX_SWRSR, 0); 37 } 38 39 static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, 40 struct rsnd_mod *mod) 41 { 42 rsnd_mod_write(mod, MIX_MDBAR, 0); 43 rsnd_mod_write(mod, MIX_MDBBR, 0); 44 rsnd_mod_write(mod, MIX_MDBCR, 0); 45 rsnd_mod_write(mod, MIX_MDBDR, 0); 46 } 47 48 static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, 49 struct rsnd_mod *mod) 50 { 51 rsnd_mod_write(mod, MIX_MIXIR, 1); 52 53 /* General Information */ 54 rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); 55 56 /* volume step */ 57 rsnd_mod_write(mod, MIX_MIXMR, 0); 58 rsnd_mod_write(mod, MIX_MVPDR, 0); 59 60 /* common volume parameter */ 61 rsnd_mix_volume_parameter(io, mod); 62 63 rsnd_mod_write(mod, MIX_MIXIR, 0); 64 } 65 66 static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, 67 struct rsnd_mod *mod) 68 { 69 /* Disable MIX dB setting */ 70 rsnd_mod_write(mod, MIX_MDBER, 0); 71 72 /* common volume parameter */ 73 rsnd_mix_volume_parameter(io, mod); 74 75 /* Enable MIX dB setting */ 76 rsnd_mod_write(mod, MIX_MDBER, 1); 77 } 78 79 static int rsnd_mix_probe_(struct rsnd_mod *mod, 80 struct rsnd_dai_stream *io, 81 struct rsnd_priv *priv) 82 { 83 return rsnd_cmd_attach(io, rsnd_mod_id(mod)); 84 } 85 86 static int rsnd_mix_init(struct rsnd_mod *mod, 87 struct rsnd_dai_stream *io, 88 struct rsnd_priv *priv) 89 { 90 rsnd_mod_power_on(mod); 91 92 rsnd_mix_activation(mod); 93 94 rsnd_mix_volume_init(io, mod); 95 96 rsnd_mix_volume_update(io, mod); 97 98 return 0; 99 } 100 101 static int rsnd_mix_quit(struct rsnd_mod *mod, 102 struct rsnd_dai_stream *io, 103 struct rsnd_priv *priv) 104 { 105 rsnd_mix_halt(mod); 106 107 rsnd_mod_power_off(mod); 108 109 return 0; 110 } 111 112 static struct rsnd_mod_ops rsnd_mix_ops = { 113 .name = MIX_NAME, 114 .probe = rsnd_mix_probe_, 115 .init = rsnd_mix_init, 116 .quit = rsnd_mix_quit, 117 }; 118 119 struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) 120 { 121 if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) 122 id = 0; 123 124 return rsnd_mod_get(rsnd_mix_get(priv, id)); 125 } 126 127 int rsnd_mix_probe(struct rsnd_priv *priv) 128 { 129 struct device_node *node; 130 struct device_node *np; 131 struct device *dev = rsnd_priv_to_dev(priv); 132 struct rsnd_mix *mix; 133 struct clk *clk; 134 char name[MIX_NAME_SIZE]; 135 int i, nr, ret; 136 137 /* This driver doesn't support Gen1 at this point */ 138 if (rsnd_is_gen1(priv)) 139 return 0; 140 141 node = rsnd_mix_of_node(priv); 142 if (!node) 143 return 0; /* not used is not error */ 144 145 nr = of_get_child_count(node); 146 if (!nr) { 147 ret = -EINVAL; 148 goto rsnd_mix_probe_done; 149 } 150 151 mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL); 152 if (!mix) { 153 ret = -ENOMEM; 154 goto rsnd_mix_probe_done; 155 } 156 157 priv->mix_nr = nr; 158 priv->mix = mix; 159 160 i = 0; 161 ret = 0; 162 for_each_child_of_node(node, np) { 163 mix = rsnd_mix_get(priv, i); 164 165 snprintf(name, MIX_NAME_SIZE, "%s.%d", 166 MIX_NAME, i); 167 168 clk = devm_clk_get(dev, name); 169 if (IS_ERR(clk)) { 170 ret = PTR_ERR(clk); 171 goto rsnd_mix_probe_done; 172 } 173 174 ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, 175 clk, rsnd_mod_get_status, RSND_MOD_MIX, i); 176 if (ret) 177 goto rsnd_mix_probe_done; 178 179 i++; 180 } 181 182 rsnd_mix_probe_done: 183 of_node_put(node); 184 185 return ret; 186 } 187 188 void rsnd_mix_remove(struct rsnd_priv *priv) 189 { 190 struct rsnd_mix *mix; 191 int i; 192 193 for_each_rsnd_mix(mix, priv, i) { 194 rsnd_mod_quit(rsnd_mod_get(mix)); 195 } 196 } 197